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

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

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),根本不再需要分库分表呢?


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

目录
相关文章
|
8月前
|
SQL 关系型数据库 MySQL
MySQL优化方案
MySQL优化方案
305 9
MySQL优化方案
|
7月前
|
SQL 关系型数据库 MySQL
106分布式电商项目 - MySQL优化(查询优化)
106分布式电商项目 - MySQL优化(查询优化)
49 0
|
7月前
|
存储 关系型数据库 MySQL
108分布式电商项目 - MySQL优化(插入数据优化)
108分布式电商项目 - MySQL优化(插入数据优化)
32 0
|
7月前
|
关系型数据库 MySQL 数据库
107分布式电商项目 - MySQL优化(数据库结构优化)
107分布式电商项目 - MySQL优化(数据库结构优化)
45 0
|
7月前
|
算法 安全 Java
架构设计第十一讲:架构之高并发:限流
架构设计第十一讲:架构之高并发:限流
|
消息中间件 存储 中间件
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)
128 0
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(中)
|
SQL 关系型数据库
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(上)
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(上)
156 0
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式事务特别及问题分析(上)
|
存储 监控 算法
【高并发项目实战】自适应高并发复杂场景的订单拆分算法工具
​本篇文章带你打造一个自适应场景的交易订单合单拆分通用算法方案,根据现有技术的痛点,我们支付的时候设计一种自适应场景的交易下单合单拆分通用算法的方案,可插拔的场景组件提升扩展性和通用性就很重要。
|
存储 关系型数据库 MySQL
MySQL索引的测试 (千万级数据) 以及特点总结|周末学习
创建表 可以看到这里创建的索引类型都是 BTREE -- ---------------------------- -- Table structure for mall -- ---------------------------- DROP TABLE IF EXISTS `mall`; CREATE TABLE `mall` ( `id` int(11) NOT NULL AUTO_INCREMENT, `categoryId` int(11) NOT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_gen
241 0
|
存储 SQL 搜索推荐
亿级大表分库分表实战总结(万字干货,实战复盘)(一)
亿级大表分库分表实战总结(万字干货,实战复盘)(一)
287 0
亿级大表分库分表实战总结(万字干货,实战复盘)(一)