数据迁移指南
从几次数据迁移来看, 目的地都是新存储, 数据量级也从百万到百亿不等, 也算是积累了一点迁移的经验
数据迁移目标
- 在线无感迁移
数据迁移流程
前期数据调研
- 必须统计的数据信息, 数据的量级和同步链路都会影响使用数据导入的方式
- 数据量级
- 数据
- 数据目前存储的同步链路
代码改造
- 在业务代码中新增新存储的链路逻辑以及新老的切换开关
- 至少需要一个 faas, 用户增量数据同步以及差异补偿
原存储导出
- 如果数据量级非常小, 可以直接通过刷在线数据的方式实现
- Mysql: 在 mysql 创建全量工单 和 创建增量工单 , 全量刷数据
- 其他的情况 , 原存储都需要通过离线导数任务导出到离线 hive 表中 (无论是 mysql, kv 存储 还是新存储)
chestnut - 新存储
数据转化
- 不同的存储介质之间通常格式不同, 通常需要通过 hive 离线表进行操作, 将结构转换成新的存储 , 这部分通常如果是 mysql->mysql 用 hsql 实现, 其他平台通常提供批量转换的脚本或者教程 , 属于中间态, 部分也可能没有
新存储导入
- 这里也是通过对应平台提供的教程执行即可
- 如果数据量级比较小(百万级别) , 可以直接通过将 hive 导出到 mq, 直接消费 mq, 这样可以先搭建同步链路再进行数据迁移 , 而且多个机房只要有同步链路的情况下只需要迁移一次
目标存储的导入通常有多种方式
增量消费
- 数据导出之前(或者说在需要导入的数据生成之前必须开启增量消费 mq 的 hold, 否则会出现数据的缺失)
- 导入完成之后开启增量数据的消费, 追平时间线
- Hold 的时间点并不是和导出全量数据时间点能完全对应的, 因为 hive 表的产生时间是不确定的 , 如果 hive 中存储着每条记录的生成时间倒是可以通过导出或者转换时候筛选大于某个时间点的数据作为基准, 然后将 mq 定位到这个时间点, 但是不保险, 推荐做法是允许部分记录重放, 数据库内保持幂等性, 差异数据通过后续补偿消除
现在 mq 的保存时间最多是 48h, 还不可设置
数据DIFF
- 数据对比分为两个部分, 全量对比 和 随机对比
全量对比
- 将新的存储导出到 hive 和 旧存储的 hive 进行比较
- 全量对比这里会出现时间窗口边缘的问题, 没办法完全保证新的存储导出的时间和旧的存储导出的时间是相同的 , 即使限制了 update_at, 在两者时间差中间部分产生的数据差异是无法确定的, 核心是只能对比某个时间节点时候的数据, 而不是实时线上的数据
直接对比离线表
- 可以直接对比 hive 表, 但是似乎不能直接将差异补偿, 不够好用
离线 SQL
- 通过运行 hsql 对比 ,当出现复杂的条件过滤筛选时候推荐使用这个
- 速度非常快 , 双边百亿级的数据 1h 对比完毕, 结果可以直接存储到 hive 中进行 mq 补偿
在线 diff 工具
- 一个非常强大的对比 diff 工具, 直接支持补偿(补偿起来要编写插件比较复杂)
- 不过因为是扫描式线上的, 所以需要限制速率, 对比起来没有纯离线的 hsql 快
随机diff
- 原理是请求重放, 将线上流量请求放到测试环境的请求, 根据两者结果的差异判断数据是否一致
- 优势是对比的永远是实时线上的数据, 准确度最高, 缺点是属于随机抽样, 不是全量数据 , 无法较好得进行补偿
请求重放 diff
非常可惜的点是有些环境能力并不完整
- 简单上手的重放对比工具
数据补偿
- 如果是使用 hsql 的情况下通常直接将差异的 hive 导到 mq 中, 消费 mq 补齐线上数据
历史消息队列资源已经比较紧张了, mq 导入的话通常使用离线数据集成任务
- 这里可以使用 hold 住 mq 补偿完再消费, 但是这样的话如果遇到 diff 差距比较大的情况会出现消费不完的情况, hold 住的时间又是有限制的, 这种方式整体看不太好
- 可以不 hold住, 直接进行线上补齐, 因为数据补偿的目的是线上补齐, 因此每条不同的记录都拿出来同时 check 线上新的和旧的的情况, 进行对比后再进行补偿, 可以有效解决线上写需要 hold 的问题, 这种方式唯一可能的缺点就是如果补偿过程中正好更改了同一行数据可能导致错误的数据, 但是出现这种场景的概率非常非常低
- Faas 一定要留个 http 接口来直接进行数据对齐, 方便数据迁移时候产生线上问题快速止损
双写
- 当存量数据导入之后, 为了保持两个存储的一致性, 需要开启双写, 同时写新存储和旧存储 , 但是涉及到一个点, 从消费 binlog 切换到直接写数据库的时间节点如何对齐
- 通过配置中心设置统一的切流时间对齐 , 将开始操作切流写的时间戳设置好, faas 将从该时刻之后的数据不消费(这个时刻不能通过时间戳获取, 而是需要通过 记录中的 updatetime 获取, 因为 mq 到消费有时间差), 服务从这个时间戳开始将数据双写
- 这样有个问题是如果是数据删除, 无法确定已删数据的具体时间节点, 不能确定是在时间戳前还是之后,解决这个的办法就是软删, 在准备切流前两分钟开启软删, 切流完成后关闭
- 通过配置中心设置统一的切流时间对齐 , 将开始操作切流写的时间戳设置好, faas 将从该时刻之后的数据不消费(这个时刻不能通过时间戳获取, 而是需要通过 记录中的 updatetime 获取, 因为 mq 到消费有时间差), 服务从这个时间戳开始将数据双写
切流读
- 这部分实际上就是通过配置中心控制切流的比例, 每次只切换一小部分, 控制影响面, 避免大量用户出问题, 实际操作的时候通常放量顺序为 10%->50%->100%
- 切流读通常意味着数据迁移的彻底完成 , 后续双写的冗余代码可以删除
数据迁移链路搭建
- 在很多个数据中心的情况下需要处理同步链路和 binlog 重放出现的问题( binlog 自带了同步能力, 如果再构建同步相当于每个请求都会被重放两次), 如果是之前停止 binlog 会出现丢失
- 下图使用 mysql -> 新存储 举例
- 核心是在同步链路准备搭建时候关闭 mq, hold 不消费, 等到搭建完成之后开启一个 region 的消费, 可以避免重放
- 这里的关闭的行为可以通过配置中心控制, 时间上的精确性会更高, 如果是手动关闭 mq 两个之间的差距还是会重放,需要看业务的可接受程度
- 核心是在同步链路准备搭建时候关闭 mq, hold 不消费, 等到搭建完成之后开启一个 region 的消费, 可以避免重放
数据迁移Demo
- 某业务收藏数据迁移改造
备注
- 数据迁移是一个极其麻烦和非常漫长的过程, 看起来的清晰快速只存在于理论上, 实际上会遇到非常多乱七八糟的问题导致各种各样的 delay , 因为路径步骤强依赖 , 导致一个卡点就会直接导致排期 delay 很久
- 资源不足: 最频繁遇到的问题 , 不同 region 缺乏的资源还不一样, 很多时候只能找各种补丁的替代方案解决
- 导入失败: 导入连续失败, 排查也不好排查, 一卡卡很久
- 跨区域数据权限: 这种通常需要目标区域的同学协助迁移