当我们分表分库后,就不可以使用数据库的自增id了,否则肯定会出现id一样的数据。
因此就需要使用分布式ID生成策略进行id的生成。
mycat序列号(文件)
在mycat中提供了全局序列号:
在sequence_conf.properties
文件中:
# 表示使用过的历史分段(一般无特殊需要可不配置)
GLOBAL_SEQ.HISIDS=
# 最小ID值
GLOBAL_SEQ.MINID=1001
# 最大ID值
GLOBAL_SEQ.MAXID=1000000000
# 当前ID值。
GLOBAL_SEQ.CURID=1000
同时在server.xml
中,提供了配置生成id的方式:
<system>
................
<property name="sequenceHandlerType">2</property>
</system>
其中,该属性有4个取值:
<!--
0:文件方式
1:数据库方式
2:时间戳方式
3:zk
--/>
当为0时,采用文件的方式,在上面的sequence_conf.properties
文件中,我们可以自定义策略,比如我们要操作order表,就可以以ORDER为前缀配置最大和最小id:
ORDER.HISIDS=
ORDER.MINID=1
ORDER.MAXID=200000
ORDER.CURID=12002
在使用插入的时候需要注意,id列的写法为next value for MYCATSEQ_前缀
,如:
INSERT INTO t_order(order_id,content) values('next value for MYCATSEQ_ORDER','heihei')
该条数据的id就从12002开始,即12003:
缺点
当MyCAT重新发布后,配置文件中的sequence会恢复到初始值。
优点
本地加载,读取速度较快。
使用数据库的策略
在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表id生成:
INSERT INTO `mycatDB`.`MYCAT_SEQUENCE`(`name`, `current_value`, `increment`) VALUES ('ORDER', 20101, 100);
接着需要在sequence_db_conf.properties
中进行配置:
ORDER=localdn
指定某个sequence哪个节点上,与dataNode的name对应。
别忘了将server.xml中该属性改为1
<property name="sequenceHandlerType">1</property>
然后就可以测试了:
INSERT INTO t_order(order_id,content) values('next value for MYCATSEQ_ORDER','lala')
后缀和上面的表中的name名一致,
可以看到新的数据从20101后开始,插入完后,MYCAT_SEQUENCE表中ORDER的当前值变成了20201,因为会一次读取100个,用完后再读取下100个。
缺点
这种策略,只能在当前库中使用, 但是如果进行分库,那么在多个库中,就一定会出现id相同的问题。
时间戳策略
这个很简单,就会根据时间戳生成,将server.xml中那个属性的类型修改为2即可。
<property name="sequenceHandlerType">2</property>
sequence_time_conf.properties
# 0-31为整数,每一个mycat节点的这两个配置值都不一样
WORKID=01
DATAACENTERID=01
插入:
Zookeper策略
将上面类型修改为3.
然后 mycat的myid.properties
中配置ZK:
loadZK一定要开启
loadZk=true
zkURL=127.0.0.1:2181
clusterId=01
myid=mycat_01
clusterSize=1
clusterNodes=mycat_01
sequence_distributed_conf.properties
:
INSTANCEID=ZK
CLUSTERID=01
emmm用zookeper的方式的时候启动有点问题,暂时不知道是哪里的问题。