本章重点
-
mycat分表分库技术(横向数据切分)
-
mycat数据切分规则(取余分库,自然月分库)
-
mycat全局序列号(实现mysql集群主键ID全局自增)
一、分库分表
简单来说,就是指通过某种特定的条件(分片规则),将我们存放在同一个数据库中的数据分散存放到多个数据库(主机) 上面,以达到分散单台设备负载的效果。
分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成,将数据大表分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。随着微服务这种架构的兴起,应用从一个完整的大的应用,切分为很多可以独立提供服务的小应用。每个应用都有独立的数据库。
1.1 为什么要分库分表
-
表太长
-
减轻数据库压力
-
数据太多,数据并发太大了
-
id用完了
-
查询太慢了
1.2 分库分表类型
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式:
-
垂直切分:按照业务模块进行切分,将不同模块的表切分到不同的数据库中。
-
水平切分:将一张大表按照一定的切分规则,按照行切分到不同的表或者不同的库中。
1.2.3 垂直切分
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面。这种七分可以称之为数据的垂直(纵向)切分。
-
==垂直分库==:是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放不同的服务器上
-
垂直分表:将一个表按照字段分成多表,每个表存储其中一部分字段。
把一个原来43表库分成4个不同的库:
优点:
-
拆分后业务清晰,拆分规则明确;
-
系统之间整合或扩展容易;
-
数据维护简单。(原来我要维护43表,每张表意义级表之间的关联要理清 ,现在每个库都有10张左右的表,每张表意义级表之间的关联更容易理清)
缺点:
-
部分业务表无法join,只能通过接口方式解决,提高了系统复杂度;(当需要跨系统做业务时,难度加大。例如会员管理要调用车辆管理的数据,就需要先获取会员数据,再另一个项目的车辆数据,在服务层拼装)
-
受每种业务不同的限制存在单库性能瓶颈(单表数据量过大的问题),不易数据扩展跟性能提高;
-
事务处理复杂(一个业务既要向会员表添加数据,还要向车辆表添加数据,如果在同一个项目中,只要使用spring声明式事务即可。但现在两张表在不同的库中,不可以使用spring声明式事务, 需要使用分布式事务,难度会加大)
1.2.2 水平切分
一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中。
-
水平分表:是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中
-
水平分库:是把同一个表的数据按一定规则拆分到不同的数据库中,每个库可以放不同的服务器上
某个字段的某种规则:根据用户ID对分表分库总数量取余方式,把数据放入不同的库。
优点:
-
拆分规则抽象好,join 操作基本可以数据库做(数据库中间件为我们实现功能)
-
不存在单库大数据,高并发的性能瓶颈;(把数据库量大的表,都进行拆开,不存在单表数据量大的问题)
-
应用端改造较少
-
提高了系统的稳定性跟负载能力。
缺点:
-
拆分规则难以抽象;(拆分规则复杂)
-
分片事务一致性难以解决;(分布式事务问题)
-
数据多次扩展难度跟维护量极大;
-
跨库 join 性能较差(跨网络请求库)
二、MyCat垂直分库实现
在两台主机上的两个数据库中的表,不可以关联查询。
分库的原则:
有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里。
2.1 准备
结构如下:
-
用户数据:保存在数据库db1中,如用户表,角色表,权限表...
-
订单数据:保存在数据库db2中,如订单表,订单详情表,订单状态字典表...
-
商品数据:保存在数据库db3中,如商品类型,商品详情...
-
准备三台mysql数据库服务器
-
启动数据库服务,在三台mysql数据库中分别创建数据库db1,db2,db3
2.2 修改server.xml
定义连接用户
2.3 修改schema配置文件
2.4 测试
-
启动mycat服务
-
连接mycat
-
在mycat数据库中,执行以下SQL语句,创建数据库表
CREATE TABLE `user_info` (`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',`user_name` varchar(32) DEFAULT NULL COMMENT '账号',`user_nickname` varchar(32) DEFAULT NULL COMMENT '昵称',`user_phone` varchar(13) DEFAULT NULL COMMENT '手机',`user_email` varchar(32) DEFAULT NULL COMMENT '邮箱',`user_pass` varchar(64) DEFAULT NULL COMMENT '密码',`user_state` int(11) DEFAULT NULL COMMENT '状态0 激活 1 未激活 2 禁用',PRIMARY KEY (`user_id`) ); CREATE TABLE `order_info` (`order_num` varchar(64) NOT NULL COMMENT '订单编号',`user_id` int(11) DEFAULT NULL COMMENT '用户ID',`address_info` varchar(128) DEFAULT NULL COMMENT '地址详情',`receiver_name` varchar(32) DEFAULT NULL COMMENT '收货人',`receiver_phone` varchar(13) DEFAULT NULL COMMENT '收货人电话',`order_price` decimal(10,2) DEFAULT NULL COMMENT '订单总价',`create_time` datetime DEFAULT NULL COMMENT '下单时间',`pay_time` datetime DEFAULT NULL COMMENT '支付时间',`order_state` int(11) DEFAULT NULL COMMENT '状态0 未支付 1 已支付 2 已取消 3 待评价 4 已完成 5 已退款',PRIMARY KEY (`order_num`) ); CREATE TABLE `book_type` (`type_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',`type_name` varchar(64) DEFAULT NULL COMMENT '类别名字',`parent_id` int(11) DEFAULT NULL COMMENT '父ID,0就代表没有',`type_state` int(11) DEFAULT NULL COMMENT '状态0 可用 1 删除 2 禁用',`type_self` int(11) DEFAULT NULL COMMENT '是否是叶子节点 0是 1否',PRIMARY KEY (`type_id`) );
发现数据表进入对应的数据库,分库实现成功
-
执行添加数据库操作,可以加入对应的数据库数据表中
INSERT INTO `user_info`(`user_id`, `user_name`, `user_nickname`, `user_phone`, `user_email`, `user_pass`, `user_state`) VALUES (null, 'admin', 'admin', '18523599358', '123@qq.com', '123456', 0);
三、MyCat水平分表实现
3.1 准备
假定预测到order_info订单表,数据量将会巨大(数据量超过1000W或者单表大小超过100G),进行分表分库操作,把订单表的数据进行水平切分,放入不同数据库的不同表中。
结构如下:
-
新建分片数据库:db1,db2,db3
-
在db1,db2,db3数据库下,新建数据库表tb_order
==注意:数据库名可以不一致,但是每个数据库下的分片表名必须一致==
create table tb_order(id int primary key comment '只能是主键不能自增',clientid int comment '客户id',goods_id int comment '商品id',shop_id int comment '商店Id',create_time datetime comment '下单时间',shard_date date comment '分片时间' );
3.2 server.xml配置
同上,不切换逻辑数据库和用户不需要重新配置
3.3 schema.xml配置
新增rule数据,配置数据表分片规则,规则名与rule.xml中声明的一致
3.4 rule.xml配置
分片规则配置文件
-
tablerule:这个标签定义表规则。
-
name:指定唯一的名字( 随便命名,和shema.xml中的table上rule属性名称对应),用于标识不同的表规则。 内嵌的 rule 标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。
-
columns:内指定要拆分的数据库列名字。
-
algorithm:使用 function 标签中的 name 属性。连接表规则和具体路由算法。当然,多个表规则可以连接到 同一个路由算法上。table 标签内使用。让逻辑表使用这个规则进行分片。
-
-
function:
-
name:指定算法的名字
-
class:制定路由算法具体的类名字
-
property:为具体算法需要用到的一些属性
-
3.4 测试
-
配置完成后,启动mycat服务
-
连接mycat,执行新增操作:表中列名称必须写
insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1001,11,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1002,12,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1003,13,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1004,14,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1005,15,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1006,16,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1007,17,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1008,18,10001,101,now(),now()); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(1009,19,10001,101,now(),now());
-
发现对应数据保存到对应的数据库中
按id取余(mod)分片,跟据插入数据的id:
1001%3 =1,该数据就放入编号为0的第2个库中
1002%3 =2,该数据就放入编号为1的第3个库中
1003%3 =0,该数据就放入编号为2的第1个库中
使用mycat查询,可以查到所有数据
四、MyCat分片规则
4.1 取模
此规则为对分片字段求摸运算。
<tableRule name="mod-long"><rule><columns>user_id</columns><algorithm>mod-long</algorithm></rule> </tableRule> <function name="mod-long" class="io.mycat.route.function.PartitionByMod"><!-- how many data nodes --><property name="count">3</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
count | 分片数量 |
根据 id 进行十进制求模预算,相比固定分片 hash,此种在批量插入时可能存在批量插入单事务插入多数据分片,增大事务一致性难度。
4.2 范围约定
此分片适用于,提前规划好分片字段某个范围属于哪个分片。
<tableRule name="auto-sharding-long"><rule><columns>user_id</columns><algorithm>rang-long</algorithm></rule> </tableRule> <function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong"><property name="mapFile">autopartition-long.txt</property><property name="defaultNode">0</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
mapFile | 标识配置文件名称 |
defaultNode | 超过范围后的默认节点 |
所有的节点配置都是从 0 开始,及 0 代表节点 1,此配置非常简单,即预先制定可能的 id 范围到某个分片:
# range start-end ,data node index # K=1000,M=10000. 0-500M=0 500M-1000M=1 1000M-1500M=2 或 0-10000000=0 10000001-20000000=1
4.3 范围求模分片
先进行范围分片计算出分片组,组内再求模。
优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题。综合了范围分片和求模分片的优点,分片组内使用求模可以保证组内数据比较均匀,分片组之间是范围分片,可以兼顾范围查询。
最好事先规划好分片的数量,数据扩容时按分片组扩容,则原有分片组的数据不需要迁移。由于分片组内数据比较均匀,所以分片组内可以避免热点数据问题。
<tableRule name="auto-sharding-rang-mod"><rule><columns>id</columns><algorithm>rang-mod</algorithm></rule> </tableRule> <function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod"><property name="mapFile">partition-range-mod.txt</property><property name="defaultNode">21</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
mapFile | 配置文件路径 |
defaultNode | 超过范围后的默认节点顺序号,节点从 0 开始。 |
partition-range-mod.txt |
# 以下配置一个范围代表一个分片组,=号后面的数字代表该分片组所拥有的分片的数量。 # range start-end ,data node group size 0-200M=5 //代表有 5 个分片节点 200M1-400M=1 400M1-600M=4 600M1-800M=4 800M1-1000M=6
注意
如上0-200M存入到5个分片中,开始范围-结束范围=该分片组有多少个分片。如果超过配置范围需要增加分片组。
4.4 按日期(天)分片
此规则为按天分片。
<tableRule name="sharding-by-date"><rule><columns>create_time</columns><algorithm>sharding-by-date</algorithm></rule> </tableRule> <function name="sharding-by-date" class="io.mycat.route.function.PartitionByDate"><property name="dateFormat">yyyy-MM-dd</property><property name="sBeginDate">2014-01-01</property><property name="sEndDate">2014-01-02</property><property name="sPartionDay">10</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
dateForma | 日期格式 |
sBeginDate | 开始日期 |
sEndDate | 结束日期 |
sPartionDay | 分区天数,即默认从开始日期算起,分隔 10 天一个分区 |
如果配置了 sEndDate 则代表数据达到了这个日期的分片后循环从开始分片插入。
注意
在查询时,如果需要查询时间段应该使用between...and,使用>=或者<=会查询所有分片。
4.5 自然月分片
按月份列分区 ,每个自然月一个分片,格式 between 操作解析的范例。
<tableRule name="sharding-by-month"><rule><columns>create_time</columns ><algorithm>sharding-by-month</algorithm></rule> </tableRule> <function name="sharding-by-month" class="io.mycat.route.function.PartitionByMonth"><property name="dateFormat">yyyy-MM-dd</property><property name="sBeginDate">2014-01-01</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
dateFormat | 日期格式 |
sBeginDate | 开始日期(无默认值) |
sEndDate | 结束日期(无默认值) |
注意
-
默认设置,节点数量必须是12个,每12个月循环从开始分片插入
-
如配置了sBeginDate="2019-01"月是第0个分片,从该时间按月递增,无最大节点
-
配置了sBeginDate = "2015-01-01"sEndDate = "2015-12-01"该配置可以看成和第一个一致
-
配置了sBeginDate = "2015-01-01"sEndDate = "2015-03-01"该配置标识只有 3 个节点;很难与月份对应上;平均分散到 3 个节点上
4.6 日期范围HASH分片
思想与范围求模一致,当由于日期在取模会有数据集中问题,所以改成 hash 方法。先根据日期分组,再根据时间 hash 使得短期内数据分布的更均匀。
优点可以避免扩容时的数据迁移,又可以一定程度上避免范围分片的热点问题。要求日期格式尽量精确些,不然达不到局部均匀的目的
<tableRule name="range-date-hash"><rule><columns>col_date</columns><algorithm>range-date-hash</algorithm></rule> </tableRule> <function name="range-date-hash" class="io.mycat.route.function.PartitionByRangeDateHash"><property name="sBeginDate">2014-01-01 00:00:00</property><property name="sPartionDay">365</property><property name="dateFormat">yyyy-MM-dd HH:mm:ss</property><property name="groupPartionSize">3</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
sBeginDate | 开始日期 |
sPartionDay | 多少天一个分片 |
dateFormat | 日期格式 |
groupPartionSize | 分片组的大小 |
注意
从sBeginDate时间开始计算,每sPartionDay天的数据为一个分片组,每个分片组可以分布在groupPartionSize个分片上面。上面的例子最多可以有三天进行分片,如果超出则会抛出以下异常。
Cause: com.mysql.jdbc.exceptions.jd bc4.MySQLSyntaxErrorException: Can't find a valid data node for specified node index :ALAN_TEST -> RANGE_DATE -> 2019-01-11 12:00:00 -> Index : 4 The error may involve com.mycat.test.model.AlanTest.insert-Inline The error occurred while setting parameters
4.7 按单月小时拆分
此规则是单月内按照小时拆分,最小粒度是小时,可以一天最多 24 个分片,最少 1 个分片,一个月完后下月从头开始循环。每个月月尾,需要手工清理数据。
<tableRule name="sharding-by-hour"><rule><columns>create_time</columns><algorithm>sharding-by-hour</algorithm></rule> </tableRule> <function name="sharding-by-hour" class="io.mycat.route.function.LatestMonthPartion"><property name="splitOneDay">24</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段(字符串类型yyyyMMddHH) |
algorithm | 分片函数 |
splitOneDay | 一天切分的分片数 |
注意
分片字段必须为字符串格式,否则分片不成功,默认存到第一个分片里面; 保存的时间格式必须为"yyyymmddHH"格式,不能多也不能少字符,否则分片不成功,默认存到第一个分片里面;
4.8 分片枚举
通过在配置文件中配置可能的枚举 id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的,这类业务使用本条规则,配置如下:
<tableRule name="sharding-by-intfile"><rule><columns>user_id</columns><algorithm>hash-int</algorithm></rule> </tableRule> <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"><property name="mapFile">partition-hash-int.txt</property><property name="type">0</property><property name="defaultNode">0</property> </function>
配置说明
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
mapFile | 标识配置文件名称 |
type | 默认值为 0,0 表示 Integer,非零表示 String |
defaultNode | 默认节点:小于 0 表示不设置默认节点,大于等于 0 设置默认节点 |
partition-hash-int.txt 配置:
10000=0 10010=1 DEFAULT_NODE=1 //默认节点
默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点,如果不配置默认节点(defaultNode 值小于 0 表示不配置默认节点),碰到不识别的枚举值就会报错
like this:can’t find datanode for sharding column:column_name val:ffffffff
4.9 固定分片 hash 算法
本条规则类似于十进制的求模运算,区别在于是二进制的操作,是取 id 的二进制低 10 位,即 id 二进制 &1111111111。
此算法的优点在于如果按照 10 进制取模运算,在连续插入 1-10 时候 1-10 会被分到 1-10 个分片,增大了插入的事务控制难度,而此算法根据二进制则可能会分到连续的分片,减少插入事务事务控制难度。
<tableRule name="rule1"><rule><columns>user_id</columns><algorithm>func1</algorithm></rule> </tableRule> <function name="func1" class="io.mycat.route.function.PartitionByLong"><property name="partitionCount">2,1</property><property name="partitionLength">256,512</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
partitionCount | 分片个数列表 |
partitionLength | 分片范围列表 |
分区长度:
默认为最大 2^n=1024 ,即最大支持 1024 分区。
约束:
count,length 两个数组的长度必须是一致的;
1024 = sum((count[i]*length[i]))
count 和 length 两个向量的点积恒等于 1024。
如果需要平均分配设置:平均分为 4 分片,partitionCount*partitionLength=1024。
<function name="func1" class="io.mycat.route.function.PartitionByLong"><property name="partitionCount">4</property><property name="partitionLength">256</property> </function>
4.10 截取数字 hash 解析
此规则是截取字符串中的 int 数值 hash 分片。
<tableRule name="sharding-by-stringhash"><rule><columns>user_id</columns><algorithm>sharding-by-stringhash</algorithm></rule> </tableRule> <function name="sharding-by-stringhash" class="io.mycat.route.function.PartitionByString"><property name="partitionLength">512</property><!-- zero-based --><property name="partitionCount">2</property><property name="hashSlice">0:2</property> </function>
配置说明:
标签属性 | 说明 |
---|---|
columns | 标识将要分片的表字段 |
algorithm | 分片函数 |
partitionLength | 字符串hash求模基数 |
partitionCount | 分区数 |
hashSlice | 预算位,即根据子字符串中 int 值 hash 运算。 0 means str.length(), -1 means str.length()-1 |
注意
hashSlice可以理解为substring(start,end),start为0则只表示0; 例1:值“45abc”,hash预算位0:2 ,取其中45进行计算 例2:值“aaaabbb2345”,hash预算位-4:0 ,取其中2345进行计算
五、MyCat全局序列号
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一(分片后,每个表主键id不能像过去一样,各自设置自增,如果设置,在mycat中查询id就会重复。为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。
5.1 sequnceHandlerType
序列号处理器类型
-
0: 本地文件方式(sequence_conf.properties)
-
1: 数据库方式(MYCAT_SEQUENCE表、sequence_db_conf.properties)
-
2: 本地时间戳方式(sequence_time_conf.properties)
-
3: 分布式ZK ID生成器
-
4: ZK递增方式
server.xml 中配置:
<system><property name="sequnceHandlerType">0</property> </system> 注:sequnceHandlerType 配置为0表示使用本地文件读取。
5.2 本地文件方式
原理:
此方式 MyCAT 将 sequence 配置到文件中,当使用到 sequence 中的配置后,MyCAT 会更下 classpath 中的 sequence_conf.properties 文件中 sequence 当前的值。
配置方式:
在MyCat的conf
目录下,找到sequence_conf.properties
文件。这个文件包含了序列的相关配置信息,如各个表或全局序列的最大值、当前值、历史值等。
vim /usr/mycat-sharding/mycat/conf/sequence_conf.properties
# 全局配置 GLOBAL.HISIDS= GLOBAL.MINID=1001 GLOBAL.MAXID=100000000 GLOBAL.CURID=1000 # 表配置,USERS是server.xml中配置的table name的大写 USERS.HISIDS= USERS.MINID=1001 USERS.MAXID=100000000 USERS.CURID=1000
配置项说明:
-
XXX.MAXID
:表示某个表或全局序列的最大ID值。 -
XXX.CURID
:表示某个表或全局序列的当前ID值。 -
XXX.HISIDS
:表示某个表或全局序列的历史ID值(通常用于记录已经使用过的ID,以避免重复)。
序列值的更新和使用:
-
更新:当应用程序从MyCat获取并使用一个序列值时,MyCat会更新
sequence_conf.properties
文件中对应的序列当前值。 -
使用:在应用程序中,可以通过SQL语句如
SELECT NEXT_VALUE FOR global_seq;
来获取全局序列的下一个值。
重启服务:
/usr/mycat-sharding/mycat/bin/mycat restart
测试查询下一个值:
select next value for MYCATSEQ_GLOBAL; select next value for MYCATSEQ_TB_TEST; select next value for MYCATSEQ_序列名;
使用示例:
insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(next value for MYCATSEQ_GLOBAL,19,10001,101,now(),'2022-01-22'); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(next value for MYCATSEQ_GLOBAL,19,10001,101,now(),'2022-02-22'); insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(next value for MYCATSEQ_GLOBAL,19,10001,101,now(),'2022-03-22');
5.3 数据库方式
在数据库中建立一张表,存放sequence名称、sequence当前值、步长等信息。MyCat负责维护这张表,并读取和更新sequence值。
vim sequence_db_conf.properties
<property name="sequnceHandlerType">1</property>
创建和维护全局序列号数据库表:
在MyCat后端连接的某个数据库中,创建一个用于存储全局序列号信息的表。这个表通常包含序列名称、当前值、步长等信息。
在数据库(db1,db2,db3任意)下执行SQL,创建索引表和函数:
DROP TABLE IF EXISTS MYCAT_SEQUENCE; CREATE TABLE MYCAT_SEQUENCE ( NAME VARCHAR (50) NOT NULL, current_value INT NOT NULL, increment INT NOT NULL DEFAULT 100, PRIMARY KEY (NAME) ) ENGINE = INNODB ; DROP FUNCTION IF EXISTS `mycat_seq_currval`; DELIMITER ;; CREATE FUNCTION `mycat_seq_currval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64) CHARSET utf8DETERMINISTIC 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 ; DROP FUNCTION IF EXISTS `mycat_seq_nextval`; DELIMITER ;; CREATE FUNCTION `mycat_seq_nextval`(seq_name VARCHAR(50)) RETURNS VARCHAR(64)CHARSET utf8DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = current_value + increment WHERE NAME = seq_name; RETURN mycat_seq_currval(seq_name); END ;; DELIMITER ; DROP FUNCTION IF EXISTS `mycat_seq_setval`; DELIMITER ;; CREATE FUNCTION `mycat_seq_setval`(seq_name VARCHAR(50), VALUE INTEGER) RETURNS VARCHAR(64) CHARSET utf8DETERMINISTIC BEGIN UPDATE MYCAT_SEQUENCE SET current_value = VALUE WHERE NAME = seq_name; RETURN mycat_seq_currval(seq_name); END ;; DELIMITER ;
配置sequence_db_conf.properties文件:
# sequence stored in datanode # 索引名字必须和数据库表中村的索引名一致 GLOBAL=dn1 COMPANY=dn1 CUSTOMER=dn1 ORDERS=dn1
重启:
重启mycat
测试:
SELECT next value for MYCATSEQ_GLOBAL; SELECT next value for MYCATSEQ_COMPANY; insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(next value for MYCATSEQ_GLOBAL,19,10001,101,now(),'2022-03-22');
5.4 本地时间戳方式
<property name="sequnceHandlerType">2</property>
重启:
重启mycat
测试:
insert into tb_order(id,clientid,goods_id,shop_id,create_time,shard_date) values(next value for MYCATSEQ_GLOBAL,19,10001,101,now(),'2024-06-21');