四 技术方案
1 系统现状
积分系统主要2张核心的表,积分总表和积分明细表,目前2个库中的数据达百亿,积分明细日新增数据也在千万级。积分系统对外提供的服务接口主要有读积分、加积分、扣减(冻结积分)、退还积分。系统目前读积分会通过缓存tair做查询,击穿缓存才会到达数据库,写积分会直接更新数据库(缓存是为了临时解决大促期间系统性能瓶颈问题)。我们可以看到大促大瓶颈是在写积分对数据库的压力上,我们还有很多秒杀场景活动,由于对数据库叠加峰值太高,如果把数据库升级最高配置,一方面预算上花费太高,另一方面也不一定能支撑预估流量,即使这次侥幸度过,将来也无法再做水平扩展。基于这个考虑,重新设计了三个方案来解决:
- 考虑数据库性能,决定将积分系统由单库多表升级为分库分表(本次采取)
- 对数据库操作进行优化,减少加积分数据库操作
- 对业务优先级进行排序,部分场景降级,不写积分明细
2 技术细节点
数据库
- 确认源db和目标db是否都是mysql
- 由于是积分总表和明细表,所以从用户维度进行分表,此次分库分表是分8个库,1024张表
数据
- 数据迁移:分库分表肯定要涉及数据迁移,数据迁移必定涉及一个全量和增量的数据,如何保证数据不会重复,不会丢失,我们此次迁移数据是采用全量+增量任务进行同步数据,精卫(ps:阿里数据迁移工具,下文会多次出现该词)会在全量任务的时候相当于记录数据库的一个副本,然后增量的时候可以进行任务回溯,同时该迁移工具可以保证数据的唯一性(对于已经存在的记录可以进行更新操作)
程序
- 程序层面,因为涉及数据源切换,到时候肯定不能通过发布程序来切换数据源,所以预先在程序里面应该加载两个db的数据源,通过分布式配置预先设定开关,然后动态进行读写切换,所以我们需要在程序里面手动去指定双数据源,并且需要保证灰度过程中切换比例可以通过配置进行调控
- 在迁移数据的过程中特别要注意的是主键ID,在上面双写的方案中也提到过主键ID需要双写的时候手动的去指定,防止ID生成顺序错误
3 数据迁移&双写方案
简要描述一下整个流程:
- 线上库配置完成
- 精卫(阿里集团数据迁移工具)上新建2张表的全量任务
- 全量迁移完成后开启增量(自动回溯全量开始时间,消息多次消费会进行幂等)
- 全量数据校验,查看数据是否一致
- 改造代码预发测试(采集线上流量进行回放,多种case跑一下,切流开关等校验),没问题发布上线
- 再次全量进行校验&订正(数据追平)
- 打开双写(保证数据实时性)
- 低流量节点(凌晨过后)进行灰度切流userId%x,进行验证,逐步流量打开,持续观察
- 双写开关切到新库,保证只写新库,完成数据迁移方案
- 系统稳定运行一段时间,迁移&双写代码下线,老库进行资源释放
准备工作
1)配置工作
- 申请db资源 8个库
- 创建逻辑库,配置逻辑表
- 配置逻辑表路由算法
- 应用中配置好分库分表规则
- 多数据源配置(可以参考springBoot多数据源配置)
2)通过监控查看业务低峰时段
- 从监控找出业务低峰期,预估操作时段,由图我们可以看出业务低峰时间在2:00-5:00
切流代码编写(查)
- 对所有查询接口进行整理
- 对DAO层编写代理层xxProxyDAO.class
- 对读接口在代理层进行开关控制
- 根据userId后4位取模进行灰度,动态获取查询时用的数据源
- 注意:对于没有路由字段userId,需要进行代码改造
双写代码编写(增,删,改)
整体上分为下面4个步骤,通过配置动态进行切换,切换期间需要注意的问题如下:
- 对写新库操作需要进行日志埋点
- 新库不要求一定写成功(不影响服务,后期不一致数据通过增量任务兜底)
- 如果老库和新库都写,最终返回结果是老数据源
- 数据源回滚:开启了双写,新数据库中总积分值不对(新数据源回滚不了)
- 开启双写时机:由于已经开启增量,所以对于还没切流前不需要开启双写。在准备进行切库时,开启双写。为什么这里还需要开启双写?
- 考虑极限情况下,增量同步任务会出现延迟(理论上是秒级)
- 实时同步数据到新数据库中
数据迁移
数据迁移其实不是一蹴而就的,每一次数据迁移都需要一段漫长的时间,有可能是一周,有可能是1个月,通常来说我们迁移数据的过程基本如下图:
首先我们需要将我们数据库已经存在的数据进行批量的迁移,然后需要处理新增的这部分数据,需要实时的把这部分数据在写完原本的数据库之后然后写到我们的新的存储,在这一过程中我们需要不断的进行数据校验。当我们校验基本问题不大的时候,然后进行切流操作,直到完全切流之后,我们就可以不用再进行数据校验和增量数据迁移。
1)开启全量同步
- 本次全量使用的是精卫(ps:阿里数据同步工具,为了方便,外部也有很多类似的中间件,或者自己编写迁移脚本),它可以根据配置好的分库分表规则,自动将数据同步到相应的物理分表中
- 全量开始时间,选择gmt_modified作为条件字段
- 全量默认走备库,目标端写入的是主库,无论是全量还是校验都会对源端备库,目标端主库造成压力,所以这里需要注意一下,设置一个读的上线qps,以免对线上服务造成影响。
- 在迁移数据过程中,当写入发生冲突时,转换为update执行
- 注意一下数据迁移完成的时间,假设我们以80亿数据上,同时开启8个任务,每个任务上线tps为1w,计算公式如下:
- 数据迁移看板,实时查看迁移进度(ps:本工具是阿里内部的,外部也有很多类似开源系统)
2)开启增量同步
- 在INSERT时,出现主键冲突,精卫会将INSERT改成UPDATE事件。用户无需担心主键冲突产生异常。
- 异常处理机制:增量任务产生异常后,迁移工具默认会在同一机器上重试三次,若三次都失败后,会给出报警信息,并稍后换一台机器继续重试。
- 位点:消费位点是指当前已经成功消费的Binlog队列的位置,位点是一个 'yyyy-MM-dd HH:mm:ss' 格式的时间戳。
全量校验服务
下面使用的工具是阿里内部的,原理都类似,可以参考阿里对外开源的数据传输服务DTS。
- 全量校验服务和全量迁移服务类似,配置流程同上。校验服务执行完成,会在页面展示缺失和差异的数量。
- 验证源端和目标端的数据是否一致,也是全量订正服务必须的前置操作。
全量订正服务
- 通过订正服务可以将不一致的源端DB和目标端DB进行数据订正,保证一致性。
- 使用订正服务前必须进行校验服务。
进行校验
需要注意:校验任务注意不要影响线上运行的服务,通常校验任务会写很多批查询的语句,会出现批量扫表的情况,如果代码没有写好很容易导致数据库挂掉。
对账标准:target数据库和source数据库中数据保持一致(所有字段)
对账梳理:可以从积分总表和积分明细两个方面来处理
对账流程:通过定时任务轮询执行已经完成迁移的用户在新老库的数据一致性。需要注意的是由于读取新老库有先后顺序,所以产生瞬时的数据不一致,对于这种问题可以采用对账重试,只要保证最终一致即可。
1)抽样数据校验
按业务类型或者用户id,对最新增量数据进行抽样校验(下面校验工具为阿里内部工具,外部可以参考阿里数据传输服务DTS),然后需要对有问题数据进行订正
2)Odps离线数据校验
使用odps的小时表来进行对账。思路很简单,利用odps数据同步能力,离线数据的处理能力,加上动态脚本的编写快速实现多系统间对账。不需要进行应用的改造,稳定性上也有保障。对于实时性要求不高的场景,可以推荐尝试使用。
进行切流
- 当我们数据校验基本没有报错了之后,说明我们的迁移程序是比较稳定的了,那么我们就可以直接使用我们新的数据了吗?当然是不可以的,如果我们一把切换了,顺利的话当然是很好的,如果出现问题了,那么就会影响所有的用户。所以我们接下来就需要进行灰度,也就是切流。
- 本次切流方案是基于用户id取模的方式去进行切流,这个切流需要制定好一个切流计划,在什么时间段,放出多少的流量,并且切流的时候一定要选择流量比较少的时候进行切流,每一次切流都需要对日志做详细的观察,出现问题尽早修复,流量的一个放出过程是一个由慢到快的过程,比如最开始是以1%的量去不断叠加的,到后面的时候我们直接以10%,20%的量去快速放量。因为如果出现问题的话往往在小流量的时候就会发现,如果小流量没有问题那么后续就可以快速放量。
切换过程采用逐步放量的形式,灰度方式很多我们采用的是先白名单验证,然后用户ID取模10000逐步放量的方式。灰度切流验证:万分之1-1%-5%-10%-50%-100%切流
完成迁移
- 直到切流到100%,开启精卫新库到老库同步任务(以防万一出问题还能切回老库)。然后观察各个业务后续工单反馈情况和各个系统预警&日志;对新库进行性能压测,确保新库的稳定性
- 最后简单来总结下这个套路,其实就是四个步骤,一个注意:存量,增量,校验,切流