1 主键重复

之前创建了 PAYMENT_ADDRESS 数据表,根据 area\_id 的值,进行分片设置。

MyCat16——分片规则之全局序列_主键

那么在实际使用当中,虽然我们在建表时将 id 字段设置为了主键,但该主键的作用域仅在单个分片的数据库中起作用。当数据被分到不同的数据库分片中,该设置就会失去作用了。

我们的分片规则是:

100000-300000=0

300001-900000=1

在下图中,因为 AREA_ID=100000 和 500000 时,就会被分配到不同的分片中,所以就出现了ID重复的数据。

MyCat16——分片规则之全局序列_数据库_02

那么如何才能避免上述问题呢?

2 解决方案

解决思路:

对于主键进行集中管理,统一分配,避免出现主键重复的问题。

2.1 本地sequence文件

在mycat中采用一个配置文件,用于存储分片表中的主键信息,取消由数据库自己控制。

优点:本地加载,读取速度快。

缺点:如果mycat宕机或配置文件损坏,主键信息将丢失。

2.2 时间戳方式

全局序列ID:由42位毫秒,5位机器ID,5位业务编码,12位重复累加值组成的64位二进制数据组成,换算为十进制数据是18位长度的long类型。可以支持每毫秒12位二进制的累加。

优点:配置简单。

缺点:18位ID数据有些臃肿。

2.3 数据库方式

在数据库中创建一个表,来实现数据累加。

为减少数据库的读写操作,mycat中会先请求数据库后加载一批号段到内存中,当号段消耗殆尽时,会再次向数据库重新请求一批号段。

如果mycat发生宕机,已经分配出去的号段不会回收,从而避免主键重复。

2.3.1 修改配置文件server.xml

全局序列类型:

  • 0 本地文件
  • 1 数据库方式
  • 2 时间戳方式

此处应修改为1。

<property name="sequenceHandlerType">1</property>

2.3.2 修改配置文件schema.xml

添加全局序列表

<table name="mycat_seq" primaryKey="name" dataNode="dn2"/>

2.3.3 修改配置文件sequence_db_conf.properties

该配置文件在conf目录下,在文件末尾添加 MYCAT=dn2,与上一步中配置的 dataNode 属性一致。

MyCat16——分片规则之全局序列_配置文件_03

2.3.4 在dn2节点添加mycat_seq表

进入dn2节点的mysql数据库中,添加用于存放全局序列的mycat_seq表。

create table MYCAT_SEQ(name varchar(50) not null primary key, current_value int not null, increment int not null default 100) engine=InnoDB;

向表中插入一条初始记录:

插入一条名称为 mycat 的序列,其当前值为-99,一次可生成的序列数。

insert into MYCAT_SEQ(name, current_value, increment) values('mycat',-99,100);

2.3.5 存储过程

创建存储过程

DELIMITER $$
CREATE FUNCTION mycat_seq_currval(seq_name varchar(50)) RETURNS VARCHAR(64)
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_SEQ WHERE NAME = seq_name;     
RETURN retval;     
END$$
DELIMITER ;

MyCat16——分片规则之全局序列_主键_04

设置sequence值

DELIMITER $$
CREATE FUNCTION mycat_seq_setval(seq_name VARCHAR(50), VALUE INTEGER) RETURNS
VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQ
SET current_value = VALUE
WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

MyCat16——分片规则之全局序列_配置文件_05

获取下一个sequence值

DELIMITER $$
CREATE FUNCTION mycat_seq_nextval(seq_name VARCHAR(50)) RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
UPDATE MYCAT_SEQ
SET current_value = current_value + increment WHERE NAME = seq_name;
RETURN mycat_seq_currval(seq_name);
END $$
DELIMITER ;

MyCat16——分片规则之全局序列_主键_06

2.3.6 重启mycat

mycat restart

2.3.7 数据验证

  • 清除PAYMENT_ADDRESS表

truncate table PAYMENT_ADDRESS;

  • 向表中使用MYCAT序列的方式,重新插入数据

insert into PAYMENT_ADDRESS(id, area_id, area_str) values(next value for mycatseq_mycat, 100000, "beijing");

insert into PAYMENT_ADDRESS(id, area_id, area_str) values(next value for mycatseq_mycat, 270011, "sichuan");

insert into PAYMENT_ADDRESS(id, area_id, area_str) values(next value for mycatseq_mycat, 300001, "yunnan");

insert into PAYMENT_ADDRESS(id, area_id, area_str) values(next value for mycatseq_mycat, 700000, "heilongjiang");

  • 在mycat中查询PAYMENT_ADDRESS表

MyCat16——分片规则之全局序列_主键_07

  • 在dn1中查询

MyCat16——分片规则之全局序列_主键_08

  • 在dn2中查询

MyCat16——分片规则之全局序列_配置文件_09

2.3.8 重启mycat验证

重新启动mycat后,再次向 PAYMENT_ADDRESS表 中插入记录,验证主键会发生什么变化。

mycat restart

mysql -uroot -p123456 -h192.168.137.3 -P8066

use TESTDB;

insert into PAYMENT_ADDRESS(id, area_id, area_str) values(next value for mycatseq_mycat, 100001, "tianjin");

查询数据表

MyCat16——分片规则之全局序列_主键_10

可以看到,新记录的ID是从101开始,原来的100条ID因为mycat重启后,即使没有使用完,也会被全部丢弃,向数据表重新申请了一批新的序列。