转转上门履约服务拆分库表迁移实践

news2024/9/21 16:32:02

文章目录

    • 1 背景
    • 2 数据迁移方案
      • 2.1 方案一:双写新旧库
      • 2.2 方案二:灰度开关切换新旧库
    • 3 迁移细节
      • 3.1 业务代码改造
      • 3.2 数据同步
      • 3.3 数据一致性校验
    • 4 总结
    • 5 参考资料

1 背景

随着业务不断发展,一个服务中部分功能模块适合沉淀下来作为通用的基础能力。作为通用的基础能力,对提供的服务可用性和稳定性有较高的要求,因此把该部分功能模块拆分出来,单独一个服务是比较好的选择。为了更好的与业务服务物理隔离,不仅需要从代码层面拆分,数据库层面也需要拆分。在做技术方案设计时面临着以下几个问题:

  • 迁移过程中是否允许停服?如果停服,停服时间窗口如何做到尽可能短?
  • 旧库表数据如何迁移到新库?
  • 迁移后如何保证旧库表数据与新库表数据一致?

2 数据迁移方案

面向C端用户的场景,我们可能会脱口而出一个数据双写的方案。面向B端用户场景,可能直接暴力停服迁移。很多时候线上业务场景都是读多写少,如果把上面两个方案折衷一下也是一个不错的迁移方案。

下面介绍两个数据迁移方案,一个是大家耳熟能详的数据双写,另一个是以短暂写入失败为代价的开关控制迁移方案。

2.1 方案一:双写新旧库

双写的迁移流程如下图所示:

双写迁表

图-1

  • 新旧库数据同步:由DBA协助完成旧库表数据迁移到新库,并使用增量同步工具把旧库表数据同步到新库。
  • 开启双写:业务服务迁库代码改造上线,在业务写入低峰期校验新库与旧库表数据一致后,DBA断开旧库与新库的同步,业务服务同步开启写新库开关,开始双写。
  • 读新库:校验新库与旧库表数据一致后,读流量切换到新库进行数据验证,验证期间有问题可以随时切换回旧库。
  • 代码清理:读写流量全量切换新库,下线写旧库代码。

采用双写方案迁移库表可以做到用户无感知的平滑切换,验证过程中发现问题可以及时回滚。

双写引入了多个数据源,项目中如果使用了事务,面临着跨库事务,对事务代码块的改动成本相对较大。同时还面临着同步双写和异步双写的选择:

  • 同步双写:新旧库的数据一致性有保障,写新库失败会影响现有的业务。
  • 异步双写:写新库失败会导致数据不一致,不影响现有业务,需要额外的补偿方案保证新旧库数据的最终一致。

2.2 方案二:灰度开关切换新旧库

该方案不涉及双写,在代码里根据开关控制使用新库还是旧库,切换流程如下图所示:

图-2

  • 新旧库数据同步:DBA协助先将旧库表数据迁移到新库,然后再使用增量同步工具把旧库表数据同步到新库。
  • 验证读新库:改造好的业务服务部署后,新库与旧库保持增量同步,开启读新库开关,读流量切换到新库进行验证,验证过程出现问题可以通过控制开关切回读旧库数据
  • 新旧库切换:整个切换流程的核心,改造好的业务服务上线。先切断对旧库的写入流量,让新库与旧库的增量同步追平,同时校验新库与旧库表数据的一致性,一致时便可把写流量切换到新库。
  • 代码清理:业务服务读写流量均切换到新库。

④为什么要把写流量切换到旧库的从库?

写流量切换到旧库的从库目的是为了断开对旧库相应表的写入流量,营造相对“静止”的环境让新库可以追上旧库。切断对旧库写入流量的方式有很多,选择写从库的方式来主要为了让开关都收拢到一处。

除此之外,我们可以对数据库帐号授权的形式来实现写流量的断开:

REVOKE INSERT, UPDATE, DELETE ON database_name.table_name FROM 'username';

从上述步骤中可以看到该方案有个硬伤:有短暂的停服过程。优点是确保迁移到新库的数据一定与旧库一致的,对有使用事务的场景,不需要考虑跨库事务,代码改造成本低。

3 迁移细节

我们要改造的业务服务代码中涉及声明式事务和编程式事务,为了降低跨库事务带来的改造成本,并结合上门履约的业务场景——业务数据写入多集中于白天,我们最终采用了“灰度开关切换新旧库”方案。

3.1 业务代码改造

需要迁移的表数量不多,实现时对DAO层代码进行改造,抽取ProxyDAO层,原来对DAO层的方法调用全部替换成ProxyDAO,ProxyDAO层代码植入开关控制代码,根据开关决定访问新库旧库。

图-3

3.2 数据同步

创建好新库后,DBA将旧库需要迁移的表数据全量同步一次到新库,然后使用PingCAP的数据导入工具——Syncer,使用该工具进行数据增量同步需要满足以下前提:

  • 5.5 < MySQL 版本 < 8.0
  • 开启binlog,并且格式为ROW,且binlog_row_image必须设置为为FULL

从Syncer架构图不难看出:同步时Syncer把自己伪装成一个 MySQL Slave,和 MySQL Master 进行通信,然后不断读取 MySQL binlog,进行 binlog 事件解析,规则过滤和数据同步。

图-4

3.3 数据一致性校验

不管是双写还是灰度开关切换新旧库的方案,都绕不开数据一致性校验。数据不一致如何产生的?

图-5

双写新旧库可能产生数据不一致的场景:

  • 图-5③:DBA检测新旧库无差异后关闭同步,写新库开关未开启前旧库来了写入的流量
  • 图-5③/④:双写后使用异步方式双写新库写入失败

灰度开关切换新旧库可能产生数据不一致的场景:

  • 图-5c/d:数据同步工具挂了

我们所使用的迁移方案需要重点关注新旧库的同步情况,为此我们做了2层数据校验:

  • DBA在旧库写流量关闭后对数据进行一致性校验
  • 业务服务写个定时任务定期去抽样校验

MySQL主从模式下可以通过show slave status 命令查看主从延迟情况,根据Seconds_Behind_Master的值是否为0来判定是否有延迟,有延迟2个库的数据肯定不一致。上面提到我们增量同步使用的是Syncer,它只是伪装成从库,并不是真正的从库,使用MySQL主从模式下数据一致性校验方法行不通了,因此借助了PingCAP官方提供的sync-diff-inspector工具进行数据一致性校验。

sync-diff-inspector工具架构图如下所示:

图-6

sync-diff-inspector校验流程主要分以下步骤:

  1. 对需要比较的表数据使用多线程方式划分为多个chunk,采用生产者-消费者模型将划分的chunk放入队列里
  2. 消费者线程从队列取出划分好的chunk,对这个chunk的上下游数据对比,计算出checksum
  3. 某个chunk的上下游checksum如果不一致,则对该chunk二分法方式找出不一致的数据,生成修复SQL

使用sync-diff-inspector工具对新旧库表全量校验后数据基本可以保障一致,不过该工具使用的前提是需要保证数据校验期间被校验的表上下游都没有数据写入。从校验工具的工作原理来看,校验耗时跟数据量成正比,迁移的数据越多校验时间越长,如果对全量数据的校验,校验周期会变得特别长。

根据目前业务现状,已经到终态的冷数据基本不会有写入操作。为尽可能缩短写入失败时间,业务数据校验的重点放在近期修改过的数据。冷数据不需要每次一致性校验时都参与进来。可以根据更新时间作为筛选条件,在新旧库抽取最近一段时间内修改过的数据,逐行对比数据是否一致,校验流程如下图所示:

图-7

对旧库和新库按照更新时间筛选数据时,使用多线程并发的方式取数,尽可能减少时间差。
根据更新时间筛选数据时,我们可能很自然的写出了下面的SQL:

select * from  table  where update_time >= X;

串行执行相同查询时序图

图-8

这个SQL如果使用单线程串行的方式执行,后面执行查出来的结果大概率会跟先执行的不一样。因为SQL筛选数据本身也会有耗时,特别是筛选时间范围比较大的时候,需要扫描更多的数据,耗费的时间越长。SQL筛选数据期间修改的数据,对先执行的SQL来说是不可见的。

校验时先对冷数据做一次全量校验,之后每次都是校验最近修改的,这样可以大大缩小查询范围,缩短校验数据一致性的时间。查询条件使用了上界和下界限定条件,保障了统计口径是一致的。校验代码消耗的时间,作为下一次迭代使用的时间偏移量,当“新旧库查询结果都为空”时表明最近都没有数据写入,并且N-S的时间差足够小,是可以认为两个库的表数据是一致的,这个时候把流量自动切换到新库可以实现平滑迁库。

N-S的时间差在什么量级?

初始时这个时间差会比较大,整个迭代过程中首次使用的更新时间筛选范围一般是最大的,除非一次取数时间加上程序校验时间的耗时比初始指定的偏移量K大。更新时间筛选范围会随着迭代越来越小,在写流量低峰期,SQL查出的数据也会越来越少,直至查不出数据。这个时间差差不多就是一条根据更新时间查数据的时间。如果更新时间是索引,查询的时间范围很小,N-S的时间差最优情况下是在毫秒级的。

4 总结

最终我们采用保守的方式——旧库写流量切换从库,没有使用平滑切换的方案。以业务数据校验为主,DBA层数据校验为辅完成数据的迁移。整个过程读流量正常,写流量在切换到旧库从库 → 新旧库增量数据一致性校验 → 写流量切换到新库期间会失败,流量低谷期写入失败时间不超过5秒。

我们选择短暂停服的技术方案,这个方案虽然不是最优的,但是会跟业务更匹配,方案简单,改造成本低,对业务影响范围更小。技术方案的选择一定是贴合实际业务场景的,脱离业务场景的所谓最优方案不过是空中楼阁,当真正踏出登楼第一步时可能就坍塌了。

服务拆分&数据迁移对技术功底要求不那么高,并不需要使用高深的技术,更多的是考验一个人细心程度,对每个细节的深入思考与把控。失之毫厘,差之千里,一个细节没处理好,可能就会带来灾难性问题。

大家还有什么好的平滑迁移数据的方法欢迎到评论区留言。

5 参考资料

  • 解析 TiDB 在线数据同步工具 Syncer
  • PingCAP 文档

关于作者

张莲祥,转转上门履约业务研发工程师

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1965002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OverlayFS 文件系统介绍

引言 OverlayFS&#xff08;Overlay Filesystem&#xff09;是 Linux 内核中的一种联合文件系统&#xff08;Union Filesystem&#xff09;&#xff0c;它通过叠加多个目录形成一个单一的文件系统视图。作为 Docker 的默认存储驱动之一&#xff0c;OverlayFS 在提高性能和简化容…

Python 装饰器简单使用

在Python编程中&#xff0c;装饰器&#xff08;Decorators&#xff09;是一种强大且优雅的功能&#xff0c;它允许我们在不修改原有函数代码的情况下&#xff0c;给函数增加新的功能。装饰器本质上是一个函数&#xff0c;它接收一个函数作为参数并返回一个新的函数&#xff0c;…

【医疗大数据】健康分析法应用于商业领域的文献回顾

这几天在看医疗大数据的文章&#xff0c;找到了这篇关于健康分析学在商业领域的应用&#xff0c;概括性地探讨了通过医疗大数据来解决医疗领域的问题。 Health analytics in business research: a literature review 1、研究背景&#xff1a; 本文探讨了健康分析学&#xff0…

吴恩达老师机器学习-ex3

使用逻辑回归 导入库&#xff0c;因为这次的数据是mat文件&#xff0c;需要使用scipy库中的loadmat进行读取数据。 通过对数据类型的分析&#xff0c;发现是字典类型&#xff0c;查看该字典的键&#xff0c;可以发现又X&#xff0c;y等关键字。 import numpy as np import m…

Python none和0区别是什么

None是Python中的一个关键字&#xff0c;None本身也是个一个数据类型&#xff0c;而这个数据类型就是None&#xff0c;它和0、空字符串以及false均不一样&#xff0c;这些都只是对象&#xff0c;而None也是一个类。 给个bool测试&#xff1a; val None if val:print "No…

【应急响应】Linux权限维持 -隐藏权限

前言 不知攻焉知守&#xff0c;学会排查就要先学习如何攻击。 隐藏文件 Linux下创建一个隐藏文件&#xff1a;touch .test.txt 查看Linux下的隐藏文件需要用到命令&#xff1a;ls -al 隐藏文件时间戳 touch -r .docker hello.php 创建的hello.php文件会和.docker创建文件的时间…

printf颜色格式化使用

前言 本文介绍了如何使用C语言标准函数进行字符的有色打印&#xff0c;以及实现一些特殊的输出。 一、一般使用 在C语言中&#xff0c;printf常用来进行标准化格式输出&#xff0c;其作用是将字符串打印到屏幕上面&#xff0c;其中可以使用占位符、转义符来对字符串进行格式…

[玄机]流量特征分析-常见攻击事件 tomcat

题目网址【玄机】&#xff1a;https://xj.edisec.net/ Tomcat是一个开源的Java Servlet容器&#xff0c;它实现了Java Servlet和JavaServer Pages (JSP) 技术&#xff0c;提供了一个运行这些应用程序的Web服务器环境。Tomcat由Apache软件基金会的Jakarta项目开发&#xff0c;是…

使用思科模拟电子邮件实验

实验十 电子邮件实验 文章目录 实验十 电子邮件实验1.实验目的2.实验流程3.实验步骤 1.实验目的 1&#xff09;理解电子邮件的含义 2&#xff09;理解邮件系统的工作过程 3&#xff09;掌握简单的邮件服务器的配置 2.实验流程 开始 → 布置拓扑 → 配置路由及IP地址 → 配置…

JAVA—面向对象编程基础

面向对象是java编程的套路。更符合人类思维习惯&#xff0c;编程更直观。面向对象有三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。 目录 1.理解面向对象 2.对象在计算机中的执行原理 3.类和对象的一些注意事项 4.类与对象的一些语法知识 &#xff08;1&am…

小阿轩yx- KVM 高级功能部署

小阿轩yx- KVM 高级功能部署 案例分析 案例概述 企业内部为了使服务器资源达到最大化利用会进行 KVM 虚拟化每台服务器部署多台 KVM 虚拟机。KVM 虚拟机数量不断增多个别服务器会出现资源过载现象&#xff0c;就需要对部分 KVM 虚拟机迁移针对可停机的 KVM 虚拟机可使用静态…

为你的世界,开一扇任意门 (中篇)

传送门 《为你的世界&#xff0c;开一扇任意门&#xff08;上篇&#xff09;》 一、蓬门今始为君开 在上篇&#xff0c;为各位朋友展示了&#xff0c;在【我的电脑】中添加自己的任意门后的效果。 本篇直接上干货&#xff0c;手把手教大家实操&#xff08;dll插件和自动化注册…

PostgreSQL如何入门学习?

在各种数据岗招聘中&#xff0c;SQL几乎成为了必备技能。有公司的地方就会有数据&#xff0c;有数据的地方就会有数据库&#xff0c;有数据库的地方就会有SQL。 SQL在数据分析中到底有多重要&#xff1f;这么说吧&#xff0c;除了Excel外&#xff0c;SQL是数据工作最常接触的到…

Java--接口和内部类

目录 接口接口的使用接口的特性多继承问题对象类型进行比较深拷贝和浅拷贝浅拷贝深拷贝 接口和抽象类区别 Object类内部类实例内部类静态内部类局部内部类匿名内部类 接口 接口是一种引用数据类型&#xff0c;语法和抽象类类似&#xff0c;只不过需要把abstract换成interface 接…

JavaScript (七)——JavaScript 对象和函数

目录 JavaScript 对象 真实生活中的对象&#xff0c;属性和方法 JavaScript 对象 对象定义 对象属性 访问对象属性 对象方法 JavaScript 函数 JavaScript 函数语法 调用带参数的函数 带有返回值的函数 局部 JavaScript 变量 全局 JavaScript 变量 JavaScript 变量…

“遥遥领先”的观察者模式

前提 观察者模式是我们在开发时经常会用到的模式&#xff0c;最近在维护公司项目时看见了前辈的代码用到了观察者模式。就想来和大家讲解观察者模式 观察者模式 生搬硬套概念肯定会让我们新手很难理解&#xff0c;刚好最近华为手机出新机了&#xff0c;一机难抢&#xff0c;…

内存问题检测

内存检测方式 gcc/g 内存检测方式如下&#xff0c;添加一些编译标签&#xff1a; -fsanitizeleak 检测内存泄漏。例如添加标签&#xff1a;-fsanitizeleak -g -O0-fsanitizeaddress 检测内存越界。例如添加标签&#xff1a;-fsanitizeaddress -g -O2&#xff0c;优化级别开…

ThreadLocal详解及ThreadLocal源码分析

提示&#xff1a;ThreadLocal详解、ThreadLocal与synchronized的区别、ThreadLocal的优势、ThreadLocal的内部结构、ThreadLocalMap源码分析、ThreadLocal导致内存泄漏的原因、要避免内存泄漏可以用哪些方式、ThreadLocal怎么解决Hash冲突问题、避免共享的设计模式、ThreadLoca…

Android DexOpt七种触发流程解析【原创硬核】

Android 13 DexOpt七种触发流程解析 众所周知&#xff0c;DexOpt是安卓应用性能优化非常重要的手段&#xff0c;相当于将应用对虚拟机的多层调用直接转化成了arm机器码。Dex优化过和没优化过&#xff0c;效果千差万别。本文深入解析android系统DexOpt机制的触发流程。 1 DexOpt…

如何学习自动化测试工具!

要学习和掌握自动化测试工具的使用方法&#xff0c;可以按照以下步骤进行&#xff1a; 一、明确学习目标 首先&#xff0c;需要明确你想要学习哪种自动化测试工具。自动化测试工具种类繁多&#xff0c;包括但不限于Selenium、Appium、JMeter、Postman、Robot Framework等&…