背景

最近在进行业务上的服务化重构,并对其中一块业务进行了优化调整。这里将数据迁移的过程记录下来并进行了简单的整理。

业务背景

优化调整的是素材分组业务,在业务关系上,店铺->素材分组->素材的三层一对多结构,即每一个拥有者(店铺)可拥有多个不同类型的素材分组,每个素材分组下可挂靠多个素材。

素材分组功能有很深的历史债务,且随着业务的推进,原有的分表设计无法满足业务需求,所以在服务化过程中,对分组相关模块做了技术改造和优化。

历史坑点

  • 分组概念中存在默认分组,且所有店铺共有默认分组,每次查询、移动、删除分组数据时需要实时group by重新计算,给数据库带来较大的慢查压力;
  • 视频类素材和图片音频素材的分组概念一致,但却单独实现业务逻辑和数据模型,同时维护两套分组方案不利于后续业务扩展;
  • 旧分组表使用归属者的店铺id作为数据表分片字段,无法支持非店铺类型的业务方接入素材分组管理服务;

优化方案

  • 引入内部id(partnerId)取代原有的数据表分片字段,支持更多维度的业务方接入,作为通用共享服务能力做铺垫;
  • 将默认分组作为一个实际存在的分组记录,初始化时生成默认分组记录,分配单独的分组id,使用类型标识和普通分组做区分判断;
  • 将各种类型(图片、音频、视频)的素材分组进行整合,共用一套分组关系的领域模型,涉及分组id使用新发号器重新发号处理;
  • 拆分不属于分组关系领域的字段(如素材大小),整合到统计领域做分组下素材的增量统计,从根本上消除慢查;

下面两张图简述了分组结构上的调整变化(不包含发号器和分组下素材统计部分):

  • 当前现状
    category_migrate_before

  • 规划方案
    category_migrate_after

分组数据平滑迁移

数据表结构设计

对分组的优化将需要新的数据表结构来支撑,我们需要将原有分组表的数据迁移到新表。
涉及迁移的表包括分组表和分组下素材关联关系表。

  • 分组表数据量较小(百万级别),主要改动面为单表拆分为分片表,以及分组id重新发号;
  • 分组关系表数据量较大(10亿级别),主要改动面为分片键和分组id变更,素材id保持不变;
    下图为新旧表的结构(部分字段名和注释已重命名和脱敏处理):
    mysql_table_migrated

数据迁移方案设计

因为迁移周期会比较长,在数据迁移期间,需要确保现有服务正常运行及迁移后数据的一致性,所以需要确认一下数据平滑迁移方案。

由于在迁移过程需要保持服务,离线迁移不在考虑范围内。日志追加法受限于重新分配发号的原因难以记录映射关系,而常规的双写方案稍加改造是能适用于这次的数据迁移场景的。所以我们使用带切换标记的双写法进行数据迁移。

双写数据迁移

数据迁移分为存量数据迁移和增量数据迁移两个部分,存量数据整理比较容易,主要精力集中在增量数据的处理方式上。

1,维护迁移分组映射关系

追踪源表分组和关联关系的增删改比较困难,但是创建一个临时表用以维护新旧表的分组映射关系相对比较容易:

字段 说明
old_category_id 老分组id
new_category_id 新分组id
partner_id 合作方id

有了映射关系,在双写时就可以针对旧分组的操作,找到对应的新分组进行双写

2,迁移粒度细化

对整个表的存量数据刷入和增量数据维护的成本较大,但是在业务上分组是有归属者的(旧表归属于某个店铺id,新表扩充到归属于某个合作方id)。所以再创建一个临时表用以维护迁移进度,将迁移粒度细化到每个合作方id/店铺id:

字段 说明
shop_id 店铺id
partner_id 合作方id
status 迁移状态(双写开关)
3,数据迁移和双写切流开关

所以数据迁移的主体过程是,按照每个店铺id/合作方id,依次对该店铺下所拥有的分组数据进行迁移,在迁移时调用发号器对分组id重新发号并生成对应的分组映射关系记录。在迁移完成并完成一致性校验后,将该店铺/合作方的迁移进度记录的状态标记为”完成”。这里的”迁移状态”作为双写的切流开关,具体表现为:

在分组和分组关系的写逻辑上,对所有已完成迁移的店铺和新增的店铺,启用双写逻辑(确保增量数据后续一致),未完成的跳过双写逻辑(只写旧表,因为未迁移完对增量数据无需关注)。

分组和分组关系的读逻辑仍然读旧表,以确保现有业务的不受影响。

数据一致性校验

数据检查

在数据迁移过程中,每个拥有者(店铺/合作方)迁移完成后,会进行数据一致性校验。这是由于迁移的过程中仍然有写操作的流量进来,正在迁移的部分老分组和关系数据还有可能发生变化。所以每个迁移完成的店铺,会有至少一次的数据一致性校验。

数据一致性校验的对比方式由具体业务决定,尽量统计多个维度来确保数据的正确性以及发现数据迁移是否有被忽视的问题。

如果一切顺利,数据迁移任务完成后一致性检查通过,即可将状态标识为迁移完成,此时该店铺的分组写操作将启用双写逻辑(先按现有逻辑写旧表,再根据映射关系写新表);

如果比对不一致,则可能迁移时源数据有变更,需要进行数据订正,并在再次重试数据检查操作。如果变更频繁可能会失败多次,这时可以延后重试或者在凌晨等流量低峰期进行重试操作。

数据修正

数据修正方式有很多,比如重做迁移期间操作记录进行补偿,或者清空该店铺/合作方的迁移数据并重做迁移。由于读写都走的旧数据表,清空重做迁移对现有业务无影响,且实际业务场景下出现的频率不高,这里直接使用清空并重做的方式进行数据修正。

读逻辑切换和表下线

当所有的店铺迁移完成且校验一致后,此时新旧表的记录已经按照预订的映射关系保持一致,且写操作双写也已经全量,读操作仍然在读取旧数据表。
接下来持续观察一段时间(一周左右),并可多次执行数据检查任务,待观察期后新表数据依旧稳定正确,即可将读逻辑切换到读新表。

读取逻辑回归无问题后,再移除双写中旧表、分组映射关系表和迁移进度表的读写逻辑,并下线对应的数据表。

至此,通过双写进行的数据平滑迁移完成。回顾下整个过程的大致流程如下:
migrated_BPMN

实际上,数据迁移双写的流程归纳总结起来都大同小异,我们需要关注的是根据业务实际需求,做一些最贴合业务场景的调整。