256变4096:分库分表扩容如何实现平滑数据迁移?

简介: 本文作者就一个高德打车弹外订单系统进行了一次扩分库分表和数据库迁移。

 

一、 背景

2020年,笔者负责的一个高德打车弹外订单系统进行了一次扩分库分表和数据库迁移。该订单系统整体部署在阿里云上,服务使用阿里云ECS部署,数据库采用阿里云RDS,配置中心基于阿里云ACM自研,数据同步基于阿里云DTS自研以及自研分库分表组件、分布式ID组件等等。

此次进行扩分库分表的背景是,原4实例4库、每个库64张表一共256张表,部分单表已超千万量级,按当前每日单量量级,一年内单表会达到上亿条记录,单表数据量过大会带来数据库性能问题。

注:【弹内弹外】弹是指弹性计算,弹内与弹外其实是指两套独立的弹性计算网络环境。弹内主要是指部署在阿里生产网的弹性计算环境,最早是基于原有淘宝技术构建的,主要用于支撑淘宝业务。弹外主要是指部署在阿里公有云的弹性计算环境,支撑了阿里云计算业务。

二 、容量规划

1 、当前分库分表情况

4实例(16C/64G/3T SSD),4库(每个实例一个库),每库64张表,共256张表。

通过RDS后台一键诊断功能,来计算表空间使用情况(这里拿测试环境数据库举例)。

 

2 、容量计算

实例数

数据库的瓶颈主要体现在:磁盘、CPU、内存、网络、连接数,而连接数主要是受CPU和内存影响。CPU和内存可以通过动态升配来提升,但是SSD磁盘容量最大支持到6T(32C以下最大3T、32C及以上最大6T)。

但是现阶段兼顾成本,可先将实例扩容一倍,采用8个实例(16C/64G/3T SSD),每个实例建4个库(database)、每个库128张表(这里实际上是一个成本取舍的过程,理论上应该采取"多库少表"的原则,单库128张表其实太多了,单库建议32或64张表为宜)。

后续如果实例压力提升可进行实例配置升级(16C/128G、32C/128G、32C/256G等);未来如出现单实例升配无法解决,在考虑扩容实例,只需要将database迁移至新实例,迁移成本较小。

 

表数

按单表最多1000w条数据评估,4096张表可支持日5000w单3年(10.1压测标准)、日2000w单5年的架构。(因业务表比较多,此处忽略掉单条数据大小的计算过程)

库数

32个库,每个库128张表。未来可最大扩容到32个实例,无需rehash,只需要迁移数据。

 

阿里云RDS规格和价格一览

三、 数据迁移

因扩分库分表涉及到rehash过程(256表变4096表),而阿里云DTS只支持同构库数据迁移,所以我们基于DTS的binlog转kafka能力自研了数据同步中间件。

整个数据迁移工作包括:前期准备、数据同步环节(历史数据全量同步、增量数据实时同步、rehash)、数据校验环节(全量校验、实时校验、校验规则配置)、数据修复工具等。

 

1 、准备工作

唯一业务ID

在进行数据同步前,需要先梳理所有表的唯一业务ID,只有确定了唯一业务ID才能实现数据的同步操作。

需要注意的是:

  • 业务中是否有使用数据库自增ID做为业务ID使用的,如果有需要业务先进行改造,还好订单业务里没有。
  • 每个表是否都有唯一索引,这个在梳理的过程中发现有几张表没有唯一索引。

一旦表中没有唯一索引,就会在数据同步过程中造成数据重复的风险,所以我们先将没有唯一索引的表根据业务场景增加唯一索引(有可能是联合唯一索引)。

在这里顺便提一下,阿里云DTS做同构数据迁移,使用的是数据库自增ID做为唯一ID使用的,这种情况如果做双向同步,会造成数据覆盖的问题。解决方案也有,之前我们的做法是,新旧实体采用自增ID单双号解决,保证新旧实例的自增ID不会出现冲突就行。因为这次我们使用的自研双向同步组件,这个问题这里不细聊。

分表规则梳理

分表规则不同决定着rehash和数据校验的不同。需逐个表梳理是用户ID纬度分表还是非用户ID纬度分表、是否只分库不分表、是否不分库不分表等等。

2、 数据同步

数据同步整体方案见下图,数据同步基于binlog,独立的中间服务做同步,对业务代码无侵入。

接下来对每一个环节进行介绍。

 

历史数据全量同步

单独一个服务,使用游标的方式从旧库分批select数据,经过rehash后批量插入(batch insert)到新库,此处需要配置jdbc连接串参数rewriteBatchedStatements=true才能使批处理操作生效。

另外特别需要注意的是,历史数据也会存在不断的更新,如果先开启历史数据全量同步,则刚同步完成的数据有可能不是最新的。所以这里的做法是,先开启增量数据单向同步(从旧库到新库),此时只是开启积压kafka消息并不会真正消费;然后在开始历史数据全量同步,当历史全量数据同步完成后,在开启消费kafka消息进行增量数据同步(提高全量同步效率减少积压也是关键的一环),这样来保证迁移数据过程中的数据一致。

增量数据实时同步

增量数据同步考虑到灰度切流稳定性、容灾和可回滚能力,采用实时双向同步方案,切流过程中一旦新库出现稳定性问题或者新库出现数据一致问题,可快速回滚切回旧库,保证数据库的稳定和数据可靠。

增量数据实时同步采用基于阿里云DTS的数据订阅自研数据同步组件data-sync实现,主要方案是DTS数据订阅能力会自动将被订阅的数据库binlog转为kafka,data-sync组件订阅kafka消息、将消息进行过滤、合并、分组、rehash、拆表、批量insert/update,最后再提交offset等一系列操作,最终完成数据同步工作。

 

  • 过滤循环消息:需要过滤掉循环同步的binlog消息,这个问题比较重要后面将进行单独介绍。
  • 数据合并:同一条记录的多条操作只保留最后一条。为了提高性能,data-sync组件接到kafka消息后不会立刻进行数据流转,而是先存到本地阻塞队列,然后由本地定时任务每X秒将本地队列中的N条数据进行数据流转操作。此时N条数据有可能是对同一张表同一条记录的操作,所以此处只需要保留最后一条(类似于redis aof重写)。
  • update转insert:数据合并时,如果数据中有insert+update只保留最后一条update,会执行失败,所以此处需要将update转为insert语句。
  • 按新表合并:将最终要提交的N条数据,按照新表进行拆分合并,这样可以直接按照新表纬度进行数据库批量操作,提高插入效率。

整个过程中有几个问题需要注意:

问题1:怎么防止因异步消息无顺序而导致的数据一致问题?

首先kafka异步消息是存在顺序问题的,但是要知道的是binlog是顺序的,所以dts在对详细进行kafka消息投递时也是顺序的,此处要做的就是一个库保证只有一个消费者就能保障数据的顺序问题、不会出现数据状态覆盖,从而解决数据一致问题。

问题2:是否会有丢消息问题,比如消费者服务重启等情况下?

这里没有采用自动提交offset,而是每次消费数据最终入库完成后,将offset异步存到一个mysql表中,如果消费者服务重启宕机等,重启后从mysql拿到最新的offset开始消费。这样唯一的一个问题可能会出现瞬间部分消息重复消费,但是因为上面介绍的binlog是顺序的,所以能保证数据的最终一致。

问题3:update转insert会不会丢字段?

binlog是全字段发送,不会存在丢字段情况。

问题4:循环消息问题。

后面进行单独介绍。

rehash

前文有提到,因为是256表变4096表,所以数据每一条都需要经过一次rehash重新做分库分表的计算。

要说rehash,就不得不先介绍下当前订单数据的分库分表策略,订单ID中冗余了用户ID的后四位,通过用户ID后四位做hash计算确定库号和表号。

 

数据同步过程中,从旧库到新库,需要拿到订单ID中的用户ID后四位模4096,确定数据在新库中的库表位置;从新库到旧库,则需要用用户ID后四位模256,确定数据在旧库中的库表位置。

双向同步时的binlog循环消费问题

想象一下,业务写一条数据到旧实例的一张表,于是产生了一条binlog;data-sync中间件接到binlog后,将该记录写入到新实例,于是在新实例也产生了一条binlog;此时data-sync中间件又接到了该binlog......不断循环,消息越来越多,数据顺序也被打乱。

怎么解决该问题呢?我们采用数据染色方案,只要能够标识写入到数据库中的数据使data-sync中间件写入而非业务写入,当下次接收到该binlog数据的时候就不需要进行再次消息流转。

所以data-sync中间件要求,每个数据库实例创建一个事务表,该事务表tb_transaction只有id、tablename、status、create_time、update_time几个字段,status默认为0。

再回到上面的问题,业务写一条数据到旧实例的一张表,于是产生了一条binlog;data-sync中间件接到binlog后,如下操作:

# 开启事务,用事务保证一下sql的原子性和一致性
start transaction;
set autocommit = 0;
# 更新事务表status=1,标识后面的业务数据开始染色
update tb_transaction set status = 1 where tablename = ${tableName};
# 以下是业务产生binlog
insert xxx;
update xxx;
update xxx;
# 更新事务表status=0,标识后面的业务数据失去染色
update tb_transaction set status = 0 where tablename = ${tableName};
commit;

此时data-sync中间件将上面这些语句打包一起提交到新实例,新实例更新数据后也会生产对应上面语句的binlog;当data-sync中间件再次接收到binlog时,只要判断遇到tb_transaction表status=1的数据开始,后面的数据都直接丢弃不要,直到遇到status=0时,再继续接收数据,以此来保证data-sync中间件只会流转业务产生的消息。

 

3、 数据校验

数据校验模块由数据校验服务data-check模块来实现,主要是基于数据库层面的数据对比,逐条核对每一个数据字段是否一致,不一致的话会经过配置的校验规则来进行重试或者报警。

全量校验

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

实时校验

  • 定时任务每5分钟校验,查询最近5+1分钟旧库和新库更新的数据,做diff。
  • 差异数据进行二次、三次校验(由于并发和数据延迟存在),三次校验都不同则报警。

 

4 、数据修复

经过数据校验,一旦发现数据不一致,则需要对数据进行修复操作。

数据修复有两种方案,一种是适用于大范围的数据不一致,采用重置kafka offset的方式,重新消费数据消息,将有问题的数据进行覆盖。

 

四、 灰度切换数据源

1 、整体灰度切流方案

整体灰度方案:SP+用户纬度来实现,SP纬度:依靠灰度环境切量来做,用户纬度:依赖用户ID后四位百分比切流。

灰度切量的过程一定要配合停写(秒级),为什么要停写,因为数据同步存在一定延迟(正常毫秒级),而所有业务操作一定要保障都在一个实例上,否则在旧库中业务刚刚修改了一条数据,此时切换到新库如果数据还没有同步过来就是旧数据会有数据一致问题。所以步骤应该是:

  1. 先停写
  2. 观察数据全部同步完
  3. 在切换数据源
  4. 最后关闭停写,开始正常业务写入

2 、切流前准备——ABC验证

虽然在切流之前,在测试环境进过了大量的测试,但是测试环境毕竟和生产环境不一样,生产环境数据库一旦出问题就可能是灭顶之灾,虽然上面介绍了数据校验和数据修复流程,但是把问题拦截在发生之前是做服务稳定性最重要的工作。

因此我们提出了ABC验证的概念,灰度环境ABC验证准备:

  1. 新购买两套数据库实例,当前订单库为A,新买的两套为分别为B、C
  2. 配置DTS从A单项同步到B(dts支持同构不需要rehash的数据同步),B做为旧库的验证库,C库做为新库
  3. 用B和C做为生产演练验证
  4. 当B和C演练完成之后,在将A和C配置为正式的双向同步

 

3、 灰度切流步骤

具体灰度方案和数据源切换流程:

  1. 代码提前配置好两套数据库分库分表规则。
  2. 通过ACM配置灰度比例。
  3. 代码拦截mybatis请求,根据用户id后四位取模,和ACM设置中设置的灰度比例比较,将新库标识通过ThreadLocal传递到分库分表组件。
  4. 判断当前是否有灰度白名单,如命中将新库标识通过ThreadLocal传递到分库分表组件。
  5. 分库分表组件根据ACM配置拿到新分库的分表规则,进行数据库读写操作。
  6. 切量时会配合ACM配置灰度比例命中的用户进行停写。

 

五 、总结

整个数据迁移过程还是比较复杂的,时间也不是很充裕(过程中还穿插着十一全链路压测改造),在有限的时间内集大家之力重复讨论挖掘可能存在的问题,然后论证解决方案,不放过任何一个可能出现问题的环节,还是那句话,把问题拦截在发生之前是做服务稳定性最重要的工作。

过程中的细节还是很多的,从数据迁移的准备工作到数据同步测试,从灰度流程确定到正式生产切换,尤其是结合业务和数据的特点,有很多需要考虑的细节,文中没有一一列出。

最终经过近两个月的紧张工作,无业务代码侵入、零事故、平稳地完成了扩分库分表和数据迁移的工作。

作者:开发者小助手_LS

原文链接

本文为阿里云原创内容,未经允许不得转载

 

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

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

相关文章

OpenYurt 如何 “0 侵入” 攻破云边融合难点

简介: 随着 5G、IoT、直播、CDN 等行业和业务的发展,越来越多的算力和业务开始下沉到距离数据源或者终端用户更近的位置,以期获得很好的响应时间和成本,这是一种明显区别于传统中心模式的计算方式——边缘计算。 随着 5G、IoT、直…

Python - 深夜数据结构与算法之 Graph

目录 一.引言 二.图的简介 1.Graph 图 2.Undirected graph 无向图 3.Directed Graph 有向图 4.DFS / BFS 遍历 三.经典算法实战 1.Num-Islands [200] 2.Land-Perimeter [463] 3.Largest-Island [827] 四.总结 一.引言 Graph 无论是应用还是算法题目在日常生活中比较…

Docker Desktop宣布收费;腾讯7月已申请注册WECHAT CLOUD商标;MongoDB成为当前最具价值开源软件公司...

NEWS本周新闻回顾Docker Desktop 宣布收费近日 Docker 官方宣布一项新的动作,即将产品订阅划分为个人、专业、团队和商业不同版本。如果企业规模在 250 名员工以上或年收入超过 1000 万美元的公司想要使用 Docker Desktop,那么必须使用付费订阅。付费订阅…

Serverless 2.0,鸡蛋还是银弹?

简介: 本篇旨在介绍 Serverless 如今应用到应用(非病句)的各种困境,以及帮助用户如何去规避一些问题,提前了解方向。 浪潮 从 2014 年 Serverless 冒头至今,已经有无数的勇士在前面探路,阿里、…

基础组件完善的今天,如何通过业务组件提效?

简介: 无论是在前端刀耕火种的 jQuery/YUI 时代,还是到现在基于数据驱动 UI 的 React/Vue 时代,物料/组件一直是前端永恒的话题。基于大量重复逻辑的封装可以很显而易见地提升前端 UI 的构建效率,简单而直接,因此无论技…

​做安全操作系统,这位技术老兵是认真的!

受访者 | 王文东记者 | 伍杏玲出品 | CSDN云计算(ID:CSDNcloud)近年来国际形势变化莫测,基础软件作为建设国家信息系统的核心,其自主研发能力备受关注与热议。作为企业和个人开发者,我们如何打破当前国产基…

基于 Flink SQL 构建流批一体的 ETL 数据集成

简介: 如何利用 Flink SQL 构建流批一体的 ETL 数据集成。 本文整理自云邪、雪尽在 Flink Forward Asia 2020 的分享,该分享以 4 个章节来详细介绍如何利用 Flink SQL 构建流批一体的 ETL 数据集成, 文章的主要内容如下: 数据仓库与数据集成…

入选 SIGMOD2021 的时间序列多周期检测通用框架 RobustPeriod 如何支撑阿里业务场景?

简介: 本文除了介绍RobustPeriod的核心技术亮点,还将重点解释如何将它构筑成服务来解决阿里云的业务痛点。 近日,由阿里云计算平台和阿里云达摩院合作的时序多周期检测相关论文RobustPeriod: Robust Time-Frequency Mining for Multiple Peri…

《新程序员002》图书正式上市! 从“新数据库时代”到“软件定义汽车”

20年前,伴随着互联网打开信息化大门,技术人成为新时代的开拓者。在时代的召唤下,CSDN于2001年推出国内首个面向IT人员的专业杂志——《程序员》,成为一代代开发者的技术启蒙。20年后的今天,人工智能、云计算、大数据等…

Kubernetes 稳定性保障手册 -- 极简版

简介: Kubernetes 在生产环境中的采用率越来越高,复杂度越来越高,由此带来的稳定性保障的挑战越来越大。 Kubernetes 在生产环境中的采用率越来越高,复杂度越来越高,由此带来的稳定性保障的挑战越来越大。 对于基于 K…

收藏!这些IDE使用技巧,你都知道吗

简介: 欲善其事,先利其器。对于研发同学,在日常的开发工作中,我们与之打交道最多的便是编程的IDE。能否高效和灵活的使用IDE,将对我们的工作效率起着举足轻重的作用。 一 、背景 1 、目的 欲善其事,先利其…

做安全操作系统,这位技术老兵是认真的!

受访者 | 王文东 记者 | 伍杏玲 出品 | CSDN云计算(ID:CSDNcloud) 近年来国际形势变化莫测,基础软件作为建设国家信息系统的核心,其自主研发能力备受关注与热议。作为企业和个人开发者,我们如何打破当前…

快手基于 Flink 的持续优化与实践

简介: 快手基于 Flink 的持续优化与实践的介绍。 一、Flink 稳定性持续优化 第一部分是 Flink 稳定性的持续优化。该部分包括两个方面,第一个方面,主要介绍快手在 Flink Kafka Connector 方面做的一些高可用,是基于内部的双机房读…

平台建设的7大问题:蚂蚁AI平台实践深度总结

简介: 在支持蚂蚁几乎所有核心业务运行和发展的过程中,我们在平台建设、业务支持、平台运营、AI创新以及AI整体运营等各个方面做了很多尝试,有了不少的收获和感悟,在此分享给大家。 过去几年,我和团队一直在负责蚂蚁集…

知乎热问:进入内核态究竟是什么意思?

‍‍知乎上有一个问题:进入内核态究竟是什么意思?暂且忘记这个问题,让我们从另一个问题出发,一步步引出这个问题的答案。特权指令问题现代计算机里面,同时运行了很多程序,比如Office软件、浏览器、QQ、还有…

我在阿里云做云开发平台

简介: 你体验过云上的研发模式了没? 各大云厂商今年在开发者阵地侧逐渐开始向“云”化开发发展,最为显著的产品就是Cloud IDE,催生出来的趋势就是云端开发。云开发现阶段尽管在各大公司内部无法作为日常开发工具普遍推广&#xff…

mysql 事务日志备份_事务日志备份与恢复 5

14.5 用Bak文件恢复到故障点的奥秘如果数据库被损坏,我们就只能利用备份集文件(通常扩展名为BAK)来恢复数据库,如果备份集中包含了尾日志备份,我们同样能将数据库恢复到故障点。前面我们已经介绍了使用restore headeronly命令可以查看备份集文…

从no-code到low-code:企业级hpaPaaS的未来

简介: 本文将简单谈一谈基于 no-code > low-code > pro-code 渐进式思路的研发体系。 引子 宜搭负责人骁勇给我举过一个例子,我们小时候逢年过节穿的衣服,都是去裁缝店选一下材料、量一下尺寸,等个半个来月,讨回…

“解救”外卖骑手,美团首次公开算法规则!

整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)相信大家点外卖的时候都会注意到“预估到达时间”这一栏,那你是否好奇过这个时间是怎么得出来的呢?简单用距离除以速度?还是结合送餐距离、出餐时间和天气情况等更多…

Serverless 极致弹性解构在线游戏行业痛点

简介: 本文将通过剖析一个个具体的场景案例,以期望给相关的游戏开发同学带来共鸣,同时也希望能给非游戏行业的同学带来一些启发。 一、前言 1. 游戏客户上云关注点 游戏行业是一个富有创意又竞争激烈的市场,被称为第九艺术。游戏…