背景
在系统的快速迭代过程中, 业务系统往往部署在同一个物理库, 没有做核心数据和非核心数据的物理隔离. 随着数据量的扩大这种情况会带来稳定性的风险, 如库的慢 sql, 磁盘, IO 等等都会相互整体影响, 从而影响核心系统的业务稳定性, 因此需要将核心业务的业务表从原有库里抽取出来, 单独到新库里. 而核心数据的迁移, 涉及到的一个关键难点: 如何平稳及用户无感知的迁移数据, 本文将结合闲鱼商品库迁移实践, 向大家展示如何解决这个难题的.
闲鱼商品数据现状
闲鱼商品数据量 XX 亿级别以上, 采用分表分库和读写分离的 MySQL 数据库集群来支撑线上查询服务, 如下图, 通过 TDDL[1]数据库中间件进行高效统一管理. 可能有些同学会对分表分库相关概念不了解, 这里先简单做些介绍.
01 分表分库原理
本质是数据库的水平拆分问题, 把一个数据库切分成多个部分放到不同的数据库 (server) 上, 从而缓解单一数据库的性能问题, 下图描述分表分库的核心原理:
当然分表分库也有负面影响, 就是表结构变更及相关管理相比单表麻烦, 有一定风险, 具体如何决择, 还是要根据实际情况来分析.
02 分表分库下全局 Sequence 生成
分表分库解决在线服务容量和性能问题, 但是也带来使用上的复杂度提升. 灵活的配置路由规则和路由数据并提供简单易用的封装都是要考虑的, 以便业务对此无感知. 阿里开源中间件产品 TDDL 提供了解决方案, 对应阿里云上产品为: DRDS[2].
TDDL 关键原理不多做介绍, 但是在数据库迁移过程中主键冲突风险是故障重要风险点, 这里简要介绍下 TDDL 的全局唯一主键生成原理.
如上图, TDDL Sequence 是基于数据库更新 + 内存分配: 每次操作批量分配 id, 分配 id 的数量就是 sequence 的内步长, 而原有 id 值就加上外部长值, 后续的分配直接就在内存里拿, 这样的优势: 简单高效 缺点: 无法保证自增顺序.
另外数据迁移过程中, 在新库中, 为了保证跟原数据库主键非冲突, 需要设置一个跃迁比较大的主键, 防止出现两个库中的主键冲突, 这是后续迁移中要注意的关键点之一.
数据迁移方案
通过前文的简单介绍, 大家对闲鱼商品库现状有了初步了解, 下面将给大家介绍一下闲鱼是如何做到稳定迁移商品库的.
01 核心思路
数据迁移核心思路抽象起来其实很简单, 即如何稳定平滑迁移数据, 如下图所示:
但围绕这个过程细化下去, 我们会遇到不少问题, 如:
1, 数据我们该如何迁移, 是一次性? 还是分阶段?
2, 如何校验数据迁移过程的正确性?
3, 我们业务改造有问题怎么办? 如何尽早发现? 如何回滚?
4, 我们的新库性能如何?
带着这些问题, 我们进一下细化梳理迁移方案.
02 实现方案
如上图所示, 整个方案分为几个部份:
1, 系统改造, 包括 SQL 改造, 双写开关, 新库 sequence 创建.
SQL 改造: 加载两套 TDDL 数据源, 一套是给老库的, 一套是给新库的, 并且生成两套 mybatis sql 模板.
双写开关: 设置好写新库, 写老库的开关, 用于线上迁移过程中双写过程及遇到问题及时回滚.
sequence 创建: 迁移 sequence 表时, 需要抬升新库的 sequence 表中的值, 用于防止主键冲突, 并且需要按照主键消耗量评估一个安全值, 这是非常重要的一个细节, 再次强调一下.
2, 稳定性保障, 迁库是大事, 改造过程中, 稳定性重中之重, 主要有系统压测, 线上流量回放, 故障演练.
系统压测: 主要针对新库进行性能测, 防止新库有意外情况.
线上流量回放: Edsger W. Dijkstra 说过如果调试程序是一种标准的可以铲除 BUG 的流程, 那么, 编程就是把他们放进来的流程. 通过引入线上数据在测试环境回放, 可以尽可能的发现问题, 保证改造后的稳定性.
故障演练: 通过注入一些人为故障, 如写新库失败, 新库逻辑有问题, 及时的演练回滚策略.
3, 数据迁移, 主要利用阿里云数据传输服务 DTS[3]的数据迁移能力, 涉及到全量迁移, 增量迁移, 一致性校验及反向任务.
全量迁移: 数据迁移首要目标如何将历史全量数据迁移到新库中, 我们的做法是指定一个时间点, 再根据这个时间点查找每张源表的最大及最小 id, 然后分别批量导到目标库中, 如图:
* 整个过程都是查询在线库的备库, 因此不影响在线业务的数据库服务.
增量迁移: 由于迁移过程中业务服务一直运行, 因此全量迁移完全成, 并且要将全量时间点后的数据追回来, 这里核心原理是同步全量时间位点后 binlog 日志数据来保证数据一致性, 需要注意的是增量时间需要前移一小断时间(如 5 分钟), 其主要原因是全量迁移启动的那刻会有时间差, 需要增量前移来保证数据最终一致性, 如下图:
一致性校验: 通过全量及增量的迁移后, 此时源库跟目标的数据理论上是一致的, 但实际上应用在经过功能测试, 线上流量回放等阶段, 数据在这个过程中有可能会现不一致的情况, 因此正式上线前, 需要做数据一致性校验, 其原理是分批查询源表(跟全量迁移的查询方式类似), 再跟目标库进行比对, 如图所示:
反向任务: 迁移到新库后, 会有一线离线业务对老库还有依赖, 需要建立从新库到老库的回流任务, 原理跟增量迁移一样, 只是变更一下原库及目标库.
03 迁库流程
到这里大家应该对迁库所涉及到点比较清楚了, 但还有一个非常重要的事, 即梳理整个迁库步骤非常关键, 通常会有两种方案.
方案一:
1,DTS 数据追平, 即全量同步完成, 开启增量同步, 并且延迟在秒级以内.
2, 上线前校验, 主要有线上流量回放, 压测, 一致性校验, 故障演练.
3, 线上开双写, 线上同时写新库及老库, 这时需要关闭增量同步任务, 防止无效覆盖.
4, 线上校验, 执行预先准备的测试脚本并结合一致性校验工具, 同时将读流量慢慢切到新库, 验证双写逻辑.
5, 切换数据源, 关闭双写并正式写入新库.
6, 创建反向任务, 数据回流老库.
方案二:
1,DTS 数据追平, 即全量同步完成, 开启增量同步, 并且延迟在秒级以内.
2, 上线前校验, 主要有线上流量回放, 压测, 一致性校验, 故障演练.
3, 线上切开关, 写新库, 同时需要关闭增量同步任务, 防止无效覆盖.
4, 创建反向任务, 数据回流老库.
方案优缺点对比:
总结起来方案 1 迁移流程相对复杂, 对迁移的控制力度更细, 适合业务复杂, 底层改造比较多, 想精细化控制迁移步骤的场景, 方案 2 迁移相对简单, 过程快速, 适合业务流程相对简单, 可控, 想快速切换的场景, 具体选选择哪个方案, 同学们可以根据自身的业务情况做选择.
这里考虑到闲鱼商品业务复杂, 底层改造较多, 从稳定性的角度考虑, 最终选择方案 1.
方案 1, 最关键的是 3,4,5 步骤, 因此需要预先做好回滚计划.
04 回滚方案
回滚方案总原则是不丢数据. 最有可能的发生点是双写期间新库出问题, 导致线上服务异常, 这时只要立即关闭写新库即可, 另外就是切到新库后, 新库出问题了(如性能问题), 可以立即切回到老库, 并通过反向任务, 保持数据一致性, 最后若没启用分布式事务, 双写的时间越短越好, 有可能会有数据不一致情况.
小结
通过周密的迁移方案设计, 以及 DTS 强大的数据迁移工具的能力, 闲鱼商品库顺利完成 XX 亿在线数据库服务迁移, 独立的物理部署显著提升商品库在线服务的稳定性. 然而不同业务系统的数据库情况可能会有差异, 如单库向多库迁移, 单表向多表迁移等, 不过整体方案大致类似, 希望本文迁库实践方案能给大家提供一个可行的参考.
来源: https://yq.aliyun.com/articles/693652