Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案
一、前言
在前面的文章Mysql系列四:数据库分库分表基础理论中,已经说过分库分表需要应对的技术难题有如下几个:
1. 分布式全局唯一id
2. 分片规则和策略
3. 跨分片技术问题
4. 跨分片事物问题
下面我们来看一下Mycat是如何解决分布式全局唯一id的问题的
二、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
<property name="sequnceHandlerType">0</property>
sequnceHandlerType可取的值有以下几个:
0:本地文件方式
1:数据库方式
2:时间戳方式
3:ZKID生成器
4:ZK递增ID
2)autoIncrement属性为true(schema.xml)
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
1. 本地文件方式
使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceHandler
在server.xml文件中开启全局序列号的配置:
<property name="sequnceHandlerType">0</property>
使用到的配置文件: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
<table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
<dataNode name="dn1" dataHost="centos1" database="db1" />
<dataNode name="dn2" dataHost="centos1" database="db2" />
<dataNode name="dn3" dataHost="centos1" database="db3" />
<dataHost name="centos1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="192.168.152.130:3306" user="root" password="123456">
<!-- can have multi read hosts -->
<readHost host="hostS2" url="192.168.152.131:3306" user="root" password="123456" />
</writeHost>
<!-- <writeHost host="hostS1" url="localhost:3316" user="root"
password="123456" />-->
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
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文件中开启全局序列号的配置:
<property name="sequnceHandlerType">1</property>
使用到的配置文件: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','','');
说明:插入了一个名为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文件中开启全局序列号的配置:
<property name="sequnceHandlerType">2</property>
使用到的配置文件:
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
<property name="sequnceHandlerType">3</property>
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
<property name="sequnceHandlerType">4</property>
使用到的配置文件:
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;
Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案的更多相关文章
- MySQL纯透明的分库分表技术还没有
MySQL纯透明的分库分表技术还没有 种树人./oneproxy --proxy-address=:3307 --admin-username=admin --admin-password=D033 ...
- 分库分表的 9种分布式主键ID 生成方案,挺全乎的
<sharding-jdbc 分库分表的 4种分片策略> 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为 ...
- 分库分表的情况下生成全局唯一的ID
分库分表情况下 跨库的问题怎么解决? 分布式事务怎么解决? 查询结果集集合合并的问题? 全局唯一的id怎么解决? 一般要求:1.保证生成的ID全局唯一,不可重复 2.生成的后一个Id必须大于前一个Id ...
- 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记
1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...
- Mysql中的分库分表
mysql中的分库分表分库:减少并发问题分表:降低了分布式事务分表 1.垂直分表 把其中的不常用的基础信息提取出来,放到一个表中通过id进行关联.降低表的大小来控制性能,但是这种方式没有解决高数据量带 ...
- Mycat安装并实现mysql读写分离,分库分表
Mycat安装并实现mysql读写分离,分库分表 一.安装Mycat 1.1 创建文件夹 1.2 下载 二.mycat具体配置 2.1 server.xml 2.2 schema.xml 2.3 se ...
- Docker安装Mycat并实现mysql读写分离,分库分表
Docker安装Mycat并实现mysql读写分离,分库分表 一.拉取mycat镜像 二.准备挂载的配置文件 2.1 创建文件夹并添加配置文件 2.1.1 server.xml 2.1.2 serve ...
- DB 分库分表(2):全局主键生成策略
DB 分库分表(2):全局主键生成策略 本文将主要介绍一些常见的全局主键生成策略,然后重点介绍flickr使用的一种非常优秀的全局主键生成方案.关于分库分表(sharding)的拆分策略和实施细则,请 ...
- (4.24)【mysql、sql server】分布式全局唯一ID生成方案
参考:分布式全局唯一ID生成方案:https://blog.csdn.net/linzhiqiang0316/article/details/80425437 分表生成唯一ID方案 sql serve ...
随机推荐
- ModelForm的使用
from django.forms import Form,ModelForm,fields,widgets as wd class QueModelForm(ModelForm): class Me ...
- Bracket 使用指南
Brackets 是一个免费.开源且跨平台的 HTML/CSS/JavaScript 前端 WEB 集成开发环境 (IDE工具).该项目由Adobe 创建和维护,根据MIT许可证发布,支持 Windo ...
- stdlib库中qsort函数的使用
qsort :功 能: 使用快速排序例程进行排序 用 法: void qsort(void *base, int nelem, int width, int (*fcmp)(const void * ...
- [模板][P3377]杜教筛
Description: 求 $ \sum_{i=1}^n \phi(i) ,\sum_{i=1}^n \mu(i)$ Hint: \(n<=10^{10}\) Solution: 考虑积性函 ...
- Java全栈程序员之01:做个Linux下的程序猿
Windows10正在成为史上口碑最差的Windows系统 (图侵删) 我曾经花了数次1小时去寻找解决方案去关闭自动更新,包括停掉服务.修改注册表等等.但是都没有成功. 微软自身是知道这个问题的,但就 ...
- 卡尔曼滤波(Kalman Filter) ZZ
一.引言 以下我们引用文献[1]中的一段话作为本文的開始: 想象你在黄昏时分看着一仅仅小鸟飞行穿过浓密的丛林.你仅仅能隐隐约约.断断续续地瞥见小鸟运动的闪现.你试图努力地猜測小鸟在哪里以及下一时刻它会 ...
- [C++] 左值、右值、右值引用
一般意义上的左值(lvalue)和右值(rvalue) * lvalue 代表了对象,可通过取地址符获取地址,可赋值.L 可看做 location. * rvalue 代表了数据,不能获取内存地址,不 ...
- C语言的原码,反码,补码
1)原码表示 原码表示法是机器数的一种简单的表示法.其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示.设有一数为x,则原码表示可记作[x]原. 例如,X1= +1010110 X2= 一1 ...
- WPF获取当前用户控件的父级窗体
方式一.通过当前控件名获取父级窗体 Window targetWindow = Window.GetWindow(button); 方式二.通过当前控件获取父级窗体 Window parentWind ...
- imp-oracle10g数据库dmp导入到11g数据库提示IMP-00058,表或试图不存在
拿了一份从10g数据库导出的dmp,本机安装的是11.2g的oracle数据库,执行imp导入后提示: IMP-00058: 遇到 ORACLE 错误 942ORA-00942: 表或视图不存在 开始 ...