1 主键重复
之前创建了 PAYMENT_ADDRESS 数据表,根据 area\_id 的值,进行分片设置。
那么在实际使用当中,虽然我们在建表时将 id 字段设置为了主键,但该主键的作用域仅在单个分片的数据库中起作用。当数据被分到不同的数据库分片中,该设置就会失去作用了。
我们的分片规则是:
100000-300000=0
300001-900000=1
在下图中,因为 AREA_ID=100000 和 500000 时,就会被分配到不同的分片中,所以就出现了ID重复的数据。
那么如何才能避免上述问题呢?
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 属性一致。
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 ;
设置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 ;
获取下一个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 ;
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表
- 在dn1中查询
- 在dn2中查询
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");
查询数据表
可以看到,新记录的ID是从101开始,原来的100条ID因为mycat重启后,即使没有使用完,也会被全部丢弃,向数据表重新申请了一批新的序列。
标签:name,seq,area,value,mycat,分片,全局,MyCat16,id From: https://blog.51cto.com/u_113754/6318171