序列是一种数据库对象,它可以生成一系列按一定规则递增(或递减)的数值,常用于生成表的主键值。一个序列对象包含名称、起始值、增量值等属性。Oracle、PostgreSQL等数据库中都有序列。MySQL中没有序列,但可以使用AUTO_INCREMENT关键字来生成自增主键,达到类似序列的效果。
随着数据容灾业务和数据库迁移业务的增加,需要确保两端数据库序列一致,使得应用层切换数据库后产生的序列值不会冲突。例如数据表的主键使用了自增序列,如果序列不一致,应用侧切换数据库后插入数据时,可能会生成已存在的主键值,导致入库失败和应用报错。
下面以mysql到postgresql的同步为例,介绍一种较为通用的序列同步方法,总体分为序列匹配、序列属性值同步、序列核对三个步骤。mysql源表的ddl如下:
CREATE TABLE `oper_log` (
`log_id` bigint(20) NOT NULL AUTO_INCREMENT,
`oper_result` tinyint(4) DEFAULT NULL,
`created_time` datetime NOT NULL,
PRIMARY KEY (`log_id`)
);
对应的postgreSQL目标表ddl如下:
CREATE TABLE "oper_log" (
"log_id" BIGINT NOT NULL UNIQUE DEFAULT nextval('"oper_log_id_seq"'),
"oper_result" SMALLINT,
"created_time" TIMESTAMP NOT NULL,
PRIMARY KEY ("log_id")
);
序列匹配
通过识别源表ddl中的auto_increment关键字,可知log_id字段使用了自增序列。查找目标表中log_id字段使用的序列,语句如下:
SELECT column_default FROM information_schema.COLUMNS WHERE "table_name"='oper_log' AND "column_name"='log_id' AND column_default ILIKE 'nextval(%';
根据语句返回结果可得知序列名为oper_log_id_seq。执行如下语句,如果oper_log_id_seq不存在则会创建。由于mysql自增序列的默认起始值和增量值都是1,所以我们创建序列时也设置起始值和增量值为1。
CREATE SEQUENCE IF NOT EXISTS "oper_log_id_seq" START WITH 1 INCREMENT BY 1;
序列属性值同步
属性值包括初始值、增量值、最小值、最大值、当前值等。其中比较常用的是当前值,它是序列产生的最新数值。由于mysql和postgresql是异构数据库,需要对序列属性做一些转换。如果mysql的一些序列属性在postgresql中不被支持,则需要舍弃掉这部分属性。以同步序列的当前值为例,首先读取源表序列的当前值:
select auto_increment from information_schema.`TABLES` where TABLE_NAME = 'oper_log';
假设读取出的值是100,那么更新oper_log_id_seq的当前值为100:
SELECT setval('oper_log_id_seq', 100, false);
序列核对
读取mysql序列当前值的方法与上一步相同。读取pg表的oper_log_id_seq的当前值,并对比两者的当前值是否一致。
select last_value from 'oper_log_id_seq';