分库分表&百亿级数据迁移
一 前言
二 背景
三 面临挑战
-
从1到N在系统架构上差异较大:原来的是单库单表的系统,之前的设计开发都是基于单库单表的,例如SQL查询,在单表上建了多个索引来支持这些查询业务,切换到分库分表之后,不带分表键的查询就不能支持了。
-
业务不可暂停:整个迁移过程,营销业务不允许暂停,类似于开着飞机换引擎,比一些银行系统的暂停服务来做迁移的难度大。
-
数据量大:单表数据量超级大,对数据同步链路的稳定性提出了很大的挑战。
-
系统架构旧:积分系统建立时间很早,所选用的技术框架老旧,有些甚至已经不再维护了,导致整个改造成本加大,改动过程风险很大。
-
接口版本多:由于历史原因,积分的发放和消耗接口版本非常多,有些历史包袱在里面,使得迁移的难度和风险都很大。
-
可监控、可灰度、可回滚:为了降低整个项目的风险,按照阿里稳定性三板斧,要求整个迁移过程可监控、可灰度、可回滚。这三个要求对服务重构没有大问题,但是对于这个同时涉及数据迁移的项目,难度反而是加大了,如果不要求可灰度、可回滚,只用做数据的从老的单库单表到新的分库分表的单向迁移就可以了,而如果要求可灰度、可回滚,则必须要求数据的双向同步,加大了数据同步链路的风险。
-
时间紧迫:需要在特定时间前完成拆库和数据源迁移(近1个多月的时间),封网期间加大了操作流程的复杂性。
四 技术方案
1 系统现状
-
考虑数据库性能,决定将积分系统由单库多表升级为分库分表(本次采取)
-
对数据库操作进行优化,减少加积分数据库操作
-
对业务优先级进行排序,部分场景降级,不写积分明细
2 技术细节点
数据库
-
确认源db和目标db是否都是mysql
-
由于是积分总表和明细表,所以从用户维度进行分表,此次分库分表是分8个库,1024张表
数据
-
数据迁移:分库分表肯定要涉及数据迁移,数据迁移必定涉及一个全量和增量的数据,如何保证数据不会重复,不会丢失,我们此次迁移数据是采用全量+增量任务进行同步数据,精卫(ps:阿里数据迁移工具,下文会多次出现该词)会在全量任务的时候相当于记录数据库的一个副本,然后增量的时候可以进行任务回溯,同时该迁移工具可以保证数据的唯一性(对于已经存在的记录可以进行更新操作)
程序
-
程序层面,因为涉及数据源切换,到时候肯定不能通过发布程序来切换数据源,所以预先在程序里面应该加载两个db的数据源,通过分布式配置预先设定开关,然后动态进行读写切换,所以我们需要在程序里面手动去指定双数据源,并且需要保证灰度过程中切换比例可以通过配置进行调控
-
在迁移数据的过程中特别要注意的是主键ID,在上面双写的方案中也提到过主键ID需要双写的时候手动的去指定,防止ID生成顺序错误
3 数据迁移&双写方案
-
线上库配置完成
-
精卫(阿里集团数据迁移工具)上新建2张表的全量任务
-
全量迁移完成后开启增量(自动回溯全量开始时间,消息多次消费会进行幂等) -
全量数据校验,查看数据是否一致 -
改造代码预发测试(采集线上流量进行回放,多种case跑一下,切流开关等校验),没问题发布上线 -
再次全量进行校验&订正(数据追平) -
打开双写(保证数据实时性) -
低流量节点(凌晨过后)进行灰度切流userId%x,进行验证,逐步流量打开,持续观察 -
双写开关切到新库,保证只写新库,完成数据迁移方案 -
系统稳定运行一段时间,迁移&双写代码下线,老库进行资源释放
准备工作
-
申请db资源 8个库 -
创建逻辑库,配置逻辑表
-
配置逻辑表路由算法 -
应用中配置好分库分表规则
-
多数据源配置(可以参考springBoot多数据源配置)
-
从监控找出业务低峰期,预估操作时段,由图我们可以看出业务低峰时间在2:00-5:00
切流代码编写(查)
-
对所有查询接口进行整理
-
对DAO层编写代理层xxProxyDAO.class
-
对读接口在代理层进行开关控制
-
根据userId后4位取模进行灰度,动态获取查询时用的数据源
-
注意:对于没有路由字段userId,需要进行代码改造
双写代码编写(增,删,改)
-
对写新库操作需要进行日志埋点
-
新库不要求一定写成功(不影响服务,后期不一致数据通过增量任务兜底)
-
如果老库和新库都写,最终返回结果是老数据源
-
数据源回滚:开启了双写,新数据库中总积分值不对(新数据源回滚不了)
-
开启双写时机:由于已经开启增量,所以对于还没切流前不需要开启双写。在准备进行切库时,开启双写。为什么这里还需要开启双写?
-
考虑极限情况下,增量同步任务会出现延迟(理论上是秒级)
-
实时同步数据到新数据库中
数据迁移
1)开启全量同步
-
本次全量使用的是精卫(ps:阿里数据同步工具,为了方便,外部也有很多类似的中间件,或者自己编写迁移脚本),它可以根据配置好的分库分表规则,自动将数据同步到相应的物理分表中
-
全量开始时间,选择gmt_modified作为条件字段
-
全量默认走备库,目标端写入的是主库,无论是全量还是校验都会对源端备库,目标端主库造成压力,所以这里需要注意一下,设置一个读的上线qps,以免对线上服务造成影响。
-
在迁移数据过程中,当写入发生冲突时,转换为update执行
-
注意一下数据迁移完成的时间,假设我们以80亿数据上,同时开启8个任务,每个任务上线tps为1w,计算公式如下:
-
数据迁移看板,实时查看迁移进度(ps:本工具是阿里内部的,外部也有很多类似开源系统)
2)开启增量同步
-
在INSERT时,出现主键冲突,精卫会将INSERT改成UPDATE事件。用户无需担心主键冲突产生异常。
-
异常处理机制:增量任务产生异常后,迁移工具默认会在同一机器上重试三次,若三次都失败后,会给出报警信息,并稍后换一台机器继续重试。
-
位点:消费位点是指当前已经成功消费的Binlog队列的位置,位点是一个 'yyyy-MM-dd HH:mm:ss' 格式的时间戳。
全量校验服务
-
全量校验服务和全量迁移服务类似,配置流程同上。校验服务执行完成,会在页面展示缺失和差异的数量。
-
验证源端和目标端的数据是否一致,也是全量订正服务必须的前置操作。
全量订正服务
-
通过订正服务可以将不一致的源端DB和目标端DB进行数据订正,保证一致性。
-
使用订正服务前必须进行校验服务。
进行校验
进行切流
-
当我们数据校验基本没有报错了之后,说明我们的迁移程序是比较稳定的了,那么我们就可以直接使用我们新的数据了吗?当然是不可以的,如果我们一把切换了,顺利的话当然是很好的,如果出现问题了,那么就会影响所有的用户。所以我们接下来就需要进行灰度,也就是切流。
-
本次切流方案是基于用户id取模的方式去进行切流,这个切流需要制定好一个切流计划,在什么时间段,放出多少的流量,并且切流的时候一定要选择流量比较少的时候进行切流,每一次切流都需要对日志做详细的观察,出现问题尽早修复,流量的一个放出过程是一个由慢到快的过程,比如最开始是以1%的量去不断叠加的,到后面的时候我们直接以10%,20%的量去快速放量。因为如果出现问题的话往往在小流量的时候就会发现,如果小流量没有问题那么后续就可以快速放量。
完成迁移
-
直到切流到100%,开启精卫新库到老库同步任务(以防万一出问题还能切回老库)。然后观察各个业务后续工单反馈情况和各个系统预警&日志;对新库进行性能压测,确保新库的稳定性
-
最后简单来总结下这个套路,其实就是四个步骤,一个注意:存量,增量,校验,切流
五 总结&反思
-
敬畏生产环境,敬畏线上数据,操作数据迁移需要核对仔细校验,迁移数据库表结构以线上数据为准,切勿以日常数据库表结构为准。在迁移前尽可能进行演练,通过一些测试编写的自动化脚本能否高效发现一些潜在的问题。
-
同时存储是有状态的,迁移难度比较大,开发者需要具备前瞻性,尽量在选型的时候慎重,选择合适的数据库,避免进行数据库迁移。发现数据库选型有潜在的问题时,需要当机立断,尽早迁移。不要以为出现问题的概率不大,就拖延了。否则一旦出现问题,就是重大故障,造成的损失难以估量。
-
在业务高速发展的同时更应该注重系统的稳定性建设,在对积分系统拆库完成之后在也不用担心积分系统在大促期间性能瓶颈问题了,只有将系统稳定性筑基好,业务才能跑的更健康更快。但是拆库仅仅是使系统变健壮的某一小步,后续系统稳定性建设可能还有更长的路要走。以此为鉴,砥砺前行。
-
没有过程的结果叫垃圾,没有结果的过程叫放屁,有意义的目标,并且拿到结果,个人认为这才是一个人、一个团队之所以努力工作的根本原因。整体上切库任务重,时间紧,风险高,在与时间赛跑的过程中,虽然难题层出不穷,路途磕磕绊绊,但最终菜鸟积分系统分库分表迁移项目顺利完成,为后续大促服务提供健壮的技术支持。
六 参考资料
资料获取
技术琐话
以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。