Java家技术论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java家技术论坛(http://www.javajia.net)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JavaEE 49期就业班视频教程2019最新 JavaEE 57期 入门到项目实战

最新微服务架构实战160讲教程

Go语言视频零基础入门到精通

Java从菜鸟到大神的学习路线之实战篇

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
查看: 224|回复: 0

[默认分类] Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案

[复制链接]
  • TA的每日心情
    开心
    2020-8-4 20:10
  • 签到天数: 8 天

    [LV.3]偶尔看看II

    发表于 2020-8-1 17:32:22 | 显示全部楼层 |阅读模式
    一、前言
    在前面的文章分库分表需要应对的技术难题有如下几个:
    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

    1. <property name="sequnceHandlerType">0</property>
    复制代码


      sequnceHandlerType可取的值有以下几个:
        0:本地文件方式
        1:数据库方式
        2:时间戳方式
        3:ZKID生成器
        4:ZK递增ID
    2)autoIncrement属性为true(schema.xml)

    1. <table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3"  rule="mod-long" />
    复制代码


    1. 本地文件方式
    使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceHandler
    在server.xml文件中开启全局序列号的配置:

    1. <property name="sequnceHandlerType">0</property>
    复制代码


    使用到的配置文件:sequence_conf.properties

    1. #default global sequence
    2. GLOBAL.HISIDS=
    3. GLOBAL.MINID=10001
    4. GLOBAL.MAXID=20000
    5. GLOBAL.CURID=10000
    6. # self define sequence
    7. COMPANY.HISIDS=
    8. COMPANY.MINID=1001
    9. COMPANY.MAXID=2000
    10. COMPANY.CURID=1000
    11. CUSTOMER.HISIDS=
    12. CUSTOMER.MINID=1001
    13. CUSTOMER.MAXID=2000
    14. CUSTOMER.CURID=1000
    15. ORDER.HISIDS=
    16. ORDER.MINID=1001
    17. ORDER.MAXID=2000
    18. ORDER.CURID=1000
    19. HOTNEWS.HISIDS=
    20. HOTNEWS.MINID=1001
    21. HOTNEWS.MAXID=2000
    22. HOTNEWS.CURID=1000
    复制代码


    拿HOTNEWS这个表的配置来说明:

    1. HOTNEWS.HISIDS=       #HOTNEWS这张表历史使用的自增id,一般不配置
    2. HOTNEWS.MINID=1001    #HOTNEWS这张表使用的最小自增id
    3. HOTNEWS.MAXID=2000    #HOTNEWS这张表使用的最大自增id
    4. HOTNEWS.CURID=1000    #HOTNEWS这张表当前使用的自增id
    复制代码


    缺点:当Mycat重新发布后,自增ID恢复到初始值。原因是因为sequnceHandlerType定义的是静态变量,不推荐使用
    示例:
    1.1 在mycat的schema.xml文件里面分别配置逻辑表hotnews和mysql的主从机,注意autoIncrement="true"才能使用全局唯一id

    1. <table name="hotnews" primaryKey="ID" autoIncrement="true" dataNode="dn1,dn2,dn3" rule="mod-long" />
    2. <dataNode name="dn1" dataHost="centos1" database="db1" />
    3.         <dataNode name="dn2" dataHost="centos1" database="db2" />
    4.         <dataNode name="dn3" dataHost="centos1" database="db3" />
    5.         <dataHost name="centos1" maxCon="1000" minCon="10" balance="0"
    6.                           writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
    7.                 <heartbeat>select user()</heartbeat>
    8.                 <!-- can have multi write hosts -->
    9.                 <writeHost host="hostM1" url="192.168.152.130:3306" user="root" password="123456">
    10.                         <!-- can have multi read hosts -->
    11.                         <readHost host="hostS2" url="192.168.152.131:3306" user="root" password="123456" />
    12.                 </writeHost>
    13.                 <!-- <writeHost host="hostS1" url="localhost:3316" user="root"
    14.                                    password="123456" />-->
    15.                 <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
    16. </dataHost>
    复制代码


    1.2 在主数据库(192.168.152.130)分别创建3个数据库db1,db2,db3,然后创建hotnews表

    1. create database db1;
    2. use db1;
    3. create table hotnews(
    4.    id bigint(20) not null primary key auto_increment,
    5.     title varchar(50) default null
    6. );
    7. create database db2;
    8. use db2;
    9. create table hotnews(
    10.    id bigint(20) not null primary key auto_increment,
    11.     title varchar(50) default null
    12. );
    13. create database db3;
    14. use db3;
    15. create table hotnews(
    16.    id bigint(20) not null primary key auto_increment,
    17.     title varchar(50) default null
    18. );
    复制代码


    1.3 插入数据
    插入数据到hotnews前我们先来看一下使用本地文件方式的配置文件sequence_conf.properties的内容

    1. cat sequence_conf.properties |grep HOTNEWS
    复制代码



    插入数据到hotnews

    1. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test1");
    2. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test2");
    3. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test3");
    复制代码


    1.4 查看hotnews表的结果

    1. select * from hotnews;
    复制代码



    再次查看配置文件sequence_conf.properties的内容,发现内容随着插入数据的自增id做了改变

    2. 数据库方式
    这种方式和本地文件的方式是一样的,只是把sequence_conf.properties的内容用数据库来管理
    使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceMySQLHandler
    这里还是以hotnews表为例
    2.1 在server.xml文件中开启全局序列号的配置:

    1. <property name="sequnceHandlerType">1</property>
    复制代码


    使用到的配置文件:sequence_db_conf.properties
    1. vim sequence_db_conf.properties
    复制代码


    1. #sequence stored in datanode
    2. GLOBAL=dn1
    3. COMPANY=dn1
    4. CUSTOMER=dn1
    5. ORDERS=dn1
    复制代码


    2.2 选择其中的一个分片,执行如下步骤,譬如我在dn1中创建,对应的数据库名为db1(为什么这里会涉及到datanode,因为后续的sequence_db_conf.properties文件会使用到),注意:是登录到数据库中创建,而不是在mycat中创建
    第一步:创建SEQUENCE表,用来存储序列号

    1. use db1;
    2. DROP TABLE IF EXISTS MYCAT_SEQUENCE;
    3. CREATE TABLE MYCAT_SEQUENCE (
    4. NAME VARCHAR (50) NOT NULL, /*全局SEQ名称*/
    5. current_value INT NOT NULL, /*当前序列ID*/
    6. increment INT NOT NULL DEFAULT 100, /*初始序列ID*/
    7. PRIMARY KEY (NAME)
    8. ) ENGINE = INNODB ;
    9. INSERT INTO MYCAT_SEQUENCE(name,current_value,increment) VALUES ("GLOBAL", 100000, 100);
    复制代码


    第二步:创建SEQ function

    1. -- 获取当前sequence的值(返回当前值,增量)
    2. DROP FUNCTION IF EXISTS `mycat_seq_currval`;
    3. DELIMITER ;;
    4. CREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50))
    5. RETURNS varchar(64) CHARSET utf8
    6.     DETERMINISTIC
    7. BEGIN DECLARE retval VARCHAR(64);
    8.         SET retval="-999999999,null";  
    9.         SELECT concat(CAST(current_value AS CHAR),",",CAST(increment AS CHAR) ) INTO retval
    10.           FROM MYCAT_SEQUENCE WHERE name = seq_name;  
    11.         RETURN retval ;
    12. END
    13. ;;
    14. DELIMITER ;
    15. -- 获取下一个sequence值
    16. DROP FUNCTION IF EXISTS `mycat_seq_nextval`;
    17. DELIMITER ;;
    18. CREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS varchar(64)
    19. CHARSET utf8
    20.     DETERMINISTIC
    21. BEGIN UPDATE MYCAT_SEQUENCE  
    22.                  SET current_value = current_value + increment
    23.                   WHERE name = seq_name;  
    24.          RETURN mycat_seq_currval(seq_name);  
    25. END
    26. ;;
    27. DELIMITER ;
    28. -- 设置sequence值
    29. DROP FUNCTION IF EXISTS `mycat_seq_setval`;
    30. DELIMITER ;;
    31. CREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50), value INTEGER)
    32. RETURNS varchar(64) CHARSET utf8
    33.     DETERMINISTIC
    34. BEGIN UPDATE MYCAT_SEQUENCE  
    35.                    SET current_value = value  
    36.                    WHERE name = seq_name;  
    37.          RETURN mycat_seq_currval(seq_name);  
    38. END
    39. ;;
    40. DELIMITER ;
    复制代码


    插入需要自增长的表的策略,这条数据是我们hotnews这个表所需要的。 name必须是大写的字符,不然就会报错  

    1. 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

    1. [root@centos1 mycat]# ./bin/mycat restart;
    复制代码



    2.4 连接mycat进行数据测试

    1. mysql -uroot -p123456 -P8066 -h192.168.152.128
    复制代码


    插入数据

    1. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test11111");
    2. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test11112");
    3. insert into hotnews(id, title) values(next value for MYCATSEQ_HOTNEWS, "test11113");
    复制代码


    查看结果:

    1. select * from hotnews;
    复制代码



    缺点:当mycat挂掉时可能出现主键冲突。不推荐使用
    3. 本地时间戳方式
    使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceTimeHandler
    3.1 在server.xml文件中开启全局序列号的配置:

    1. <property name="sequnceHandlerType">2</property>
    复制代码


    使用到的配置文件:
    sequence_time_conf.properties
              WORKID=01(范围01-31)
              DATAACENTERID=01(范围01-31)
      
    示例:
    首先清空hotnews这张表的数据,方便查看测试结果

    1. delete from hotnews;
    复制代码


    插入测试数据

    1. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test11111");
    2. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test11112");
    3. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test11113");
    复制代码


    查看结果:

    1. 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

    1. <property name="sequnceHandlerType">3</property>
    复制代码


    5.2 修改如下配置文件:
    1)myid.properties

    1. 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

    1. vim sequence_distributed_conf.properties
    复制代码



    配置文件说明:
    INSTANCEID=ZK  //改成“ZK”(默认是01)
    CLUSTERID=01   //集群编号
    5.3 重启mycat

    1. [root@centos1 mycat]# ./bin/mycat restart;
    复制代码


    5.4 连接mycat进行数据测试

    1. mysql -uroot -p123456 -P8066 -h192.168.152.128
    复制代码


    插入数据

    1. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000001");
    2. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000002");
    3. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000003");
    复制代码


    查看结果:

    1. select * from hotnews;
    复制代码



    6. ZK递增方式(推荐使用)
    使用到的mycat源码:io.mycat.route.sequence.handler.IncrSequenceZKHandler
    6.1 在server.xml文件中开启全局序列号的配置:
    vim server.xml

    1. <property name="sequnceHandlerType">4</property>
    复制代码


    使用到的配置文件:
    1) myid.properties 参考“5. 分布式ZK ID生成器”中的介绍。
    2) sequence_conf.properties

    1. HOTNEWS.HISIDS=       #HOTNEWS这张表历史使用的自增id,一般不配置
    2. HOTNEWS.MINID=1001    #zk使用的区间内最小值
    3. HOTNEWS.MAXID=2000    #zk使用的区间内最大值
    4. HOTNEWS.CURID=1000    #zk使用的区间内当前值
    复制代码


    说明:
    以hotnews为例,然后根据sequence_conf.properties里面的hotnews的配置取MAXID和MINID的偏移量(这里是1000)作为id的增量值,当插入数据时把这1000个值用完了在继续取MAXID和MINID的偏移量
    6.2 重启mycat

    1. [root@centos1 mycat]# ./bin/mycat restart;
    复制代码


    6.3  连接mycat进行数据测试

    1. mysql -uroot -p123456 -P8066 -h192.168.152.128
    复制代码


    插入数据

    1. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000001");
    2. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000002");
    3. insert into hotnews(id, title) values(next value for MYCATSEQ_GLOBAL, "test00000003");
    复制代码


    查看结果:

    1. select *  from hotnews;
    复制代码



    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    .

    Java家技术论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2020-9-28 17:38 , Processed in 0.158153 second(s), 43 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表