分库分表后,怎么设计,可以降低数据迁移的难度?

#中间件#面试八股

分库分表后,怎么设计,可以降低数据迁移的难度?

结论:迁移难度在设计期就决定了,取决于三件事——分片键是否可变、分片空间是否预留、数据是否有全局 ID 或时间戳可用于追增量。落地用五阶段迁移:双写 → 全量加增量追平 → 数据校验 → 灰度切读 → 切写并下线旧库,每一阶段都要能一键回滚。

一、设计侧的五条规矩

设计决定好处反例
分片键不可变数据一辈子不因业务变更而搬库用手机号分片,换绑就要迁移
预留分片空间(1024)扩容只搬表,不改算法按 4 库写死,扩容必须重算
全局 ID 加时间戳增量同步可断点续传只有自增主键,无法判断新旧
逻辑表名抽象迁移只改路由配置表名硬编码在 SQL 字符串里
双写开关迁移期可随时回退直接停服割接

二、五阶段迁移

 ①双写           ②全量 + 增量       ③校验           ④切读          ⑤切写/下线
┌────────┐     ┌─────────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
│ 新老库  │ ──► │ DataX 全量   │──►│ 行数与金额│──►│ 灰度 1%  │──►│ 旧库只读  │
│ 同时写  │     │ binlog 追平  │   │ 摘要比对  │   │ 5%→50%   │   │ 最终下线  │
└────────┘     └─────────────┘   └──────────┘   └──────────┘   └──────────┘
      └──────────── 任一阶段异常,关掉双写开关即回滚 ────────────┘

每阶段停留多久取决于数据量与业务容忍度:全量同步按 10 MB/s 估算,一亿行大约 4 到 8 小时;binlog 追平后再做校验,校验通过才允许切读。

三、双写与开关

@Component
public class OrderWriter {
    private final OrderMapper newMapper;
    private final LegacyOrderMapper oldMapper;
    private final SwitchService switches;          // 配置中心下发

    public void save(Order order) {
        newMapper.insert(order);                   // 新库为主链路
        if (switches.isOn("order.dual.write")) {
            try {
                oldMapper.insert(order);           // 旧库旁路写
            } catch (Exception e) {
                log.warn("旧库双写失败 orderId={}", order.getId(), e);
                metrics.counter("dual.write.fail").increment();
            }
        }
    }
}

关键点:旧库写入失败绝不能影响主流程;缺口靠 binlog 补偿与校验任务修复,而不是靠重试把请求拖死。

四、校验 SQL

-- 按天比对行数与金额,差异超阈值即告警
SELECT DATE(create_time) AS d,
       COUNT(*)          AS cnt,
       SUM(amount)       AS amt
  FROM t_order
 WHERE create_time >= '2025-11-01' AND create_time < '2025-11-04'
 GROUP BY DATE(create_time);

-- 更严格的摘要比对:主键集合与关键字段做 CRC32
SELECT COUNT(*) AS cnt,
       SUM(CRC32(CONCAT_WS('#', id, user_id, amount))) AS digest
  FROM t_order
 WHERE create_time >= '2025-11-01' AND create_time < '2025-11-02';

新旧库各执行一次,cnt 与 digest 都相等才算通过;不等时按主键二分定位差异行。

五、常见追问

Q1:迁移期间的数据一致性怎么保证? 靠「全量快照 + binlog 增量」追平,再用校验任务发现差异并反查补偿。不要指望双写 100% 成功,网络抖动与超时必然留下缺口。

Q2:怎么回滚? 旧库保持可写、双写开关可关、路由配置可切回,三个条件同时满足才叫可回滚。切写完成后旧库至少保留一个业务周期的只读数据。

Q3:停机迁移可以吗? 数据量小、有维护窗口时可以,但要评估停服成本。互联网业务通常不接受,只能在线迁移。

Q4:历史冷数据也要迁吗? 不必。按时间归档到冷存储或对象存储,只迁活跃数据,迁移量能降一个数量级。

小结

  • 迁移难度在设计期就决定了:键不可变、空间预留、有全局 ID 与时间戳。
  • 五阶段迁移的核心是「双写 + 校验 + 灰度」,每一步都可回滚。
  • 校验要有量化指标(行数、金额摘要),不能只靠肉眼抽查。
笔记加载中…