vlambda博客
学习文章列表

分库分表&百亿级数据迁移


一  前言


拆库&数据迁移说白了,考验的不是一个人的技术功底,而是一个人干活的细致程度,以及抗压能力。无论在哪个公司,数据库迁移的机会都不会太多,因此,我也是非常珍惜这次历练,用阿里的一句老话来说就是 “因人成事,借事修人”。写这篇文章的目的主要是自己进行一个总结,也希望能给需要的同学们一些参考。

二  背景


在星爷的《大话西游》中有一句非常出名的台词:“曾经有一份真挚的感情摆在我的面前我没有珍惜,等我失去的时候才追悔莫及,人间最痛苦的事莫过于此,如果上天能给我一次再来一次的机会,我会对哪个女孩说三个字:我爱你,如果非要在这份爱上加一个期限,我希望是一万年!”在我们开发人员的眼中,这个感情就和我们数据库中的数据一样,我们多希望他一万年都不改变,但是往往事与愿违,随着公司的不断发展,业务的不断变更,我们对数据的要求也在不断的变化。在早期为了快速尝试新业务,菜鸟积分系统在发展初期是单体系统,数据库是单库多表。随着菜鸟C端业务快速增长,服务消费者已经过3亿+,对于消费者阵地也从查、取、寄开始慢慢扩展到玩、购。主要是裹酱商城以及各个业务线多种多样的活动,这些活动包含了停留、任务、开奖、分享、签到、助力、兑换、抽奖等多种多样的互动手段。权益在其中起到了钩子的作用,引导流量进入互动业务,互动产品本身提供了某些“价值”,使得消费者在平台停留时间增加,“价值”承载的实体之一就是虚拟权益积分。菜鸟积分系统一直扮演着一个底层核心角色,承载用户核心资产。在大促期间需要支持大流量的活动,因此整个积分系统在大促期间面临挑战是非常大的。

分库分表&百亿级数据迁移

为了支持菜鸟C端营销业务的持续爆炸式增长,我们启动了菜鸟积分系统升级工作,如何在业务不暂停的情况下,完成积分从单库单表到分库分表的数据架构升级,背后实施步骤以及遇到过哪些意想不到的坑,本文将分享这一高风险操作是如何逐步分阶段完成的。

分库分表&百亿级数据迁移


三  面临挑战


在开始设计迁移方案之前,我们首先需要调研系统现状,通过调研系统现状之后,我们发现了本项目面临以下几个主要的挑战:

  • 从1到N在系统架构上差异较大:原来的是单库单表的系统,之前的设计开发都是基于单库单表的,例如SQL查询,在单表上建了多个索引来支持这些查询业务,切换到分库分表之后,不带分表键的查询就不能支持了。

  • 业务不可暂停:整个迁移过程,营销业务不允许暂停,类似于开着飞机换引擎,比一些银行系统的暂停服务来做迁移的难度大。

  • 数据量大:单表数据量超级大,对数据同步链路的稳定性提出了很大的挑战。

  • 系统架构旧:积分系统建立时间很早,所选用的技术框架老旧,有些甚至已经不再维护了,导致整个改造成本加大,改动过程风险很大。

  • 接口版本多:由于历史原因,积分的发放和消耗接口版本非常多,有些历史包袱在里面,使得迁移的难度和风险都很大。

  • 可监控、可灰度、可回滚:为了降低整个项目的风险,按照阿里稳定性三板斧,要求整个迁移过程可监控、可灰度、可回滚。这三个要求对服务重构没有大问题,但是对于这个同时涉及数据迁移的项目,难度反而是加大了,如果不要求可灰度、可回滚,只用做数据的从老的单库单表到新的分库分表的单向迁移就可以了,而如果要求可灰度、可回滚,则必须要求数据的双向同步,加大了数据同步链路的风险。

  • 时间紧迫:需要在特定时间前完成拆库和数据源迁移(近1个多月的时间),封网期间加大了操作流程的复杂性。

备注:涉及到数据的重构项目风险极大,没处理好可能就要背包走人了,如果数据一旦错乱或者丢失,有可能会造成部分数据不可恢复,或者即使能恢复(不同于单纯的服务重构),恢复时间通常也很长,按小时,甚至按天计算。业务很难承受这个代价。在期间师兄送了我四字方针:胆大心细。既然风险这么大,那我们能不干这个分库分表迁移项目吗?不能!因为菜鸟积分系统在2020年双十一期压测期间,已经成为瓶颈了,已经到了不得不迁移重构的地步,而且这个重构越晚做风险越大。这个时候就适合阿里的一条价值观上场了:“If not now, when? If not me, who?” So, let's do it.


四  技术方案


1  系统现状


积分系统主要2张核心的表,积分总表和积分明细表,目前2个库中的数据达百亿,积分明细日新增数据也在千万级。积分系统对外提供的服务接口主要有读积分、加积分、扣减(冻结积分)、退还积分。系统目前读积分会通过缓存tair做查询,击穿缓存才会到达数据库,写积分会直接更新数据库(缓存是为了临时解决大促期间系统性能瓶颈问题)。我们可以看到大促大瓶颈是在写积分对数据库的压力上,我们还有很多秒杀场景活动,由于对数据库叠加峰值太高,如果把数据库升级最高配置,一方面预算上花费太高,另一方面也不一定能支撑预估流量,即使这次侥幸度过,将来也无法再做水平扩展。基于这个考虑,重新设计了三个方案来解决:

  1. 考虑数据库性能,决定将积分系统由单库多表升级为分库分表(本次采取) 

  2. 对数据库操作进行优化,减少加积分数据库操作 

  3. 对业务优先级进行排序,部分场景降级,不写积分明细


2  技术细节点


数据库


  • 确认源db和目标db是否都是mysql

  • 由于是积分总表和明细表,所以从用户维度进行分表,此次分库分表是分8个库,1024张表


数据


  • 数据迁移:分库分表肯定要涉及数据迁移,数据迁移必定涉及一个全量和增量的数据,如何保证数据不会重复,不会丢失,我们此次迁移数据是采用全量+增量任务进行同步数据,精卫(ps:阿里数据迁移工具,下文会多次出现该词)会在全量任务的时候相当于记录数据库的一个副本,然后增量的时候可以进行任务回溯,同时该迁移工具可以保证数据的唯一性(对于已经存在的记录可以进行更新操作)

程序


  • 程序层面,因为涉及数据源切换,到时候肯定不能通过发布程序来切换数据源,所以预先在程序里面应该加载两个db的数据源,通过分布式配置预先设定开关,然后动态进行读写切换,所以我们需要在程序里面手动去指定双数据源,并且需要保证灰度过程中切换比例可以通过配置进行调控

  • 在迁移数据的过程中特别要注意的是主键ID,在上面双写的方案中也提到过主键ID需要双写的时候手动的去指定,防止ID生成顺序错误


3  数据迁移&双写方案


分库分表&百亿级数据迁移


分库分表&百亿级数据迁移


简要描述一下整个流程:

  1. 线上库配置完成

  2. 精卫(阿里集团数据迁移工具)上新建2张表的全量任务

  3. 全量迁移完成后开启增量(自动回溯全量开始时间,消息多次消费会进行幂等)


  4. 全量数据校验,查看数据是否一致


  5. 改造代码预发测试(采集线上流量进行回放,多种case跑一下,切流开关等校验),没问题发布上线


  6. 再次全量进行校验&订正(数据追平)


  7. 打开双写(保证数据实时性)


  8. 低流量节点(凌晨过后)进行灰度切流userId%x,进行验证,逐步流量打开,持续观察


  9. 双写开关切到新库,保证只写新库,完成数据迁移方案


  10. 系统稳定运行一段时间,迁移&双写代码下线,老库进行资源释放


准备工作


1)配置工作

  • 申请db资源 8个库
  • 创建逻辑库,配置逻辑表
  • 配置逻辑表路由算法 
  • 应用中配置好分库分表规则
  • 多数据源配置(可以参考springBoot多数据源配置)

2)通过监控查看业务低峰时段

  • 从监控找出业务低峰期,预估操作时段,由图我们可以看出业务低峰时间在2:00-5:00

分库分表&百亿级数据迁移


分库分表&百亿级数据迁移



切流代码编写(查)


  • 对所有查询接口进行整理

  • 对DAO层编写代理层xxProxyDAO.class

  • 对读接口在代理层进行开关控制

  • 根据userId后4位取模进行灰度,动态获取查询时用的数据源
  • 注意:对于没有路由字段userId,需要进行代码改造


双写代码编写(增,删,改)


分库分表&百亿级数据迁移


整体上分为下面4个步骤,通过配置动态进行切换,切换期间需要注意的问题如下:

  • 对写新库操作需要进行日志埋点

  • 新库不要求一定写成功(不影响服务,后期不一致数据通过增量任务兜底)

  • 如果老库和新库都写,最终返回结果是老数据源

  • 数据源回滚:开启了双写,新数据库中总积分值不对(新数据源回滚不了)

  • 开启双写时机:由于已经开启增量,所以对于还没切流前不需要开启双写。在准备进行切库时,开启双写。为什么这里还需要开启双写?

    • 考虑极限情况下,增量同步任务会出现延迟(理论上是秒级)

    • 实时同步数据到新数据库中


分库分表&百亿级数据迁移


数据迁移


数据迁移其实不是一蹴而就的,每一次数据迁移都需要一段漫长的时间,有可能是一周,有可能是1个月,通常来说我们迁移数据的过程基本如下图:

分库分表&百亿级数据迁移


首先我们需要将我们数据库已经存在的数据进行批量的迁移,然后需要处理新增的这部分数据,需要实时的把这部分数据在写完原本的数据库之后然后写到我们的新的存储,在这一过程中我们需要不断的进行数据校验。当我们校验基本问题不大的时候,然后进行切流操作,直到完全切流之后,我们就可以不用再进行数据校验和增量数据迁移。

1)开启全量同步


  • 本次全量使用的是精卫(ps:阿里数据同步工具,为了方便,外部也有很多类似的中间件,或者自己编写迁移脚本),它可以根据配置好的分库分表规则,自动将数据同步到相应的物理分表中

  • 全量开始时间,选择gmt_modified作为条件字段

  • 全量默认走备库,目标端写入的是主库,无论是全量还是校验都会对源端备库,目标端主库造成压力,所以这里需要注意一下,设置一个读的上线qps,以免对线上服务造成影响。

  • 在迁移数据过程中,当写入发生冲突时,转换为update执行

  • 注意一下数据迁移完成的时间,假设我们以80亿数据上,同时开启8个任务,每个任务上线tps为1w,计算公式如下:

分库分表&百亿级数据迁移


  • 数据迁移看板,实时查看迁移进度(ps:本工具是阿里内部的,外部也有很多类似开源系统)

分库分表&百亿级数据迁移


2)开启增量同步


  • 在INSERT时,出现主键冲突,精卫会将INSERT改成UPDATE事件。用户无需担心主键冲突产生异常。

  • 异常处理机制:增量任务产生异常后,迁移工具默认会在同一机器上重试三次,若三次都失败后,会给出报警信息,并稍后换一台机器继续重试。

  • 位点:消费位点是指当前已经成功消费的Binlog队列的位置,位点是一个 'yyyy-MM-dd HH:mm:ss' 格式的时间戳。


全量校验服务


下面使用的工具是阿里内部的,原理都类似,可以参考阿里对外开源的数据传输服务DTS。


  • 全量校验服务和全量迁移服务类似,配置流程同上。校验服务执行完成,会在页面展示缺失和差异的数量。

  • 验证源端和目标端的数据是否一致,也是全量订正服务必须的前置操作。

分库分表&百亿级数据迁移


分库分表&百亿级数据迁移


分库分表&百亿级数据迁移

全量订正服务


  • 通过订正服务可以将不一致的源端DB和目标端DB进行数据订正,保证一致性。

  • 使用订正服务前必须进行校验服务。

分库分表&百亿级数据迁移


分库分表&百亿级数据迁移


进行校验


需要注意:校验任务注意不要影响线上运行的服务,通常校验任务会写很多批查询的语句,会出现批量扫表的情况,如果代码没有写好很容易导致数据库挂掉。

对账标准:target数据库和source数据库中数据保持一致(所有字段)

对账梳理:可以从积分总表和积分明细两个方面来处理

对账流程:通过定时任务轮询执行已经完成迁移的用户在新老库的数据一致性。需要注意的是由于读取新老库有先后顺序,所以产生瞬时的数据不一致,对于这种问题可以采用对账重试,只要保证最终一致即可。

分库分表&百亿级数据迁移


分库分表&百亿级数据迁移


1)抽样数据校验

按业务类型或者用户id,对最新增量数据进行抽样校验(下面校验工具为阿里内部工具,外部可以参考阿里数据传输服务DTS),然后需要对有问题数据进行订正

分库分表&百亿级数据迁移


2)Odps离线数据校验

使用odps的小时表来进行对账。思路很简单,利用odps数据同步能力,离线数据的处理能力,加上动态脚本的编写快速实现多系统间对账。不需要进行应用的改造,稳定性上也有保障。对于实时性要求不高的场景,可以推荐尝试使用。

分库分表&百亿级数据迁移


进行切流


  • 当我们数据校验基本没有报错了之后,说明我们的迁移程序是比较稳定的了,那么我们就可以直接使用我们新的数据了吗?当然是不可以的,如果我们一把切换了,顺利的话当然是很好的,如果出现问题了,那么就会影响所有的用户。所以我们接下来就需要进行灰度,也就是切流。

  • 本次切流方案是基于用户id取模的方式去进行切流,这个切流需要制定好一个切流计划,在什么时间段,放出多少的流量,并且切流的时候一定要选择流量比较少的时候进行切流,每一次切流都需要对日志做详细的观察,出现问题尽早修复,流量的一个放出过程是一个由慢到快的过程,比如最开始是以1%的量去不断叠加的,到后面的时候我们直接以10%,20%的量去快速放量。因为如果出现问题的话往往在小流量的时候就会发现,如果小流量没有问题那么后续就可以快速放量。



切换过程采用逐步放量的形式,灰度方式很多我们采用的是先白名单验证,然后用户ID取模10000逐步放量的方式。灰度切流验证:万分之1-1%-5%-10%-50%-100%切流

完成迁移


  • 直到切流到100%,开启精卫新库到老库同步任务(以防万一出问题还能切回老库)。然后观察各个业务后续工单反馈情况和各个系统预警&日志;对新库进行性能压测,确保新库的稳定性

  • 最后简单来总结下这个套路,其实就是四个步骤,一个注意:存量,增量,校验,切流


五  总结&反思


本文介绍了菜鸟积分系统拆库&数据迁移整个过程,总结了基于菜鸟现业务下的数据迁移方案,同时介绍了迁移时遇到的问题以及解决方案,整个迁移过程从方案制定到最终的迁移完成持续约一个多月时间,最终完成迁移。无论是利用数据库工具还是利用服务对数据进行迁移,目标都是一致的那就是数据无差异,用户无感知,异常可监控,方案可回滚。下面是自己反思的几个点:

  • 敬畏生产环境,敬畏线上数据,操作数据迁移需要核对仔细校验,迁移数据库表结构以线上数据为准,切勿以日常数据库表结构为准。在迁移前尽可能进行演练,通过一些测试编写的自动化脚本能否高效发现一些潜在的问题。

  • 同时存储是有状态的,迁移难度比较大,开发者需要具备前瞻性,尽量在选型的时候慎重,选择合适的数据库,避免进行数据库迁移。发现数据库选型有潜在的问题时,需要当机立断,尽早迁移。不要以为出现问题的概率不大,就拖延了。否则一旦出现问题,就是重大故障,造成的损失难以估量。

  • 在业务高速发展的同时更应该注重系统的稳定性建设,在对积分系统拆库完成之后在也不用担心积分系统在大促期间性能瓶颈问题了,只有将系统稳定性筑基好,业务才能跑的更健康更快。但是拆库仅仅是使系统变健壮的某一小步,后续系统稳定性建设可能还有更长的路要走。以此为鉴,砥砺前行。

  • 没有过程的结果叫垃圾,没有结果的过程叫放屁,有意义的目标,并且拿到结果,个人认为这才是一个人、一个团队之所以努力工作的根本原因。整体上切库任务重,时间紧,风险高,在与时间赛跑的过程中,虽然难题层出不穷,路途磕磕绊绊,但最终菜鸟积分系统分库分表迁移项目顺利完成,为后续大促服务提供健壮的技术支持。

六  参考资料


[1] TDDL开源Github地址 
https://github.com/alibaba/tb_tddl

[2] 阿里愚公开源Github地址 
https://github.com/alibaba/yugong

[3] 阿里云DTS产品官网 
https://www.aliyun.com/product/dts




资料获取




技术琐话 



以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。