2018/11 作者:ihunter 0 次 0
一、前言
分库分表需要应对的技术难题有如下几个:
1. 分布式全局唯一id
2. 分片规则和策略
3. 跨分片技术问题
4. 跨分片事物问题
下面我们来看一下Mycat是如何解决分布式全局唯一id的问题的
关注、转发、评论头条号每天分享java知识,私信回复“555”赠送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式资料
二、Mycat全局序列号
Mycat保证id唯一的方式有如下几个:
1)本地文件方式
2)数据库方式
3)时间戳方式
4)ZKID生成器
5)ZK递增ID
推荐使用第 4,5 种
以上5中方式都要统一在server.xml文件中开启全局序列号的配置和在schema.xml文件中配置逻辑表的autoIncrement属性为true(2个必须步骤)
1)sequnceHandlerType进行相应全局序列号策略选项设置(server.xml),在mycat中对应的源码是MyCATSequnceProcessor.java
0
sequnceHandlerType可取的值有以下几个:
0:本地文件方式
1:数据库方式
2:时间戳方式
3:ZKID生成器
4:ZK递增ID
2)autoIncrement属性为true(schema.xml)
1. 本地文件方式
使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceHandler
在server.xml文件中开启全局序列号的配置:
0
使用到的配置文件:sequence_conf.properties
#default global sequence GLOBAL.HISIDS= GLOBAL.MINID=10001 GLOBAL.MAXID=20000 GLOBAL.CURID=10000 # self define sequence COMPANY.HISIDS= COMPANY.MINID=1001 COMPANY.MAXID=2000 COMPANY.CURID=1000 CUSTOMER.HISIDS= CUSTOMER.MINID=1001 CUSTOMER.MAXID=2000 CUSTOMER.CURID=1000 ORDER.HISIDS= ORDER.MINID=1001 ORDER.MAXID=2000 ORDER.CURID=1000 HOTNEWS.HISIDS= HOTNEWS.MINID=1001 HOTNEWS.MAXID=2000 HOTNEWS.CURID=1000
拿HOTNEWS这个表的配置来说明:
HOTNEWS.HISIDS= #HOTNEWS这张表历史使用的自增id,一般不配置 HOTNEWS.MINID=1001 #HOTNEWS这张表使用的最小自增id HOTNEWS.MAXID=2000 #HOTNEWS这张表使用的最大自增id HOTNEWS.CURID=1000 #HOTNEWS这张表当前使用的自增id
缺点: 当Mycat重新发布后,自增ID恢复到初始值。原因是因为sequnceHandlerType定义的是静态变量,不推荐使用
示例:
1.1 在mycat的schema.xml文件里面分别配置逻辑表hotnews和mysql的主从机,注意autoIncrement="true"才能使用全局唯一id
1.2 在主数据库(192.168.152.130)分别创建3个数据库db1,db2,db3,然后创建hotnews表
create database db1; use db1; create table hotnews( id bigint(20) not null primary key auto_increment, title varchar(50) default null ); create database db2; use db2; create table hotnews( id bigint(20) not null primary key auto_increment, title varchar(50) default null ); create database db3; use db3; create table hotnews( id bigint(20) not null primary key auto_increment, title varchar(50) default null );
1.3 插入数据
插入数据到hotnews前我们先来看一下使用本地文件方式的配置文件sequence_conf.properties的内容
cat sequence_conf.properties |grep HOTNEWS
插入数据到hotnews
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test1'); insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test2'); insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test3');
1.4 查看hotnews表的结果
select * from hotnews;
再次查看配置文件sequence_conf.properties的内容,发现内容随着插入数据的自增id做了改变
2. 数据库方式
这种方式和本地文件的方式是一样的,只是把sequence_conf.properties的内容用数据库来管理
使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceMySQLHandler
这里还是以hotnews表为例
2.1 在server.xml文件中开启全局序列号的配置:
1
使用到的配置文件:sequence_db_conf.properties
vim sequence_db_conf.properties #sequence stored in datanode GLOBAL=dn1 COMPANY=dn1 CUSTOMER=dn1 ORDERS=dn1
2.2 选择其中的一个分片,执行如下步骤,譬如我在dn1中创建,对应的数据库名为db1(为什么这里会涉及到datanode,因为后续的sequence_db_conf.properties文件会使用到), 注意:是登录到数据库中创建,而不是在mycat中创建
第一步:创建SEQUENCE表,用来存储序列号
use db1; DROP TABLE IF EXISTS MYCAT_SEQUENCE; CREATE TABLE MYCAT_SEQUENCE ( NAME VARCHAR (50) NOT NULL, /*全局SEQ名称*/ current_value INT NOT NULL, /*当前序列ID*/ increment INT NOT NULL DEFAULT 100, /*初始序列ID*/ PRIMARY KEY (NAME) ) ENGINE = INNODB ; INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ('GLOBAL', 100000, 100);
第二步:创建SEQ function
-- 获取当前sequence的值(返回当前值,增量) DROP FUNCTION IF EXISTS `mycat_seq_currval`; DELIMITER ;; 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 ; -- 获取下一个sequence值 DROP FUNCTION IF EXISTS `mycat_seq_nextval`; DELIMITER ;; 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 ; -- 设置sequence值 DROP FUNCTION IF EXISTS `mycat_seq_setval`; DELIMITER ;; 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 ;
插入需要自增长的表的策略,这条数据是我们hotnews这个表所需要的。 name必须是大写的字符,不然就会报错
insert into MYCAT_SEQUENCE values('HOTNEWS','101','100');
说明:插入了一个名为HOTNEWS的sequence,当前值为101,步长为100。当插入第一条数据时id为201,后面每插入一条数据id加1
第三步:在sequence_db_conf.properties这个文件中定义hotnews这张表的序列名称,同时可以定义到哪个分片上。这里是定义在dn1上的
名字=分片1[,分片2][,.....][,分片N]
vim sequence_db_conf.properties
保存:
:wq
2.3 重启mycat
[root@centos1 mycat]# ./bin/mycat restart;
2.4 连接mycat进行数据测试
mysql -uroot -p123456 -P8066 -h192.168.152.128
插入数据
insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11111'); insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11112'); insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, 'test11113');
查看结果:
select * from hotnews;
缺点:当mycat挂掉时可能出现主键冲突。不推荐使用
3. 本地时间戳方式
使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceTimeHandler
3.1 在server.xml文件中开启全局序列号的配置:
2
使用到的配置文件:
sequence_time_conf.properties
WORKID=01(范围01-31)
DATAACENTERID=01(范围01-31)
示例:
首先清空hotnews这张表的数据,方便查看测试结果
delete from hotnews;
插入测试数据
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11111'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11112'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test11113');
查看结果:
select * from hotnews;
优点:不存在id重复的现象。
缺点:主键太长。时间可能被重置。没有特殊要求的场景可以使用
4. 自增长主键
方式1:不同自增长初始值+相同步长
方式2:参考“数据库方式”
5. 分布式ZK ID生成 (推荐使用)
使用到的mycat源码:
io.mycat.route.sequence.handler.IncrSequenceZKHandler
io.mycat.route.sequence.handler.DistributedSequenceHandler
环境准备:先在虚拟机192.168.152.130里面装好zookeeper,具体参考我的文章 搭建dubbo+zookeeper+dubboadmin分布式服务框架(windows平台下)
5.1 在server.xml文件中开启全局序列号的配置:
vim server.xml
3
5.2 修改如下配置文件:
1)myid.properties
vim myid.properties
配置文件说明:
loadZK=true|false // 是否使用 zk 序列生成器
zkURL=xxx.xxx.xxx.xxx:2182,xxx.xxx.xxx.xxx:2182,xxx.xxx.xxx.xxx:2182
clusterId= 集群名称
2)sequence_distributed_conf.properties
vim sequence_distributed_conf.properties
配置文件说明:
INSTANCEID=ZK // 改成“ ZK ”(默认是 01 )
CLUSTERID=01 //集群 编号
5.3 重启mycat
[root@centos1 mycat]# ./bin/mycat restart;
5.4 连接mycat进行数据测试
mysql -uroot -p123456 -P8066 -h192.168.152.128
插入数据
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000001'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000002'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000003');
查看结果:
select * from hotnews;
6. ZK递增方式 (推荐使用)
使用到的mycat源码: io.mycat.route.sequence.handler.IncrSequenceZKHandler
6.1 在server.xml文件中开启全局序列号的配置:
vim server.xml
4
使用到的配置文件:
1) myid.properties 参考“5. 分布式ZK ID生成器”中的介绍。
2) sequence_conf.properties
HOTNEWS.HISIDS= #HOTNEWS这张表历史使用的自增id,一般不配置 HOTNEWS.MINID=1001 #zk使用的区间内最小值 HOTNEWS.MAXID=2000 #zk使用的区间内最大值 HOTNEWS.CURID=1000 #zk使用的区间内当前值
说明:
以 hotnews 为例 ,然后根据 sequence_conf.properties 里面的 hotnews 的配置取 MAXID和MINID 的偏移量(这里是 1000 )作为 id 的增量值,当插入数据时把这 1000 个值用完了在继续取MAXID和MINID的偏移量
6.2 重启mycat
[root@centos1 mycat]# ./bin/mycat restart;
6.3 连接mycat进行数据测试
mysql -uroot -p123456 -P8066 -h192.168.152.128
插入数据
insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000001'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000002'); insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, 'test00000003');
查看结果:
select * from hotnews;