亿级大表分库分表实战总结(万字干货,实战复盘)(二)

简介: 亿级大表分库分表实战总结(万字干货,实战复盘)(二)

4.第三阶段:改造和上线(慎重)


前两个阶段完成后,开始业务切换流程,主要步骤如下:


1)中台服务采用单读 双写 的模式


2)旧表往新表开着数据同步


3) 所有服务升级依赖的projectDB版本,上线RPC,如果出现问题,降版本即可回滚(上线成功后,单读新库,双写新旧库)


4)检查监控确保没有 中台服务 以外的其他服务访问旧库旧表


5)停止数据同步


6)删除旧表


4.1 查询改造


如何验证我们前两个阶段设计是否合理?能否完全覆盖查询的修改 是一个前提条件。


当新表设计完毕后,就可以以新表为标准,修改老的查询。


以本项目为例,需要将旧的sql在 新的中台服务中 进行改造。


1)读查询的改造


可能查询会涉及以下几个方面:


a)根据查询条件,需要将pk1和pk2的inner join改为对应分表键的新表表名


b)部分sql的废弃字段处理


c)非分表键查询改为走搜索平台的查询,注意保证语义一致


d)注意写单测避免低级错误,主要是DAO层面。


只有新表结构和存储架构能完全适应查询改造,才能认为前面的设计暂时没有问题。


当然,这里还有个前提条件,就是相关查询已经全部收拢,没有遗漏。


2) 写查询的改造


除了相关字段的更改以外,更重要的是,需要改造为旧表、新表的双写模式。


这里可能涉及到具体业务写入逻辑,本项目尤为复杂,需要改造过程中与业务方充分沟通,保证写入逻辑正确。


可以在双写上各加一个配置开关,方便切换。如果双写中发现新库写入有问题,可以快速关闭。


同时,双写过程中不关闭 旧库到新库 的数据同步。


为什么呢?主要还是由于我们项目的特殊性。由于我们涉及到几十个服务,为了降低风险,必须分批上线。因此,存在比较麻烦的中间态,一部分服务是老逻辑,一部分服务是新逻辑,必须保证中间态的数据正确性,具体见4.5.1的分析。


4.2 服务化改造


为什么需要新建一个 服务来 承载改造后的查询呢?


一方面是为了改造能够方便的升级与回滚切换,另一方面是为了将查询收拢,作为一个中台化的服务来提供相应的查询能力。


将改造后的新的查询放在服务中,然后jar包中的原本查询,全部替换成这个服务的client调用。


同时,升级jar包版本到3.0.0-SNAPSHOT。


4.3 服务分批上线


为了降低风险,需要安排从非核心服务到核心服务的分批上线。


注意,分批上线过程中,由于写服务往往是核心服务,所以安排在后面。可能出现非核心的读服务上线了,这时候会有读新表、写旧表的中间状态。


1) 所有相关服务使用 重构分支 升级projectdb版本到3.0.0-SNAPSHOT并部署内网环

境;


2) 业务服务依赖于 中台服务,需要订阅服务


3) 开重构分支(不要与正常迭代分支合并),部署内网,内网预计测试两周以上

使用一个新的 重构分支 是为了在内网测试两周的时候,不影响业务正常迭代。每周更新的业务分支可以merge到重构分支上部署内网,然后外网使用业务分支merge到master上部署。


当然,如果从线上线下代码分支一致的角度,也可以重构分支和业务分支一起测试上线,对开发和测试的压力会较大。


4)分批上线过程中,如果碰到依赖冲突的问题,需要及时解决并及时更新到该文档中


5)服务上线前,必须要求业务开发或者测试,明确评估具体api和风险点,做好回归。


这里再次提醒,上线完成后,请不要漏掉离线的数据分析业务!请不要漏掉离线的数据分析业务!请不要漏掉离线的数据分析业务!


4.4 旧表下线流程


1)检查监控确保没有中台服务以外的其他服务访问旧库旧表


2)检查数据库上的sql审计,确保没有其他服务仍然读取旧表数据


3)停止数据同步


4)删除旧表


4.5 最佳实践


4.5.1 写完立即读可能读不到


在分批上线过程中,遇到了写完立即读可能读不到的情况。由于业务众多,我们采用了分批上线的方式降低风险,存在一部分应用已经升级,一部分应用尚未升级的情况。未升级的服务仍然往旧表写数据,而升级后的应用会从新表读数据,当延迟存在时,很多新写入的记录无法读到,对具体业务场景造成了比较严重的影响。


延迟的原因主要有两个


1)写服务还没有升级,还没有开始双写,还是写旧表,这时候会有读新表、写旧表的中间状态,新旧表存在同步延迟。


2)为了避免主库压力,新表数据是从旧表获取变更、然后反查旧表只读实例的数据进行同步的,主从库本身存在一定延迟。


解决方案一般有两种


1)数据同步改为双写逻辑。


2)在读接口做补偿,如果新表查不到,到旧表再查一次。


4.5.2 数据库中间件唯一ID替换自增主键(划重点,敲黑板)


由于分表后,继续使用单表的自增主键,会导致全局主键冲突。因此,需要使用分布式唯一ID来代替自增主键。各种算法网上比较多,本项目采用的是数据库自增sequence生成方式。


数据库自增sequence的分布式ID生成器,是一个依赖Mysql的存在, 它的基本原理是在Mysql中存入一个数值, 每有一台机器去获取ID的时候,都会在当前ID上累加一定的数量比如说2000, 然后把当前的值加上2000返回给服务器。这样每一台机器都可以继续重复此操作获得唯一id区间。


但是仅仅有全局唯一ID就大功告成了吗?显然不是,因为这里还会存在新旧表的id冲突问题。


因为服务比较多,为了降低风险需要分批上线。因此,存在一部分服务还是单写旧表的逻辑,一部分服务是双写的逻辑。


这样的状态中,旧表的id策略使用的是auto_increment。如果只有单向数据来往的话(旧表到新表),只需要给旧表的id预留一个区间段,sequence从一个较大的起始值开始就能避免冲突。


但该项目中,还有新表数据和旧表数据的双写,如果采用上述方案,较大的id写入到旧表,旧表的auto_increment将会被重置到该值,这样单旧表的服务产生的递增id的记录必然会出现冲突。


所以这里交换了双方的区间段,旧库从较大的auto_increment起始值开始,新表选择的id(也就是sequence的范围)从大于旧表的最大记录的id开始递增,小于旧表auto_increment即将设置的起始值,很好的避免了id冲突问题。


1)切换前:


sequence的起始id设置为当前旧表的自增id大小,然后旧表的自增id需要改大,预留一段区间,给旧表的自增id继续使用,防止未升级业务写入旧表的数据同步到新库后产生id冲突;


2)切换后


无需任何改造,断开数据同步即可


3)优点


只用一份代码;


切换可以使用开关进行,不用升级改造;


如果万一中途旧表的autoincrement被异常数据变大了,也不会造成什么问题。


4)缺点


如果旧表写失败了,新表写成功了,需要日志辅助处理


4.6 本章小结


完成旧表下线后,整个分库分表的改造就完成了。


在这个过程中,需要始终保持对线上业务的敬畏,仔细思考每个可能发生的问题,想好快速回滚方案(在三个阶段提到了projectdb的jar包版本迭代,从1.0.0-SNAPSHOT到3.0.0-SNAPSHOT,包含了每个阶段不同的变更,在不同阶段的分批上线的过程中,通过jar包版本的方式进行回滚,发挥了巨大作用),避免造成重大故障。


5.稳定性保障


这一章主要再次强调稳定性的保障手段。作为本次项目的重要目标之一,稳定性其实贯穿在整个项目周期内,基本上在上文各个环节都已经都有提到,每一个环节都要引起足够的重视,仔细设计和评估方案,做到心中有数,而不是靠天吃饭:


1)新表设计必须跟业务方充分沟通、保证review。


2)对于“数据同步”,必须有数据校验保障数据正确性,可能导致数据不正确的原因上文已经提到来很多,包括实时性、一致性的问题。保证数据正确是上线的大前提


3)每一阶段的变动,都必须做好快速回滚都预案。


4)上线过程,都以分批上线的形式,从非核心业务开始做试点,避免故障扩大。


5)监控告警要配置全面,出现问题及时收到告警,快速响应。不要忽略,很重要,有几次出现过数据的问题,都是通过告警及时发现和解决的。6)单测,业务功能测试等要充分


6.项目管理之跨团队协作


关于“跨团队协作”,本文专门拎出来作为一章。


因为在这样一个跨团队的大型项目改造过程中,科学的团队协作是保障整体项目按时、高质量完成的不可缺少的因素。


下面,分享几点心得与体会。


6.1 一切文档先行


团队协作最忌“空口无凭”。


无论是团队分工、进度安排或是任何需要多人协作的事情,都需要有一个文档记录,用于追踪进度,把控流程。


6.2 业务沟通与确认


所有的表结构改造,必须跟相关业务方沟通,对于可能存在的历史逻辑,进行全面梳理;


所有讨论确定后的字段改造,必须由每个服务的Owner进行确认。


6.3 责任到位


对于多团队多人次的合作项目,每个团队都应该明确一个对接人,由项目总负责人与团队唯一对接人沟通,明确团队完整进度和完成质量。


7.展望


其实,从全文的篇幅就能够看出,本次的分库分表项目由于复杂的业务逻辑改造,费大量的时间和精力,并且非常容易在改造过程中,引起不稳定的线上问题。


本文复盘了整个分库分表从拆分、设计、上线的整体过程,希望能对大家有所帮助。


看到这里,我们会想问一句。所以,有没有更好的方式呢?


也许,未来还是需要去结合业界新的数据库中间件技术,能够快速实现分库分表。


也许,未来还可以引入新的数据存储技术与方案(polardb、tidb、hbase),根本不再需要分库分表呢?


继续跟进新技术的发展,我相信会找到答案。

目录
相关文章
|
SQL 缓存 安全
Android ORM 框架之 greenDAO
Android ORM 框架之 greenDAO
1016 0
|
3月前
|
SQL Java 数据库连接
持久层框架MyBatisPlus
本课程系统讲解MyBatis-Plus(MP)的核心功能与实战应用,涵盖快速入门、条件构造器、Service接口、代码生成、分页插件等常用功能,结合Spring Boot实现CRUD操作与复杂查询,提升开发效率。
持久层框架MyBatisPlus
|
Arthas 测试技术 网络安全
The telnet port 3658 is used by process
是否在本地使用Arthas的时候,遇到The telnet port 3658 is used by process 34725 instead of target process 44848, you will connect to an unexpected process的异常,其实解决方法很简单。
2634 0
The telnet port 3658 is used by process
|
6月前
|
安全
一文搞懂synchronized锁的升级过程
synchronized锁的升级过程包括偏向锁、轻量锁和重量级锁。偏向锁在无竞争时可重复使用,轻量锁通过CAS自旋实现多线程竞争,重量级锁则会导致线程阻塞,涉及用户态到内核态的切换。CAS(比较并交换)用于实现乐观锁,保证原子性操作,但可能引发CPU资源浪费。文中还展示了手写锁的升级实现代码。
367 0
|
12月前
|
机器学习/深度学习 人工智能 自然语言处理
论文推荐:R1-Omni、VisualPRM、4D LangSplat、Vision-R1、GoT
简要介绍:由复旦大学、上海AI实验室等机构提出了首个统一多模态理解和生成的奖励模型UnifiedReward。该工作构建了大规模人类偏好数据集,包含图像和视频生成/理解任务,并利用该模型进行自动构建高质量偏好对数据,最终通过DPO优化视觉模型。实验结果表明,联合学习评估多样化视觉任务可以带来显著的相互益处。
425 1
|
存储 缓存 应用服务中间件
Nginx 响应头 Vary 的介绍与应用
`Vary` 头部字段在Web开发中扮演着重要角色,通过合理使用 `Vary`,可以优化缓存策略,提升Web应用的性能和响应速度。本文介绍了 `Vary` 头部字段的基本概念、作用、常见使用场景及其在Nginx中的配置方法。通过这些内容,希望读者能够更好地理解和应用 `Vary` 头部字段,提高Web应用的缓存效率和用户体验。
509 10
|
分布式计算 DataWorks 关系型数据库
DataWorks操作报错合集之出现了传参后提示有字段没映射上,但字段连线都已经正常连接的情况,该如何处理
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
存储 NoSQL 关系型数据库
面试官:别告诉我你管这个叫高可用
大家好。今天分享一篇写得很透彻的关于高可用的理解。以下是正文: 今天我们来聊一下互联网三高(高并发、高性能、高可用)中的高可用,看完本文相信能解开你关于高可用设计的大部分困惑
|
Kubernetes jenkins 持续交付
Kubernetes CI/CD 集成:持续交付的最佳实践
【8月更文第29天】随着微服务架构和容器化的普及,Kubernetes 成为了运行容器化应用的事实标准。为了确保应用能够快速迭代并稳定发布,持续集成/持续部署(CI/CD)流程变得至关重要。本文将介绍如何将 Kubernetes 集成到 CI/CD 流程中,并提供一些最佳实践。
944 1
|
监控
解决 浏览器访问kibana报错:Kibana server is not ready yet.
大家好,我是无名小歌!!!今天分享一个浏览器访问 Kibana 是出现的问题及解决方法。
2740 0
解决 浏览器访问kibana报错:Kibana server is not ready yet.

热门文章

最新文章