数据迁移场景

从库

  在主库上建一个从库,数据同步完成后,将从库升级成主库(新库),再将流量切到新库(需要短暂停机);

  适合数据结构不变,而且空闲时间段流量很低;

  1. 新建从库(新数据库),开始主从同步;
  2. 数据同步完成后,为了保证主从数据库数据一致,需要先停掉服务,然后再把从库升级为主库,将流量分发过去;最后启动服务,整个迁移过程完成。
  • 优点:迁移成本低,迁移周期短
  • 缺点:切换数据库时需要停机;

双写

在线迁移:也可能发生数据不一致;

时间戳:T;

注意使用upsert语意,否则双写和同步旧数据这两个执行,可能发生insert重复;

  • 阶段一:业务正常读写源表,使用源表的 T 以前的数据,初始化目标表(或者说导入 T 时刻前历史数据,完成后可以考虑进行一次全量校验和修复,在双写前尽量减少数据不一致的可能性);
  • 阶段二:T时刻开启双写,源表和目标表,源表为准(先写),读写源表,考虑mq异步写入目标表,注意时间戳的判断(即新数据不能被旧数据覆盖);
  • 阶段三:双写,源表和目标表,目标表为准(先写),读写目标表;(数据不一致时,还可以回滚到二阶段,因为源表依然有所有数据,包括新数据)
  • 阶段四:仅读写目标表,源表可归档数据到大数据平台后删除;

双写的实现方法:   可以提供一种 dao接口 的实现,双写两库,其中如果发生错误,如超时或者写入失败,都不太好立刻处理,需要全量校验去修复;

注:Hook做不了,因为这算是强绑定了实现(hook 会绑定上 db 实例),即无法动态转换,如阶段 2 到 3 调换参考角色;

全量检验和修复
  1. 可以使用 []byte 接受数据,直接进行比较(因为比较的是双方,即使类型转 go 可能有偏差,但负负得正);

  2. 使用泛型写一个通用的查询方法,需实现具体的比较方法(前提是拿到表的定义);发现数据不一致时上报 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成功)的清除:

  定时任务查增量数据;(最新时间 + 最大移除次数(防止压力过大))