表结构:
DROP TABLE IF EXISTS `double_write_test_old`;
CREATE TABLE `double_write_test_old` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`data` int NOT NULL COMMENT '数据',
`updated_at` datatime,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '双写测试旧表' ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `double_write_test_new`;
CREATE TABLE `double_write_test_new` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`data` int NOT NULL COMMENT '数据',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '双写测试新表' ROW_FORMAT = Dynamic;
只存在新增数据
setp 1
开启后台脚本
从旧数据开始往前追;
小批量插入;(一次太多会给旧库和新库太大压力);
脚本停止条件:
直到因已存在而插入失败则停止;
setp 2
在线双写
old.insert
insert into double_write_test_old(`data`) value(`data`);
id := select LAST_INSERT_ID();
new.insert (这里可以走异步插入,并不影响主业务性能)
insert into double_write_test_old(id,data) value(id,data);
若存在失败:
若old插入成功,new插入失败;(或者old插入执行完后,宕机);
此时old的数据较多,new少了一条; 对业务来说没有问题;
setp 3
结束:
如果出现错误或者宕机等情况,重试;
多校验几次后,没有错误出现且满足脚本执行结束条件,就可以结束;
更新服务单写;
存在新增和修改数据
setp 1
开启后台脚本
从旧数据开始往前追;
insert into double_write_test_new (`id`,`data`)
values(result.id, result.data, result.updated_at)
on duplicate key update data = result.data and updated_at < result.updated_at;
此处稍有区别,需要当前的时间大于已存在的才能修改值,防止脚本将新的修改数据覆盖;
时间精度是秒,可以改为更高精度的或者添加版本号处理该问题;
脚本停止条件:
time = 开启双写的那一刻+一段时间(大概估算一个,防止漏数据);
满足 updated_at < time的新旧库数据是一致的;
select * from double_write_test_old where updated_at < time;
select * from double_write_test_new where updated_at < time;
证明在新库数据已经追上了旧库数据;
updated_at 添加索引,并分段;
step 2
在线双写
新增的数据:
同上;
修改的数据:
1 old.update
update double_write_test_old set data = 2 where id = xx;
result := select * from double_write_test_old where id = 1;
2 new.updateOrInsert(这里可以走异步,并不影响主业务性能)
insert into double_write_test_new (`id`,`data`)
values(result.id, result.data, result.updated_at)
on duplicate key update data = result.data updated_at = result.updated_at;
这里都使用old库的时间;
step 3
结束:
如果出现错误或者宕机等情况,重试;
多校验几次后,没有错误出现且满足脚本执行结束条件,就可以结束;
上述过程为了加速,添加了索引,需要在事前事后做表结构修改
使用pt-online-schema-change,这里不做过多说明,其核心思想和上述方案相同;