数据迁移场景
从库
在主库上建一个从库,数据同步完成后,将从库升级成主库(新库),再将流量切到新库(需要短暂停机);
适合数据结构不变,而且空闲时间段流量很低;
- 新建从库(新数据库),开始主从同步;
- 数据同步完成后,为了保证主从数据库数据一致,需要先停掉服务,然后再把从库升级为主库,将流量分发过去;最后启动服务,整个迁移过程完成。
- 优点:迁移成本低,迁移周期短
- 缺点:切换数据库时需要停机;
双写
在线迁移:也可能发生数据不一致;
时间戳:T;
注意使用upsert语意,否则双写和同步旧数据这两个执行,可能发生insert重复;
- 阶段一:业务正常读写源表,使用源表的 T 以前的数据,初始化目标表(或者说导入 T 时刻前历史数据,完成后可以考虑进行一次全量校验和修复,在双写前尽量减少数据不一致的可能性);
- 阶段二:T时刻开启双写,源表和目标表,源表为准(先写),读写源表,考虑mq异步写入目标表,注意时间戳的判断(即新数据不能被旧数据覆盖);
- 阶段三:双写,源表和目标表,目标表为准(先写),读写目标表;(数据不一致时,还可以回滚到二阶段,因为源表依然有所有数据,包括新数据)
- 阶段四:仅读写目标表,源表可归档数据到大数据平台后删除;
双写的实现方法: 可以提供一种 dao接口 的实现,双写两库,其中如果发生错误,如超时或者写入失败,都不太好立刻处理,需要全量校验去修复;
注:Hook做不了,因为这算是强绑定了实现(hook 会绑定上 db 实例),即无法动态转换,如阶段 2 到 3 调换参考角色;
全量检验和修复
-
可以使用 []byte 接受数据,直接进行比较(因为比较的是双方,即使类型转 go 可能有偏差,但负负得正);
-
使用泛型写一个通用的查询方法,需实现具体的比较方法(前提是拿到表的定义);发现数据不一致时上报 kafka;
校验需要考虑A->B时,1、B的数据是否有缺失或者和A对不上;2、同步后,A硬删除数据,B对应的数据就是多余的了;
补充
异构数据迁移:原表和新表的表结构不一致(需要注意类型转换和完整性校验);
注:任何后台运行的负载任务,都可以优化为:遇到高负载时就挂起,低负载继续执行; 个人认为记录utime,可以避免大部分的错误覆盖;
可利用泛型校验数据
离线迁移:导入导出慢,如果发生错误,一般就需要回滚了,因为剩下时间也不够了(预演有用,但也不能杜绝错误);
导出工具
mysqldump
; -h
指定mysql地址; --port
指定端口; table > xxx.sql
导出到sql; source
导入命令
水平分离存储场景
数据库 + MongoDB
MongoDB 是一个高可用、分布式的文档数据库,用于大容量数据存储,以类似 json 的格式存储,文档型的;
场景要求:核心数据写入数据库,非核心等json数据写入 MongoDB,数据库会存一个 mongo id,指向 MongoDB 的文档;
用户读请求 -> 查数据库 -> 查 MongoDB;
方案: 首先需求明确一个最重要的前提:用户读数据时,非核心数据的查询依赖于数据库记录的 mongo id,即如果数据库写入失败,此时即使 MongoDB 的非核心数据写入成功了,用户也不会读到 这部分的非核心数据,不会出现数据不一致的情况,所以可以确定写入顺序:先写 MongoDB 再写数据库;
但是明显反过来就不行了,所以核心可以分几种有效情况讨论:
1、MongoDB 写入成功,数据库写入成功; 2、MongoDB 写入成功,数据库写入失败,需要清除掉 MongoDB 内的垃圾数据,且需要注意用户修改操作,明显不能覆盖修改 MongoDB 数据; 3、MongoDB 写入失败,数据库写入失败,记录错误;
总体流程: 业务:用户修改数据请求 -> 非核心数据(文档)写入 MongoDB -> 核心数据(新增 mongo id)修改数据库 -> 发送mq消息删除 旧 mongo id;
mq broker;
mq consumer:删除 旧 Mongo id,包含重试逻辑;
补充:如果重试都失败,可以将消息保存到 死信队列里,有一个监控死信队列的程序,发现新数据到来后会发送告警;
垃圾数据(即数据库写入失败 MongoDB成功)的清除:
定时任务查增量数据;(最新时间 + 最大移除次数(防止压力过大))