从单表到分表实现数据平滑迁移

背景

初到新公司接手支付中心系统,发现支付表单表数据量达到了 7 亿多,震惊之余不由的脊背发凉,这个系统运行犹如走钢丝,稍微有点风吹草动就可能造成线上事故,DBA 天天催着要数据迁移,给出大表解决方案,给出排期。

当你担心一件事要出问题,他就一定会出问题,不得不说这墨菲定律真™️准。营销部门搞活动,支付单量激增,客服反应好多用户投诉明明支付成功了,但还是处于支付中状态。打开日志排查原因,就是因为数据库响应慢,支付状态未能更新成功,导致未能及时通知上游系统支付成功结果。(当然支付逻辑也存在问题,这点不在此文讨论范围内)

痛定思痛,分库分表和数据迁移迫在眉睫。

分库?分表?

什么时候需要分库

分库主要是解决并发量大的问题,数据库的连接数是有限的,当读或者写的QPS过高,导致数据库连接数不够时,就需要考虑分库,通过增加数据库实例的方式来增加连接数,从而提升系统的并发性能。

什么时候需要分表

分表主要时应对数据量大的情况,当一个表的数据量过大时,不论是读还是写的性能都会出现明显下降,一张表的数据量最好控制在1000万以内(看硬件性能)。

业务增量分析及容量规划

首先通过计算得到高峰期QPS每秒约50,并发量不算特别高,所以没有必要做分库来分担并发量。

在业务高峰期的月份每日的支付单量约270多万,业务低峰期的月份每日的支付单量约130多万,每年大概的单量为6亿单左右,

按照3到5年的规划,每张表约1000万数据,计划分256张表。

我们目前数据库配置是32C/64G/3T SSD,满足需求。

数据迁移

数据迁移分析及遇到的问题:

  • 使用业务单号的hash值对256取模(hash(业务唯一ID)%256),得到表的索引值。之前单表存在唯一的业务单号,直接使用即可。
  • 需要将旧库新增的实时数据同步到新库,并记录开始数据的ID。
  • 需要将存量数据同步到新库,终止点为1中开始数据的ID。
  • 考虑切流量过程中的稳定性和可回滚能力,需要将实时数据双向同步,即新库新增的数据也需要同步到旧库。
  • 迁移完成,需要做新旧库数据的比对工作。

整体迁移方案


下面按照迁移步骤来逐一讲解:

1. 增量数据同步

将实时新增的数据同步到新库的表中。这阶段利用的 Canal、RocketMQ 中间件实现,Canal 获取 MySQL BinLog 日志,发送到 RocketMQ 指定的 Topic,消费者对数据做处理后插入到新的库表。

增量数据同步问题

  1. 需要记录一下增量数据同步的开始 ID,这个 ID 作为存量数据的终止点。
  2. RocketMQ 消费消息要保证顺序性,需要指定消费模式为顺序消费 consumeMode = ConsumeMode.ORDERLY,顺序消费需要注意,如果消费消息异常,会导致消息阻塞。
  3. 在增量数据同步时,需要将 update 语句转为 insert 语句,因为我们先执行增量数据同步,在这个过程中可能会有对存量的数据进行更新的操作,如果不将 update 转为 insert 的话,在迁移存量数据的时候,迁移数据在存入新库前,发生了更新,更新先到的新库,这时会更新 0 条记录,因为存量数据还未插入到新库,就会发生更新丢失的情况。binlog 是全部字段发送的,经过解析可以 insert 全部的字段。

2. 存量数据同步

存量数据同步使用的是游标方式,从旧库中分批查询数据,经过业务单号hash取模后批量插入到新库,直到迁移到数据 ID 为增量开始的数据 ID 时结束。

3. 数据双向同步

为了保证迁移过程中的系统稳定性,在出现问题时,要有可回滚的能力,这就需要新库中的新增数据也要同步到旧库中。

那这就存在消息无限循环问题了,旧库中新增一条数据,Canal 接收到这条数据的 binlog 消息,将该数据写入到新库中,于是新库也产生一条 binlog 消息,Canal 接收到消息,将该数据写入到旧库中,....不断的循环 。

我们的方案是给数据染色,旧表需要新增一个标识字段来说明是旧表产生的数据,标识字段默认值为 0。同样新表也需要增加一个标识字段说明是新表产生的数据,标识字段默认值为 1。标识字段一定要指定默认值,这样业务代码就不需要改动了。

当旧库向新库同步时,消息过滤组件会判断标识字段是否为 0,如果为 0 则说明是旧库产生的数据,就同步到新库,否则就会丢弃消息。新库向旧库同步也是如此,只不过新库的标识字段为 1。这样就解决了数据双向同步产生的消息循环问题了。


数据校验

数据迁移完成,需要核对逐条逐个字段是否一致,如果不一致需要记录下数据和报警通知。

以旧库为基准,查询每条数据在新库中是否存在,并且每个字段是否一致。

以新库为基准,查询每条数据在旧库中是否存在,并且每个字段是否一致。

对于不一致的数据记录到 diff 表中,扫描 diff 表再次、三次做校验,如果还是不一致,就需要手动校验了。再次校验的目的是可能新旧库同步存在延迟。

灰度流量切换

流量灰度方案及数据源切换方案:

  1. 新增新库分表代码,分表使用的是 ShardingSphere 中间件,访问旧库和新库可以根据配置来切换。
  2. 通过配置中心配置切量白名单,如果命中白名单则会走分表数据源,否则就会走旧库。
  3. 数据校验组件如果无报警,修改配置中心配置来慢慢放量,直到全部切量完成。
  4. 如果数据有问题,可以修改配置及时切回旧库。

数据分表之后的查询方案

数据分表之后对 B 端后台业务的查询是有影响的,之前查询一张表,现在要查询 256 张表吗?

对此我们做了数据归集方案,将 256 张表的数据都归集到 TiDB 分布式数据库中,TiDB 在大数据量的查询效率上大大优于 MySQL。

利用 Canal 将分表数据做归集


TiDB 库中的表可以根据 B 端业务查询的字段需求,多设置几个索引,以提高查询效率。

总结

经过一个多月的奋战,终于将 7 亿多的表拆分完成,无事故,无回滚,无业务代码侵入平稳的完成了数据迁移和分表操作。

#Java##基础架构##程序员##计算机##Java学习#
全部评论
感谢楼主的详细分享
点赞 回复 分享
发布于 2022-08-09 14:06

相关推荐

程序员小白条:这比例牛逼,750:1
点赞 评论 收藏
分享
06-27 12:54
已编辑
门头沟学院 Java
累了,讲讲我的大学经历吧,目前在家待业。我是一个二本院校软件工程专业。最开始选专业是觉得计算机感兴趣,所以选择了他。本人学习计算机是从大二暑假结束开始的,也就是大三开始。当时每天学习,我个人认为Java以及是我生活的一部分了,就这样持续学习了一年半,来到了大四上学期末,大概是在12月中旬,我终于找的到了一家上海中厂的实习,但我发现实习生的工作很枯燥,公司分配的活也不多,大多时间也是自己在自学。就这样我秋招末才找到实习。时间来到了3月中旬,公司说我可以转正,但是转正工资只有7000,不过很稳定,不加班,双休,因为要回学校参加答辩了,同时当时也是心高气傲,认为可以找到更好的,所以放弃了转正机会,回学校准备论文。准备论文期间就也没有投递简历。然后时间来到了5月中旬,这时春招基本也结束了,然后我开始投递简历,期间只是约到了几家下场面试。工资也只有6-7k,到现在我不知道该怎么办了。已经没有当初学习的心劲了,好累呀,但是又不知道该干什么去。在家就是打游戏,boss简历投一投。每天日重一次。26秋招都说是针对26届的人,25怎么办。我好绝望。要不要参加考公、考研、央国企这些的。有没有大佬可以帮帮我。为什么感觉别人找工作都是顺其自然的事情,我感觉自己每一步都在艰难追赶。八股文背了又忘背了又忘,我每次都花很长时间去理解他,可是现在感觉八股、项目都忘完了。真的已经没有力气再去学习了。图片是我的简历,有没有大哥可以指正一下,或者说我应该走哪条路,有点不想在找工作了。
码客明:太累了就休息一下兄弟,人生不会完蛋的
如果实习可以转正,你会不...
点赞 评论 收藏
分享
评论
点赞
7
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务