MyCat全局ID主键生成策略详解

                                                  MyCat全局ID主键生成策略详解

一、简介

Mycat是非常好用和优秀的数据库分库分表中间件,其官网地址在这里,里面有非常详细的文档。

分库分表的情况下,多个数据库自增主键无法保证自增主键的全局唯一,因此MyCat的全局主键生成策略孕育而生。

由于MyCat是Java写的,强烈建议可以直接下载源码,进行测试ID生成的情况。

这是启动类:MycatStartup,运行main方法就可以了,启动日志可以看出是正常启动的。

其所有的配置文件如下:

修改server.xml,配置Mycat的用户名和密码。

	<!-- 用户名为root、密码为123456、schema.xml指定的名称为TESTDB-->
    <user name="root" defaultAccount="true">
		<property name="password">123456</property>
		<property name="schemas">TESTDB</property>
		<property name="defaultSchema">TESTDB</property>
	</user>
	<user name="user">
		<property name="password">user</property>
		<property name="schemas">TESTDB</property>
		<property name="readOnly">true</property>
		<property name="defaultSchema">TESTDB</property>
	</user>

 修改schema.xml文件,配置MyCat连接的数据库和表。

<mycat:schema xmlns:mycat="http://io.mycat/">

	<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" >
		<table name="order_info" autoIncrement="true" primaryKey="id" dataNode="test1" rule="mod-long" />
	</schema>

	<dataNode name="test1" dataHost="localhost" database="macaw" />
	
	<dataHost name="localhost" maxCon="1000" minCon="10" balance="0"
			  writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
		<heartbeat>select user()</heartbeat>
		<writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root" password="root"></writeHost>
	</dataHost>
</mycat:schema>

准备order_info表进行测试

CREATE TABLE `order_info` (
  `id` varchar(64) NOT NULL COMMENT '订单ID',
  `name` varchar(64) NOT NULL DEFAULT '' COMMENT '订单名称',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='订单表';

二、ID生成方式

  1. 本地文件方式:使用服务器本地磁盘文件的方式
  2. 本地时间戳方式:使用时间戳方式
  3. 数据库方式:使用数据库的方式
  4. zookeeper:使用zookeeper生成id

三、四种ID生成方式详解

3.1、本地文件方式

修改 conf/server.xml 文件

<property name="sequenceHandlerType">0</property>

 其中,该属性有4个取值:

<!--
    0:文件方式
    1:数据库方式
    2:时间戳方式
    3:zk
--/>

当为0时,采用文件的方式,在sequence_conf.properties文件中,我们可以自定义策略,比如我们要操作order_info表,就可以以ORDER_INFO为前缀配置最大和最小id:

ORDER_INFO.HISIDS=
ORDER_INFO.MINID=1000
ORDER_INFO.MAXID=200000000
ORDER_INFO.CURID=1250

在使用插入的时候需要注意,id列的写法为next value for MYCATSEQ_前缀,如:

INSERT INTO order_info(id,name) values('next value for MYCATSEQ_ORDER_INFO','文件方式生成的ID');
SELECT * FROM order_info;

优点:本地加载,读取速度较快,配置简单  

缺点:mycat重新发布时,seq文件需要替换,集群部署无法用此方式,路由到不同的mycat上无法保证id唯一,使mycat变成了有状态的中间件

3.2、本地时间戳方式

修改server.xml 文件

<property name="sequenceHandlerType">2</property>

 修改sequence_time_conf.properties文件

# 0-31为整数,每一个mycat节点的这两个配置值都不一样
WORKID=01
DATAACENTERID=01
INSERT INTO order_info(name) values('时间戳方式生成的ID');
SELECT * FROM order_info;

 

* 本地时间戳计算方式:

ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加) 长度18位,因此表主键字段长度必须大于等于18位

优点:不存在mycat重新发布影响seq的问题,

缺点:字段长度是18位,比较占空间

3、数据库方式

修改server.xml 文件

<property name="sequenceHandlerType">1</property>

在mycat的dbseq.sql文件中有如下建表和函数语句用于基于数据库生成id:

DROP TABLE IF EXISTS MYCAT_SEQUENCE;
CREATE TABLE MYCAT_SEQUENCE (  name VARCHAR(64) NOT NULL,  current_value BIGINT(20) NOT NULL,  increment INT NOT NULL DEFAULT 1, PRIMARY KEY (name) ) ENGINE=InnoDB;

-- ----------------------------
-- Function structure for `mycat_seq_currval`
-- ----------------------------
DROP FUNCTION IF EXISTS `mycat_seq_currval`;
DELIMITER ;;
CREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64);
    SET retval="-1,0";
    SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR) ) INTO retval FROM MYCAT_SEQUENCE  WHERE name = seq_name;
    RETURN retval ;
END
;;
DELIMITER ;

-- ----------------------------
-- Function structure for `mycat_seq_nextval`
-- ----------------------------
DROP FUNCTION IF EXISTS `mycat_seq_nextval`;
DELIMITER ;;
CREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(64)) RETURNS varchar(64) CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64);
    DECLARE val BIGINT;
    DECLARE inc INT;
    DECLARE seq_lock INT;
    set val = -1;
    set inc = 0;
    SET seq_lock = -1;
    SELECT GET_LOCK(seq_name, 15) into seq_lock;
    if seq_lock = 1 then
      SELECT current_value + increment, increment INTO val, inc FROM MYCAT_SEQUENCE WHERE name = seq_name for update;
      if val != -1 then
          UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;
      end if;
      SELECT RELEASE_LOCK(seq_name) into seq_lock;
    end if;
    SELECT concat(CAST((val - inc + 1) as CHAR),",",CAST(inc as CHAR)) INTO retval;
    RETURN retval;
END
;;
DELIMITER ;

-- ----------------------------
-- Function structure for `mycat_seq_setvals`
-- ----------------------------
DROP FUNCTION IF EXISTS `mycat_seq_nextvals`;
DELIMITER ;;
CREATE FUNCTION `mycat_seq_nextvals`(seq_name VARCHAR(64), count INT) RETURNS VARCHAR(64) CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64);
    DECLARE val BIGINT;
    DECLARE seq_lock INT;
    SET val = -1;
    SET seq_lock = -1;
    SELECT GET_LOCK(seq_name, 15) into seq_lock;
    if seq_lock = 1 then
        SELECT current_value + count INTO val FROM MYCAT_SEQUENCE WHERE name = seq_name for update;
        IF val != -1 THEN
            UPDATE MYCAT_SEQUENCE SET current_value = val WHERE name = seq_name;
        END IF;
        SELECT RELEASE_LOCK(seq_name) into seq_lock;
    end if;
    SELECT CONCAT(CAST((val - count + 1) as CHAR), ",", CAST(val as CHAR)) INTO retval;
    RETURN retval;
END
;;
DELIMITER ;

-- ----------------------------
-- Function structure for `mycat_seq_setval`
-- ----------------------------
DROP FUNCTION IF EXISTS `mycat_seq_setval`;
DELIMITER ;;
CREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(64), value BIGINT) RETURNS varchar(64) CHARSET latin1
    DETERMINISTIC
BEGIN
    DECLARE retval VARCHAR(64);
    DECLARE inc INT;
    SET inc = 0;
    SELECT increment INTO inc FROM MYCAT_SEQUENCE WHERE name = seq_name;
    UPDATE MYCAT_SEQUENCE SET current_value = value WHERE name = seq_name;
    SELECT concat(CAST(value as CHAR),",",CAST(inc as CHAR)) INTO retval;
    RETURN retval;
END
;;
DELIMITER ;

INSERT INTO MYCAT_SEQUENCE VALUES ('GLOBAL', 1, 1);

MYCAT_SEQUENCE 的三个字段:

  • name sequence名称
  • current_value 当前value
  • increment 增长步长 可理解为mycat在数据库中一次读取多少个sequence. 当这些用完后, 下次再从数据库中读取.

插入一条数据,用于order_info表id生成:

INSERT INTO `MYCAT_SEQUENCE`(`name`, `current_value`, `increment`) VALUES ('ORDER_INFO', 23650, 100);

接着需要在sequence_db_conf.properties中进行配置:指定某个sequence哪个节点上,与dataNode的name对应。

	<dataNode name="test1" dataHost="localhost" database="macaw" />
GLOBAL=test1
ORDER_INFO=test1

执行sql进行测试

INSERT INTO order_info(name) values('数据库方式生成的ID');
SELECT * FROM order_info;

* 注意 :将mycat_sequence表也放出来,且注意大小写(数据库默认区分大小写)

优点:重新部署mycat不受影响,节点如果是主从切换后,数据id可能会有异常(重复)

缺点:这种策略,只能在当前库中使用, 但是如果进行分库,那么在多个库中,就一定会出现id相同的问题。

3.4、ZK方式

修改server.xml 文件

<property name="sequnceHandlerType">3</property>

修改myid.properties

 #使用zk管理mycat
loadZk=true
#zk服务器的地址和端口
zkURL=127.0.0.1:2181
#mycat集群的ID
clusterId=1
#本mycat的ID
myid=mycat_fz_01
#集群大小
clusterSize=1
#集群内mycat的ID
clusterNodes=mycat_fz_01
#server  booster  ;   booster install on db same server,will reset all minCon to 2
type=server
boosterDataHosts=dataHost1

修改sequence_distributed_conf.properties

INSTANCEID=ZK #代表使用zk
CLUSTERID=1 #与myid.properties中的CLUSTERID设置的值相同

修改schema.xml

schema的table 增加属性 autoIncrement="true"和 primaryKey="id"

测试之前请启动zookeeper!

INSERT INTO order_info(name) values('zk方式生成的ID');
SELECT * FROM order_info;

优点:无悲观锁,无强竞争,吞吐量更高

缺点:对zookeeper集群的要求增加

 

 

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值