← 返回题目列表

分库分表后如何做平滑扩容和数据迁移?

高频 困难 第 14 / 27 题 更新于 2026/07/28
平滑扩容数据迁移双写数据校验

简化版

平滑扩容一般按“新建分片、全量迁移、增量同步、数据校验、灰度切流、回滚预案、清理旧数据”的流程做。关键是迁移期间保证新旧数据一致、路由可灰度、异常可回滚,不能简单停机改取模规则。

详细版

常见扩容流程:

  1. 设计新路由规则和分片映射,准备新库新表;
  2. 全量迁移历史数据,控制批次和限速;
  3. 增量同步迁移期间的新写入,可用 binlog、消息或双写;
  4. 做数据校验,包括行数、校验和、抽样、业务一致性校验;
  5. 灰度切读,观察延迟、错误率、数据差异;
  6. 切写或切主路由,保留回滚窗口;
  7. 稳定后下线旧路由和旧数据。

如果一开始做了逻辑分片和预分片,扩容只需要移动部分逻辑分片,成本会低很多。没有预留时,直接从 mod 64 改成 mod 128 会导致大量数据重分布,风险很高。

完整版教学

一、扩容难在“数据在变”

如果数据库是静止的,扩容很简单:把数据复制到新库,改路由,结束。但线上系统一直有读写流量。你迁移上午 10 点之前的数据时,10 点之后还有新订单、新支付、新状态变更不断写入。如果只做一次全量复制,新库一定会落后。

所以平滑扩容的核心是处理两个部分:历史存量数据和迁移期间的增量数据。全量迁移负责把大部分旧数据搬过去,增量同步负责追上迁移过程中发生的新变化。

二、不要直接修改取模规则

很多事故来自简单粗暴地把:

shard = user_id % 64

改成:

shard = user_id % 128

看起来只是分片数变了,实际上大部分用户的路由结果都会改变。旧数据还在旧分片,请求却被路由到新分片,自然会查不到。正确做法通常是引入逻辑分片或映射表,让路由规则具备迁移过程中的兼容能力。

例如固定 user_id % 1024 得到逻辑分片,再用映射表决定逻辑分片在哪个物理库。扩容时只把部分逻辑分片从旧库搬到新库,应用层逻辑分片规则不变。

三、典型迁移流程要有阶段感

平滑扩容可以拆成几个阶段:

  1. 准备阶段:创建新库表、索引、权限、监控、备份;
  2. 全量阶段:按主键范围分批搬历史数据,避免大事务和长锁;
  3. 增量阶段:通过 binlog、消息或双写同步迁移期间变化;
  4. 校验阶段:比较行数、关键字段校验和、抽样详情;
  5. 灰度阶段:少量流量读新库,对比新旧返回;
  6. 切换阶段:逐步把路由切到新分片;
  7. 收尾阶段:观察稳定后清理旧数据和临时同步链路。

每个阶段都要可暂停、可重试。迁移任务不能因为一条脏数据就整体崩掉,也不能无限制压垮线上库。

四、双写和增量同步各有坑

双写是应用同时写旧库和新库。它直观,但一致性难做:旧库写成功、新库写失败怎么办?重试会不会重复?两边写入顺序不一致怎么办?所以双写必须配合幂等键、重试队列、失败补偿和监控告警。

基于 binlog 的增量同步对业务侵入小,但要处理延迟、乱序、DDL、回放幂等和断点续传。同步工具要记录位点,失败后能从位点继续,不然会漏数据或重复应用。

灰度读也很关键。可以让一小部分请求读新库,同时异步对比旧库结果。如果差异率超过阈值,立即回滚读路由,继续排查同步问题。

五、扩容前的预设计能省很多痛苦

最好的扩容不是临时救火,而是在第一次分库分表时就考虑未来。常见预设计包括:

  • 逻辑分片数大于物理分片数;
  • 路由由配置中心或元数据表控制;
  • 订单号、用户 ID 中保留可选分片信息;
  • 每个分片有容量水位、热点监控和迁移工具;
  • 迁移任务具备限速、断点续传、校验和回滚能力。

这些设计短期看增加复杂度,长期看能把扩容从“开膛手术”变成“搬几组逻辑分片”。

六、迁移期间的读写策略要提前定清楚

平滑扩容最容易翻车的地方是迁移期间读写双轨。常见策略有旧写旧读、新旧双写、读旧写双、读新写新、灰度读新等。每种策略都要回答异常场景:双写时新库失败怎么办;读新发现没有数据要不要回源旧库;旧库和新库数据不一致以谁为准;回滚时新库新增数据如何处理。

比较稳妥的流程通常是先全量搬迁,再增量追平,然后灰度读新并做新旧结果比对,确认无明显差异后切写。切写后仍保留旧库一段时间,用于回滚和校验。不要在没有校验、没有回滚窗口的情况下直接切掉旧链路。

迁移任务本身也要限速。全量扫描会占用 IO、Buffer Pool、网络和从库复制能力。生产迁移一般按主键范围分批,每批控制大小和间隔,并监控线上延迟、慢查询、复制延迟。一旦影响主链路,要能暂停和续跑。

七、数据校验不能只看行数

行数相等只能说明大概率没少太多数据,不能证明数据正确。更完整的校验包括主键范围校验、关键字段校验和、金额类字段汇总、状态分布对比、更新时间最大值、抽样详情对比。订单、支付、库存这类数据,还要做业务语义校验,比如支付成功订单是否都有支付流水。

校验也要分阶段:全量迁移后校验一次,增量追平后校验一次,灰度读期间持续对比,新路由切换后继续对账。发现差异时要能定位差异来源:是全量漏搬、增量漏订阅、双写失败、消息乱序,还是业务本身有历史脏数据。

面试里如果能说出“全量、增量、校验、灰度、切换、回滚”还不够,再补上“限速、断点续传、幂等写入、差异修复、旧链路保留窗口”,就能体现你真的理解在线迁移的风险控制。

八、常见误区与追问

这道题要紧扣「在线扩容迁移」本身回答,不能把它混成泛泛的分库分表套话。面试官通常不是只听定义,而是看你能不能把适用场景、关键流程、失败边界和工程取舍串起来,尤其要说明拆分边界、路由规则、扩容迁移、跨库查询和一致性兜底。

回答层次要讲清的内容容易漏掉的边界
核心结论在线扩容通常采用双写、存量迁移、增量同步、校验切流和回滚预案,避免停机搬库不要停在名词解释
流程机制冻结迁移方案和路由版本 -> 存量数据分批复制 -> 增量写入双写或订阅同步 -> 校验新旧数据一致 -> 灰度切读再切写 -> 保留回滚窗口要说清触发点、状态变化、确认点和失败兜底
工程取舍从 8 库扩到 16 库时不能一次性改路由,否则历史数据仍在旧库,新请求会查不到分库分表能扩展容量和吞吐,但会带来路由、事务、Join、唯一约束、扩容和运维复杂度
在线扩容迁移 面试拆解:
1. 冻结迁移方案和路由版本
2. 存量数据分批复制
3. 增量写入双写或订阅同步
4. 校验新旧数据一致
5. 灰度切读再切写
6. 保留回滚窗口

记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「在线扩容迁移」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。

  • 误区:扩容就是改取模分片数。 取模变化会改变大量 key 的归属,必须迁移历史数据和处理增量。
  • 误区:停写才能迁移。 在线迁移可用双写、binlog 或消息同步增量,但复杂度更高。
  • 误区:校验行数一致就够了。 还要校验关键字段、哈希摘要、索引和业务可读性。
  • 追问:双写失败怎么办? 记录失败日志和补偿队列,必要时读旧库兜底。
  • 追问:什么时候切流? 存量完成、增量追平、校验通过、监控稳定后灰度切换。
  • 追问:如何回滚? 保留旧路由、旧数据和反向同步窗口,切换失败可回到旧链路。

九、加强记忆

平滑扩容记住“全量搬旧、增量追新、校验确认、灰度切流、随时回滚”。分库分表扩容最怕只改路由不搬数据,也怕只搬全量不管增量。真正可靠的扩容一定同时设计数据迁移、路由切换、数据校验和回滚预案。