一、前言

在前面的文章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解决方案的更多相关文章

  1. MySQL纯透明的分库分表技术还没有

    MySQL纯透明的分库分表技术还没有  种树人./oneproxy --proxy-address=:3307 --admin-username=admin --admin-password=D033 ...

  2. 分库分表的 9种分布式主键ID 生成方案,挺全乎的

    <sharding-jdbc 分库分表的 4种分片策略> 中我们介绍了 sharding-jdbc 4种分片策略的使用场景,可以满足基础的分片功能开发,这篇我们来看看分库分表后,应该如何为 ...

  3. 分库分表的情况下生成全局唯一的ID

    分库分表情况下 跨库的问题怎么解决? 分布式事务怎么解决? 查询结果集集合合并的问题? 全局唯一的id怎么解决? 一般要求:1.保证生成的ID全局唯一,不可重复 2.生成的后一个Id必须大于前一个Id ...

  4. 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记

    1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...

  5. Mysql中的分库分表

    mysql中的分库分表分库:减少并发问题分表:降低了分布式事务分表 1.垂直分表 把其中的不常用的基础信息提取出来,放到一个表中通过id进行关联.降低表的大小来控制性能,但是这种方式没有解决高数据量带 ...

  6. Mycat安装并实现mysql读写分离,分库分表

    Mycat安装并实现mysql读写分离,分库分表 一.安装Mycat 1.1 创建文件夹 1.2 下载 二.mycat具体配置 2.1 server.xml 2.2 schema.xml 2.3 se ...

  7. Docker安装Mycat并实现mysql读写分离,分库分表

    Docker安装Mycat并实现mysql读写分离,分库分表 一.拉取mycat镜像 二.准备挂载的配置文件 2.1 创建文件夹并添加配置文件 2.1.1 server.xml 2.1.2 serve ...

  8. DB 分库分表(2):全局主键生成策略

    DB 分库分表(2):全局主键生成策略 本文将主要介绍一些常见的全局主键生成策略,然后重点介绍flickr使用的一种非常优秀的全局主键生成方案.关于分库分表(sharding)的拆分策略和实施细则,请 ...

  9. (4.24)【mysql、sql server】分布式全局唯一ID生成方案

    参考:分布式全局唯一ID生成方案:https://blog.csdn.net/linzhiqiang0316/article/details/80425437 分表生成唯一ID方案 sql serve ...

随机推荐

  1. ModelForm的使用

    from django.forms import Form,ModelForm,fields,widgets as wd class QueModelForm(ModelForm): class Me ...

  2. Bracket 使用指南

    Brackets 是一个免费.开源且跨平台的 HTML/CSS/JavaScript 前端 WEB 集成开发环境 (IDE工具).该项目由Adobe 创建和维护,根据MIT许可证发布,支持 Windo ...

  3. stdlib库中qsort函数的使用

    qsort :功 能: 使用快速排序例程进行排序  用 法: void qsort(void *base, int nelem, int width, int (*fcmp)(const void * ...

  4. [模板][P3377]杜教筛

    Description: 求 $ \sum_{i=1}^n \phi(i) ,\sum_{i=1}^n \mu(i)$ Hint: \(n<=10^{10}​\) Solution: 考虑积性函 ...

  5. Java全栈程序员之01:做个Linux下的程序猿

    Windows10正在成为史上口碑最差的Windows系统 (图侵删) 我曾经花了数次1小时去寻找解决方案去关闭自动更新,包括停掉服务.修改注册表等等.但是都没有成功. 微软自身是知道这个问题的,但就 ...

  6. 卡尔曼滤波(Kalman Filter) ZZ

    一.引言 以下我们引用文献[1]中的一段话作为本文的開始: 想象你在黄昏时分看着一仅仅小鸟飞行穿过浓密的丛林.你仅仅能隐隐约约.断断续续地瞥见小鸟运动的闪现.你试图努力地猜測小鸟在哪里以及下一时刻它会 ...

  7. [C++] 左值、右值、右值引用

    一般意义上的左值(lvalue)和右值(rvalue) * lvalue 代表了对象,可通过取地址符获取地址,可赋值.L 可看做 location. * rvalue 代表了数据,不能获取内存地址,不 ...

  8. C语言的原码,反码,补码

    1)原码表示 原码表示法是机器数的一种简单的表示法.其符号位用0表示正号,用:表示负号,数值一般用二进制形式表示.设有一数为x,则原码表示可记作[x]原. 例如,X1= +1010110 X2= 一1 ...

  9. WPF获取当前用户控件的父级窗体

    方式一.通过当前控件名获取父级窗体 Window targetWindow = Window.GetWindow(button); 方式二.通过当前控件获取父级窗体 Window parentWind ...

  10. imp-oracle10g数据库dmp导入到11g数据库提示IMP-00058,表或试图不存在

    拿了一份从10g数据库导出的dmp,本机安装的是11.2g的oracle数据库,执行imp导入后提示: IMP-00058: 遇到 ORACLE 错误 942ORA-00942: 表或视图不存在 开始 ...