千寻

道路很长, 开始了就别停下!

0%

如何不停机完成单表拆分

在业务发展的早期,为了快速迭代及时响应市场需求,通常架构比较简单,比如数据库表,开始是单表,但随着业务的发展,数据量逐步膨胀,开始考虑分库分表。

本文以交易系统的订单表为例:

订单通常分为两张表:

  • 订单主表(order_main)
描述 字段名
订单id order_id
买家id buyer_id
卖家id seller_id
下单时间 create_order_time
付款时间 pay_time
发货时间 deliver_time
买家确认收货时间 confirm_receive_goods_time
订单金额 order_sum
订单状态 order_status
其它业务字段 。。。
  • 订单明细表(order_entry)
描述 字段名
明细id order_entry_id
订单id order_id
商品名称 product_name
商品id product_id
商品金额 product_price
购买数量 quantity
物流单id logistics_order_id
快照id snapshot_id
明细状态 entry_status
其它业务字段 。。。

注意点:

  • 订单主表与明细表一对多关系
  • 每时每刻都有新的数据进来,可能是新增订单,也可能是订单修改,业务方不接受停机迁移方案

问题:

  • 老order_main的订单id采用mysql自增方式,如果采用分表比如1024张,关于订单id肯定要提供单独的id生成器服务
  • 迁移到新表,采用新的订单id,老的订单id与新的订单id需要建立映射关系

核心步骤

一、双写

原来的创建订单接口上,增加新库的写入逻辑

过程:

  • 老库创建订单
  • 申请新的订单号(为了查库方便,新订单分表键采用订单号,订单号采用id序列号+buyer_userId后6位组成,理论上可最多分100W张表)
    • 这样分的好处,无论是按买家维度查订单列表,还是按订单号查询,都能快速定位到具体表
  • 在新库创建订单
  • 并建立新老order_id之间的映射关系

新订单,如果是修改操作,新、老表都同时修改,保证事务。

老订单,修改操作,只操作老表

二、全量数据迁移

  • 启动任务,开始全量迁移老表的数据,到新的表中,返回新的order_id
  • 并建立新老order_id之间的映射关系

注:

  • 老的订单要在老库完结。此时对老库表记录修改时,需要发kakfa,异步任务监听,同步数据到新表

三、读切到新库

  • 所有依赖于老的订单表的接口,增加中间层逻辑
  • 先根据老的订单号查新的订单号
  • 将流量切到新的订单服务接口

注意:

  • 上面的步聚2和3之间会有时间间隔,所以这段时间的订单修改记录要单独存储
  • 在步骤3完成后,对这些增量数据再做一次同步

四、下双写

  • 对老订单表的写依赖下线