当前位置: 首页 > news >正文

订单服务拆分库表迁移实践

随着业务的不断发展,有些功能模块适合沉淀为通用的基础能力。作为通用基础能力,这些功能模块对服务的可用性和稳定性有较高要求。因此,将这部分功能模块拆分为一个独立的服务是一个较好的选择。为了更好地实现与业务服务的物理隔离,不仅需要在代码层面进行拆分,还需要在数据库层面进行拆分。在设计技术方案时,需要解决以下几个问题:迁移过程中是否允许停服?如果需要停服,如何尽量缩短停服时间窗口?如何将旧库表的数据迁移到新库?迁移后如何保证旧库表数据与新库表数据的一致性?针对这些问题,对于面向C端用户的场景,我们可能会迅速想到数据双写方案。而对于面向B端用户的场景,可能会选择直接停服进行迁移。许多时候,线上业务场景中读操作远多于写操作,因此,综合上述两种方案的优点,采用一种折衷的迁移方案也是一个不错的选择。下面介绍两个数据迁移方案,一个是大家耳熟能详的数据双写,另一个是以短暂写入失败为代价的开关控制迁移方案。

二、数据迁移方案

1、方案一:双写新旧库
双写的迁移流程如下图所示:
在这里插入图片描述
新旧库数据同步方案
1)数据迁移:由DBA协助将旧库表的数据迁移到新库,并使用增量同步工具将旧库表数据同步到新库。
2)开启双写:在业务写入低峰期,业务服务的迁库代码改造上线。校验新库与旧库表数据一致后,DBA断开旧库与新库的同步。业务服务同步开启写新库的开关,开始双写。
3)读新库:校验新库与旧库表数据一致后,将读流量切换到新库进行数据验证。验证期间若出现问题,可以随时切换回旧库。
4)代码清理:读写流量全部切换到新库后,下线写旧库的代码。
通过双写方案迁移库表,可以实现用户无感知的平滑切换,并在验证过程中发现问题时及时回滚。
双写方案中的挑战
双写引入了多个数据源,若项目中使用了事务,会面临跨库事务的问题。对事务代码块的改动成本较大。同时,还需在同步双写和异步双写之间进行选择:
同步双写:新旧库的数据一致性有保障,但若写新库失败,会影响现有业务。
异步双写:写新库失败会导致数据不一致,但不影响现有业务。需要额外的补偿方案以保证新旧库数据的最终一致。
2、方案二:灰度开关切换新旧库
该方案不涉及双写,在代码里根据开关控制使用新库还是旧库,切换流程如下图所示:
在这里插入图片描述
新旧库数据同步方案
1)数据迁移:DBA协助将旧库表的数据迁移到新库,然后使用增量同步工具将旧库表数据同步到新库。
2)验证读新库:改造好的业务服务部署后,新库与旧库保持增量同步,开启读新库的开关,将读流量切换到新库进行验证。验证过程中若出现问题,可以通过控制开关切回读旧库数据。
3)新旧库切换:这是整个切换流程的核心步骤。改造好的业务服务上线后,先切断对旧库的写入流量,让新库与旧库的增量同步追平。同时,校验新库与旧库表数据的一致性。当一致性得到确认后,将写流量切换到新库。
4)代码清理:业务服务的读写流量全部切换到新库后,进行代码清理,下线写旧库的代码。
5)为什么要将写流量切换到旧库的从库?将写流量切换到旧库的从库的目的是为了断开对旧库相应表的写入流量,营造一个相对“静止”的环境,以便新库追上旧库。切断对旧库写入流量的方式有很多,选择写从库的方式主要是为了集中管理开关,简化操作和控制。
选择方案
双写方案在保证数据一致性的前提下,实现了用户无感知的平滑切换。在验证过程中,如果发现问题,可以及时回滚。针对具体的业务场景和需求,选择同步双写或异步双写方案:
同步双写:保证新旧库数据的一致性,但如果写新库失败,会影响现有业务。
异步双写:写新库失败会导致数据不一致,但不影响现有业务。需要额外的补偿方案保证新旧库数据的最终一致。

三、迁移细节

我们要改造的业务服务代码中涉及声明式事务和编程式事务,为了降低跨库事务带来的改造成本,并结合上门履约的业务场景——业务数据写入多集中于白天,我们最终采用了“灰度开关切换新旧库”方案。
1、业务代码改造
需要迁移的表数量不多,实现时对DAO层代码进行改造,抽取ProxyDAO层,原来对DAO层的方法调用全部替换成ProxyDAO,ProxyDAO层代码植入开关控制代码,根据开关决定访问新库旧库。
在这里插入图片描述
2、数据同步
使用PingCAP的Syncer工具进行新旧库数据同步
1)创建新库并全量同步:在新库创建好后,DBA将旧库中需要迁移的表数据全量同步到新库。使用Syncer进行增量同步:
2)使用PingCAP的数据导入工具Syncer进行数据增量同步。使用该工具需要满足以下前提条件:MySQL 版本在 5.5 和 8.0 之间
开启 binlog,并且格式为 ROW
binlog_row_image 设置为 FULL
3)Syncer的工作原理:从Syncer的架构图可以看出,Syncer在同步时会将自己伪装成一个 MySQL Slave,并与 MySQL Master 进行通信。然后,Syncer不断读取 MySQL binlog,进行 binlog 事件解析,规则过滤和数据同步。
在这里插入图片描述3、数据一致性校验
不管是双写还是灰度开关切换新旧库的方案,都绕不开数据一致性校验。数据不一致如何产生的?
在这里插入图片描述
双写新旧库可能产生的数据不一致场景
场景 1:DBA检测新旧库无差异后关闭同步,但在写新库开关未开启前,旧库来了写入的流量。
场景 2:在双写过程中,使用异步方式进行双写,新库写入失败。灰度开关切换新旧库可能产生的数据不一致场景
场景 3:数据同步工具发生故障。
迁移方案的重点关注——新旧库同步情况
为确保新旧库的数据一致性,我们采用了两层数据校验机制:
1)DBA数据一致性校验:在旧库写流量关闭后,DBA对数据进行一致性校验。
2)业务服务定时校验:业务服务写入定时任务,定期进行抽样校验。
在MySQL主从模式下,可以通过 show slave status 命令查看主从延迟情况,根据 Seconds_Behind_Master 的值是否为0来判断是否存在延迟。如果有延迟,两个库的数据肯定不一致。然而,由于我们增量同步使用的是Syncer,它只是伪装成从库,并不是真正的从库,因此MySQL主从模式下的数据一致性校验方法不再适用。
使用Syncer和sync-diff-inspector进行数据一致性校验
我们借助PingCAP官方提供的 sync-diff-inspector 工具进行数据一致性校验。该工具能够有效检测和解决新旧库之间的数据不一致问题,确保迁移方案的可靠性。
在这里插入图片描述
使用sync-diff-inspector进行数据一致性校验的流程
1)划分数据:对需要比较的表数据使用多线程方式划分为多个chunk,采用生产者-消费者模型将划分的chunk放入队列中。
2)计算校验和:消费者线程从队列中取出划分好的chunk,对该chunk的上下游数据进行对比,计算出checksum。
3)查找不一致数据:如果某个chunk的上下游checksum不一致,则对该chunk使用二分法找出不一致的数据,并生成修复SQL。
通过sync-diff-inspector工具对新旧库表进行全量校验后,可以基本保障数据的一致性。不过,该工具的使用前提是需要确保在数据校验期间,被校验的表上下游都没有数据写入。
校验工具的工作原理和挑战
从校验工具的工作原理来看,校验耗时与数据量成正比。迁移的数据越多,校验时间越长。如果进行全量数据的校验,校验周期会变得特别长。因此,在实施过程中,需要合理安排校验时间,并结合业务需求选择适当的校验策略,以确保数据一致性的同时,尽量减少对业务的影响。
根据目前业务现状,已经到终态的冷数据基本不会有写入操作。为尽可能缩短写入失败时间,业务数据校验的重点放在近期修改过的数据。冷数据不需要每次一致性校验时都参与进来。可以根据更新时间作为筛选条件,在新旧库抽取最近一段时间内修改过的数据,逐行对比数据是否一致,校验流程如下图所示:
在这里插入图片描述
对旧库和新库按照更新时间筛选数据时,使用多线程并发的方式取数,尽可能减少时间差。根据更新时间筛选数据时,我们可能很自然的写出了下面的SQL:
select * from table where update_time >= X;
在这里插入图片描述
优化SQL执行及数据校验策略
在执行SQL筛选数据时,若采用单线程串行方式,后面的查询结果很可能会与先执行的不一致。这是因为SQL筛选数据本身需要耗时,尤其是筛选时间范围较大时,需要扫描更多的数据,耗时更长。筛选期间修改的数据对先执行的SQL不可见。
数据校验策略
全量校验冷数据:首先对冷数据进行一次全量校验。
增量校验最近修改的数据:之后每次校验最近修改的数据,以缩小查询范围,缩短校验数据一致性的时间。
查询条件和迭代策略
使用上下界限定条件确保统计口径一致。校验代码消耗的时间作为下一次迭代使用的时间偏移量。当“新旧库查询结果都为空”时,表明最近没有数据写入,并且N-S的时间差足够小,可以认为两个库的表数据是一致的。这时可自动将流量切换到新库,实现平滑迁库
N-S的时间差量级
初始阶段:时间差较大。首次使用的更新时间筛选范围一般最大,除非一次取数时间加上程序校验时间耗时比初始指定的偏移量K大。
迭代过程中:更新时间筛选范围会逐渐变小。写流量低峰期,SQL查出的数据会越来越少,直至查不出数据。最优情况:如果更新时间是索引,查询的时间范围很小,N-S的时间差在最优情况下可以达到毫秒级。
通过上述优化和策略,可以有效减少数据校验时间,提高迁移过程的效率和数据一致性。

相关文章:

  • 杰理-701-手表sdk无法电脑连接经典蓝牙
  • calico.yaml+国内源
  • 《Effective Python》第2章 字符串和切片操作——深入理解Python 中的字符数据类型(bytes 与 str)的差异
  • Day1 时间复杂度
  • 【深度学习-Day 10】机器学习基石:从零入门线性回归与逻辑回归
  • 云共享虚拟主机具体是指什么?
  • “追光”植物背后的故事(二)
  • SpringBoot--springboot简述及快速入门
  • 基于 PLC 的轮式服务机器人研究
  • 医疗实时操作系统方案:手术机器人的微秒级运动控制
  • 【Hot 100】208. 实现 Trie (前缀树)
  • 基于C#+MySQL实现(WinForm)企业设备使用信息管理系统
  • niushop单商户V5多门店版V5.5.0全插件+商品称重、商家手机端+搭建环境教程
  • 从数据中台到数据飞轮:数字化转型的演进之路
  • python如何做人脸识别
  • Docker与PostgreSQL
  • 国联股份卫多多与七腾机器人签署战略合作协议
  • 基于Spring Boot+Layui构建企业级电子招投标系统实战指南
  • 工业巡检机器人 —— 机器人市场的新兴增长引擎
  • OpenTiny icons——超轻量的CSS图标库,引领图标库新风向
  • 西安市未央区委书记刘国荣已任西咸新区党工委书记
  • 横跨万里穿越百年,《受到召唤·敦煌》中张艺兴一人分饰两角
  • 演员黄晓明、金世佳进入上海戏剧学院2025年博士研究生复试名单
  • 美股全线收涨:道指涨逾千点,纳斯达克中国金龙指数涨5.4%
  • 比特币挖矿公司GRYP股价涨超171%:将与特朗普儿子创设的公司合并
  • 库尔德工人党决定自行解散