在实现分库分表的情况下,数据库自增主键已经无法保证在集群中是全局唯一的主键了,因此mycat提供了全局的sequence,并且提供了本地配置、数据库配置等多种实现方式。

本地文件方式

采用该方式,mycat将sequence配置到classpath目录的sequence_conf.properties文件中,在sequence_conf.properties文件做如下配置

GLOBAL_SEQ.HISIDS= 
GLOBAL_SEQ.MINID=1001 
GLOBAL_SEQ.MAXID=100000000 
GLOBAL_SEQ.CURID=1000

其中HISIDS表示历史分段(一般无特殊需要则可以不配置),MINID表示最新的ID值,MAXID表示最大的ID值,CURID表示当前的ID值。
需要启用这种方式,则首先需要在servier.xml中配置如下参数:

<system><property name="sequnceHandlerType">0</property></system>

注意:sequnceHandlerType配置为0,表示使用本地文件方式

采用这种方式的缺点是mycat重新发布后,配置文件中的sequence会恢复到初始值,有点事本地加载且读取速度快。

 

数据库方式

在数据库中创建一张表名为sequence的表,有sequence的当前值(current_value)、步长(increment int 类型,指每次读取多少个sequence,假设值为K)等信息。

squence的获取步骤如下:

1)每次使用sequence时候,根据传入的sequence名称,从数据库表中 读取current_value、increment到mycat中,并将数据库中的current_value 修改为current_value + increment的值。
2)mycat将读取到的current_value+increment 作为本次使用的sequence值,在下次使用的时候,sequence自动加1,当使用increment次后,执行与步骤一相同操作。
3)mycat负责维护这张表,用到哪些sequence时,主需要在这张表插入一条记录即可,若某次读取sequence没有用完就宕机了,则本次已经读取sequence且未使用的值将会被丢弃。

若要启用这种方式,需要在server.xml中配置如下参数:

<system><property name="sequnceHandlerType">1</property></system>

注意:这里配置成1,表示使用数据库方式生成sequence.

数据库配置如下:

创建MYCAT_SEQUENCE的表

CREATE DATABASE `mycat_mobp2p` DEFAULT CHARACTER SET utf8;
use mycat_mobp2p;
CREATE TABLE `MYCAT_SEQUENCE` (
 `NAME` varchar(50) NOT NULL,
 `current_value` int(11) NOT NULL,
 `increment` int(11) NOT NULL DEFAULT '100',
 PRIMARY KEY (`NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;name、current_value和increment 分别sequence的名称、当前value的值和增长步长。
插入一条sequence语句:
USER 是已经创建好的逻辑表,已经做好了相关ruleinsert into MYCAT_SEQUENCE(name,current_value,increment) VALUES('USER',10000,100); 
创建相关的function
DELIMITER $$
USE `mycat_mobp2p`$$
DROP FUNCTION IF EXISTS `mycat_seq_currval`$$
CREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64) CHARSET utf8
 DETERMINISTIC
BEGIN 
 DECLARE retval VARCHAR(64);
 SET retval="-999999999,null"; 
 SELECT CONCAT(CAST(current_value AS CHAR),",",CAST(increment AS CHAR) ) INTO retval 
 FROM MYCAT_SEQUENCE WHERE NAME = seq_name; 
 RETURN retval ; 
END$$
DELIMITER ; 
DELIMITER $$
USE `mycat_mobp2p`$$
DROP FUNCTION IF EXISTS `mycat_seq_nextval`$$
CREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64) CHARSET utf8
 DETERMINISTIC
BEGIN 
 UPDATE MYCAT_SEQUENCE 
 SET current_value = current_value + increment 
 WHERE NAME = seq_name; 
 RETURN mycat_seq_currval(seq_name); 
END$$
DELIMITER ; 
DELIMITER $$
USE `mycat_mobp2p`$$
DROP FUNCTION IF EXISTS `mycat_seq_setval`$$
CREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50), VALUE INTEGER) RETURNS VARCHAR(64) CHARSET utf8
 DETERMINISTIC
BEGIN 
 UPDATE MYCAT_SEQUENCE 
 SET current_value = VALUE 
 WHERE NAME = seq_name; 
 RETURN mycat_seq_currval(seq_name); 
END$$
DELIMITER ;equence_db_conf.properties指定 sequence 相关配置在哪个节点上,配置如下:
[root@canal3 conf]# cat sequence_db_conf.properties 
GLOBAL=dn1
COMPANY=dn1
CUSTOMER=dn1
ORDERS=dn1
USER=dn2注意:USER必须为大写,这个与表的数据是否大写无关,事实上,MYCAT_SEQUENCE中name是否大小写对结果没有影响。
[root@canal3 conf]# cat schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
 <schema name="integration01" checkSQLschema="false" sqlMaxLimit="100">
 <table name="user" primaryKey="id" autoIncrement="true" subTables="user$1-10" rule="mod-long" dataNode="dn1"></table > 
 </schema> 
 <dataNode name="dn1" dataHost="integration01" database="integration01"/> 
 <dataNode name="dn2" dataHost="mycat_mobp2p" database="mycat_mobp2p"/>

 <dataHost name="integration01" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="2" slaveThreshold="100">
 <heartbeat>show slave status</heartbeat>
 <writeHost host="10.40.20.44" url="10.40.20.44:3307" user="root" password="chengce243"> 
 <readHost host="10.40.20.44" url="10.40.20.44:3308" user="root" password="chengce243"/> 
 </writeHost>
 </dataHost>

 <dataHost name="mycat_mobp2p" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
 <heartbeat>select user()</heartbeat>
 <writeHost host="10.40.20.44" url="10.40.20.44:3307" user="root" password="chengce243"> 
 </writeHost>
 </dataHost>

</mycat:schema> 
 
[root@canal3 conf]# cat server.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
 - you may not use this file except in compliance with the License. - You 
 may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
 - - Unless required by applicable law or agreed to in writing, software - 
 distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
 License for the specific language governing permissions and - limitations 
 under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
 <system>
 <property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
 <property name="useHandshakeV10">1</property>
 <property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
 <property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 --> <property name="sequnceHandlerType">1</property>
 <property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
 <!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
 <!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
 <!-- <property name="processorBufferChunk">40960</property> -->
 <!-- 
 <property name="processors">1</property> 
 <property name="processorExecutor">32</property> 
 -->
 <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
 <property name="processorBufferPoolType">0</property>
 <!--默认是65535 64K 用于sql解析时最大文本长度 -->
 <!--<property name="maxStringLiteralLength">65535</property>-->
 <!--<property name="sequnceHandlerType">0</property>-->
 <!--<property name="backSocketNoDelay">1</property>-->
 <!--<property name="frontSocketNoDelay">1</property>-->
 <!--<property name="processorExecutor">16</property>-->
 <!--
 <property name="serverPort">8066</property> <property name="managerPort">9066</property> 
 <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> 
 <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
 <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
 <property name="handleDistributedTransactions">0</property>

 <!--
 off heap for merge/order/group/limit 1开启 0关闭
 -->
 <property name="useOffHeapForMerge">1</property> <!--
 单位为m
 -->
 <property name="memoryPageSize">64k</property> <!--
 单位为k
 -->
 <property name="spillsFileBufferSize">1k</property> <property name="useStreamOutput">0</property>
 <!--
 单位为m
 -->
 <property name="systemReserveMemorySize">384m</property> <!--是否采用zookeeper协调切换 -->
 <property name="useZKSwitch">false</property> <!-- XA Recovery Log日志路径 -->
 <!--<property name="XARecoveryLogBaseDir">./</property>--> <!-- XA Recovery Log日志名称 -->
 <!--<property name="XARecoveryLogBaseName">tmlog</property>--> </system>

 <!-- 全局SQL防火墙设置 -->
 <!--白名单可以使用通配符%或着*-->
 <!--例如<host host="127.0.0.*" user="root"/>-->
 <!--例如<host host="127.0.*" user="root"/>-->
 <!--例如<host host="127.*" user="root"/>-->
 <!--例如<host host="1*7.*" user="root"/>-->
 <!--这些配置情况下对于127.0.0.1都能以root账户登录-->
 <!--
 <firewall>
 <whitehost>
 <host host="1*7.0.0.*" user="root"/>
 </whitehost>
 <blacklist check="false">
 </blacklist>
 </firewall>
 --> <user name="root" defaultAccount="true">
 <property name="password">123456</property>
 <property name="schemas">integration01</property>

 <!-- 表级 DML 权限设置 -->
 </user> <user name="user">
 <property name="password">user</property>
 <property name="schemas">integration01</property>
 <property name="readOnly">false</property>
 </user></mycat:server>

使用示例:

insert into user (borrow_nid,create_user,create_time,update_time) values('201802051008888','dba',now(),now());
insert into user (borrow_nid,create_user,create_time,update_time) values('201802051007777','dba',now(),now());
insert into user (borrow_nid,create_user,create_time,update_time) values('201802051006666','dba',now(),now());
insert into user (borrow_nid,create_user,create_time,update_time) values('201802051005555','dba',now(),now());
insert into user (borrow_nid,create_user,create_time,update_time) values('201802051004444','dba',now(),now());如果执行时报错:you might want to use the less safe log_bin_trust_function_creators variable。
解决办法:在 my.cnf中设置 SET GLOBAL log_bin_trust_function_creators = 1;

这样做虽然MyCat为无状态而且id有持久化,并且一次可以取出多个id,通过配置可以有主从切换。但是,id还是纯数字,没有有意义的信息,而且,MyCat主从切换并不可靠,id生成有故障,则整个服务都无法正常进行,这在架构上有单点问题,是不推荐的。