全局序列
在实现分库分表的情况下,数据库自增主键已无法保证主键的全局唯一。所以,Mact提供了全区sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
1. 本地文件
原理:此方式MyCAT将sequence配置到文件中,当使用到sequence中的配置后,MyCAT会更下classpath中的 sequence_conf.properties文件中sequence当前的值。
配置方式:
#在sequence_conf.properties文件中做如下配置: GLOBAL_SEQ.HISIDS=
GLOBAL_SEQ.MINID=1001
GLOBAL_SEQ.MAXID=1000000000
GLOBAL_SEQ.CURID=1000
其中HISIDS表示使用过的历史分段(一般无特殊需要可不配置),MINID表示最小ID值,MAXID表示最大ID值,CURID表示当前 ID值。
server.xml中配置:
sequnceHandlerType需要配置为0,表示使用本地文件方式。
使用示例:
insert into table1(id,name) values(next value for MYCATSEQ_GLOBAL,‘test’);
- 缺点:当MyCAT重新发布后,配置文件中的sequence会恢复到初始值。
- 优点:本地加载,读取速度较快。
数据库方式
利用数据库一个表 来进行计数累加。但是并不是每次生成序列都读写数据库,这样效率太低。
Mycat 会预加载一部分号段到 Mycat 的内存中,这样大部分读写序列都是在内存中完成的。 如果内存中的号段用完了 Mycat 会再向数据库要一次。
但是,如果Mycat挂了,Mycat 启动后会向数据库申请新的号段,原有号段会弃用。 也就是说如果 Mycat 重启,那么损失是当前的号段没用完的号码,但是不会因此出现主键重复。
配置:
在dn1数据结点创建MYCAT_SEQUENCE表:
CREATE TABLE MYCAT_SEQUENCE (
name VARCHAR(50) NOT NULL, #名称
current_value INT NOT NULL, #当前value
increment INT NOT NULL DEFAULT 100, #mycat在数据库中一次读取多少个sequence
PRIMARY KEY(name)
) ENGINE=InnoDB;
插入一条sequence:
INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES (‘ORDERS’, 100000, 100);
创建相关function,返回当前sequence的值:
DROP FUNCTION IF EXISTS mycat_seq_currval;
DELIMITER $$
CREATE FUNCTION mycat_seq_currval(seq_name VARCHAR(50)) RETURNS varchar(64)
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;
设置sequence值:
DROP FUNCTION IF EXISTS mycat_seq_setval;
DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50),value INTEGER) RETURNS varchar(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = value
WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER;
获取下一个sequence值:
DROP FUNCTION IF EXISTS mycat_seq_nextval;
DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS varchar(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQUENCE
SET current_value = current_value + increment WHERE name = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER;
配置sequence_db_conf.properties,指定sequence相关配置在哪个节点上:
例如:
ORDERS=dn1
配置server.xml:
指定sequenceHandlerType=1,为数据库方式。(0=本地文件、1=数据库、2=时间戳)
注意:MYCAT_SEQUENCE表和以上的3个function,需要放在同一个节点上。function请直接在具体节点的数据库上执行,如果执行的时候报:
you might want to use the less safe log_bin_trust_function_creators variable
需要对数据库做如下设置:
windows下my.ini[mysqld]加上
log_bin_trust_function_creators=1
linux下/etc/my.cnf下my.ini[mysqld]加上
log_bin_trust_function_creators=1
修改完后,即可在mysql数据库中执行上面的函数.
测试:登录Mycat,插入数据
insert into orders(id, amount, customer_id, order_type) values(next value for MYCATSEQ_ORDERS, 1000, 101, 102);
insert into orders(id, amount, customer_id, order_type) values(next value for MYCATSEQ_ORDERS, 1000, 101, 103);
重新Mycat,再次插入数据:
根据我们的配置,加载到内存的100个序列因为重启被废弃了,重启后mycat重新加载了100个。
时间戳
全局ID = 64位二进制(42(毫秒)+ 5(机器ID)+ 5(机器业务编码)+ 12(重复累加))换算成十进制为18位数的long类型,每毫秒可以并发12位二进制的累加。
优点:配置简单;
缺点:18位ID过长;
自主生成全局序列
- 根据业务逻辑组合
- 利用redis或zookeeper生成全局ID。