初到新公司接手支付中心系统,发现支付表单表数据量达到了 7 亿多,震惊之余不由的脊背发凉,这个系统运行犹如走钢丝,稍微有点风吹草动就可能造成线上事故,DBA 天天催着要数据迁移,给出大表解决方案,给出排期。
当你担心一件事要出问题,他就一定会出问题,不得不说这墨菲定律真™️准。营销部门搞活动,支付单量激增,客服反应好多用户投诉明明支付成功了,但还是处于支付中状态。打开日志排查原因,就是因为数据库响应慢,支付状态未能更新成功,导致未能及时通知上游系统支付成功结果。(当然支付逻辑也存在问题,这点不在此文讨论范围内)
痛定思痛,分库分表和数据迁移迫在眉睫。
什么时候需要分库
分库主要是解决并发量大的问题,数据库的连接数是有限的,当读或者写的QPS过高,导致数据库连接数不够时,就需要考虑分库,通过增加数据库实例的方式来增加连接数,从而提升系统的并发性能。
什么时候需要分表
分表主要时应对数据量大的情况,当一个表的数据量过大时,不论是读还是写的性能都会出现明显下降,一张表的数据量最好控制在1000万以内(看硬件性能)。
首先通过计算得到高峰期QPS每秒约50,并发量不算特别高,所以没有必要做分库来分担并发量。
在业务高峰期的月份每日的支付单量约270多万,业务低峰期的月份每日的支付单量约130多万,每年大概的单量为6亿单左右,
按照3到5年的规划,每张表约1000万数据,计划分256张表。
我们目前数据库配置是32C/64G/3T SSD,满足需求。
数据迁移分析及遇到的问题:
整体迁移方案
下面按照迁移步骤来逐一讲解:
将实时新增的数据同步到新库的表中。这阶段利用的 Canal、RocketMQ 中间件实现,Canal 获取 MySQL BinLog 日志,发送到 RocketMQ 指定的 Topic,消费者对数据做处理后插入到新的库表。
增量数据同步问题
存量数据同步使用的是游标方式,从旧库中分批查询数据,经过业务单号hash取模后批量插入到新库,直到迁移到数据 ID 为增量开始的数据 ID 时结束。
为了保证迁移过程中的系统稳定性,在出现问题时,要有可回滚的能力,这就需要新库中的新增数据也要同步到旧库中。
那这就存在消息无限循环问题了,旧库中新增一条数据,Canal 接收到这条数据的 binlog 消息,将该数据写入到新库中,于是新库也产生一条 binlog 消息,Canal 接收到消息,将该数据写入到旧库中,....不断的循环 。
我们的方案是给数据染色,旧表需要新增一个标识字段来说明是旧表产生的数据,标识字段默认值为 0。同样新表也需要增加一个标识字段说明是新表产生的数据,标识字段默认值为 1。标识字段一定要指定默认值,这样业务代码就不需要改动了。
当旧库向新库同步时,消息过滤组件会判断标识字段是否为 0,如果为 0 则说明是旧库产生的数据,就同步到新库,否则就会丢弃消息。新库向旧库同步也是如此,只不过新库的标识字段为 1。这样就解决了数据双向同步产生的消息循环问题了。
数据迁移完成,需要核对逐条逐个字段是否一致,如果不一致需要记录下数据和报警通知。
以旧库为基准,查询每条数据在新库中是否存在,并且每个字段是否一致。
以新库为基准,查询每条数据在旧库中是否存在,并且每个字段是否一致。
对于不一致的数据记录到 diff 表中,扫描 diff 表再次、三次做校验,如果还是不一致,就需要手动校验了。再次校验的目的是可能新旧库同步存在延迟。
流量灰度方案及数据源切换方案:
数据分表之后对 B 端后台业务的查询是有影响的,之前查询一张表,现在要查询 256 张表吗?
对此我们做了数据归集方案,将 256 张表的数据都归集到 TiDB 分布式数据库中,TiDB 在大数据量的查询效率上大大优于 MySQL。
利用 Canal 将分表数据做归集
TiDB 库中的表可以根据 B 端业务查询的字段需求,多设置几个索引,以提高查询效率。
经过一个多月的奋战,终于将 7 亿多的表拆分完成,无事故,无回滚,无业务代码侵入平稳的完成了数据迁移和分表操作。
本文由梁桂钊于2023-09-12发表在梁桂钊的博客,如有疑问,请联系我们。
本文链接:https://720ui.com/12959.html