分库分表后,生产环境如何实现不停机迁移数据
大纲
前言
假设,目前线上系统是单库单表(即未分库分表),单表已有约 600 万条数据。计划将数据迁移到 3 个库、每个库 4 张表的分库分表架构中,每张表存放约 50 万条数据。已经选定了分库分表的中间件(例如 Sharding-JDBC 或 Mycat),问题是:如何在不影响线上业务(即不停机)的情况下,将系统平滑迁移到新的分库分表架构上?
为什么要分库分表?
分库分表主要有两个原因,要不就是单库并发压力太高,要不就是单库数据量太大。
停机迁移方案
停机迁移数据
这里先介绍一种不常用的方案 - 停机迁移,相对比较简单、粗暴,技术含量不高。在网站或 App 提前发布公告,通知用户 0 点到 6 点期间系统停机维护。凌晨 0 点开始运维,到点后先停机,系统停止服务,不再有新的流量写入,此时旧的单库单表数据库保持静止。接着运行事先准备好的一次性数据迁移工具,把单库单表中的数据批量导出,并按照分库分表规则导入到新库中。数据导入完成后,更新系统的数据库连接配置,同时上线新版本代码(因为分库分表后 SQL 或数据访问逻辑可能有改动)。最后启动系统,连接新的分库分表架构,进行业务验证。如果业务验证通过,就算迁移完成。不过,这种 “停机迁移” 的方案相对比较简单、粗暴,技术含量不高。

停机迁移方案通常包括以下几个核心步骤:
- (1) 读取数据
- 按分页(
limit/offset或者主键范围)从旧表里分批读取数据,避免一次性读 600 万导致 OOM 或锁表。 - 典型做法:
- 根据主键(比如自增 ID)按范围扫描:
select * from old_table where id between x and y - 每次取 1w ~ 5w 条,批量处理。
- 根据主键(比如自增 ID)按范围扫描:
- 按分页(
- (2) 路由规则计算(分库分表规则映射)
- 根据预设的分库分表规则(比如
user_id % 6→ 定位到某个库某个表)。 - 数据迁移工具对每一条数据计算:
- 属于哪个分库
- 属于哪个分表
- 举个例子:
1
2
3
4user_id = 1025
库号 = user_id % 3 (0 ~ 2)
表号 = user_id % 4 (0 ~ 3)
最终落点 = db_2.user_table_1
- 根据预设的分库分表规则(比如
- (3) 写入新库
- 批量插入目标分表(
batch insert),减少网络往返。 - 写入前可能需要做:
- 字段映射(比如旧表字段和新表字段不完全一致)
- 数据清洗(处理非法数据、空值等)
- 主键处理(旧表用自增 ID,新表可能使用雪花 ID 或 UUID,需要重新生成)
- 批量插入目标分表(
- (4) 数据校验
- 迁移完一批数据后,进行校验:
- 数量校验:源表 10 万条数据,目标表分散后总和也应该是 10 万条数据。
- 抽样校验:随机抽几条数据,对比新旧库的字段值是否一致。
- 全量校验(可选):做 MD5 校验,或者比对哈希值。
- 迁移完一批数据后,进行校验:
- (5) 日志与容错
- 每批迁移要有日志,记录成功或者失败。
- 失败的数据要单独写入 “重试队列”。
- 支持断点续传(例如数据迁移中断,可以从上次 ID 继续跑)。
- (6) 收尾工作
- 所有数据迁移完毕,全量校验通过。
- 更新系统的数据库连接配置,让系统指向新的分库分表架构。
- 上线新版本代码(因为分库分表后 SQL 或数据访问逻辑可能有调整)。
- 重启服务,进行业务验证,确认无误后切换工作正式完成。
不停机迁移方案
双写迁移数据
双写迁移是目前最常用且较为稳妥的一种迁移方式,优点是不需要停机,也不用熬夜等凌晨运维窗口。核心思路:在系统中对所有写库操作(增、删、改)进行改造,不仅写入旧库,同时也写入新库,即所谓的 “双写”。这样可以保证在迁移期间,新库持续写入最新数据。在此基础上,通常需要配合以下机制:
数据同步:迁移启动前,先通过全量复制将旧库的历史数据导入新库,再通过 Binlog 或数据同步程序保证新旧库实时增量同步。这样新库能够与旧库保持一致性,不会出现 “有新数据但未同步” 的情况。数据比对:迁移过程中,需要定期进行新旧库的数据校验(例如按主键范围抽样比对行数、字段值校验、聚合校验等),确保两边数据在数量和内容上保持一致,避免出现漏同步、同步延迟或数据不一致的问题。补偿机制:若比对过程中发现差异,可通过重放 Binlog、补偿 SQL 或自动修复任务进行数据回补,保证最终一致性。补偿一般在后台异步执行,不影响业务正常运行。

双写迁移方案通常包括以下几个核心步骤:
- (1) 数据实时同步
- 首先开发并部署一个数据同步程序,将旧库的历史数据全量复制到新库中。由于旧库仍然在对外提供写服务(例如订单持续写入),同步程序还需要实时捕获旧库的增量变更并写入新库。核心思路是 “全量复制 + 增量实时同步”。
- 数据实时同步的常见做法是基于 MySQL Binlog 实时同步,通过解析 Binlog 日志捕获
INSERT/UPDATE/DELETE操作,再写入新库,从而保证新旧库数据基本保持一致。 - 写入新库时,可以通过
gmt_modified等时间戳字段进行判断:若新库不存在该数据,则直接插入;若新库已有该数据,但旧库的版本更新(修改时间更晚),则覆盖写入。 - 这一过程对旧库和业务系统无侵入性,不需修改应用代码,也无需回滚,仅需额外部署新库和同步程序;即使同步程序对旧库产生影响,也可随时停掉,不影响线上业务。
- (2) 双写改造与部署
- 在业务系统中改造写操作,使所有增删改操作同时作用于旧库和新库。常见方案包括:
数据库中间件拦截:利用 ShardingSphere、MyCAT 等支持双写或路由功能的中间件,在代理层统一拦截写操作,实现业务层零改动即可完成双写。但需关注复杂 SQL 与事务支持情况,确保中间件稳定可靠。ORM/AOP 层拦截:在 ORM 框架或 AOP 拦截器层统一实现双写逻辑,例如通过 MyBatis 插件 + 多数据源实现。业务层透明感知数据库操作即可完成双写,同时可顺带处理幂等、异常重试和事务顺序控制,前提是系统使用统一的 ORM 框架。Binlog 增量同步:也可继续使用 Binlog 方式(Canal、Maxwell、Debezium 等)捕获旧库变更并实时写入新库,这种方式对业务无侵入,适合大数据量场景,但需保证 Binlog、网络和同步工具的稳定性,并结合全量导入和校验机制保障数据完整性。
- 上线部署系统完成双写改造的新版本代码,然后停掉前期的数据同步程序,确保新写入数据由业务逻辑直接写入新库。
- 在业务系统中改造写操作,使所有增删改操作同时作用于旧库和新库。常见方案包括:
- (3) 数据比对与补偿
- 由于双写策略也不保证新旧库的强一致性,此时需要上线一个数据比对与补偿程序。
- 数据比对与补偿程序会对比旧库近期的数据变更,逐表逐行校验新旧库的数据是否一致。
- 若发现不一致的数据,则从旧库读取并写入新库进行补偿。
- 该过程可能需要多轮循环,直至新旧库数据完全一致。
- (4) 切换到新库
- 当确认新旧库数据完全一致后,可以停掉数据比对和补偿程序。
- 上线部署仅依赖分库分表、去掉双写逻辑的新版本代码。
- 重新部署系统,此时所有读写流量切换到新库,旧库逐步进入下线流程。
双写迁移方案需要注意以下地方:
- 事务一致性
- 旧库与新库之间无法保证强一致事务,必须容忍 “临时不一致”。
- 遇到异常时,至少要保证旧库优先成功,新库可以重试补偿。
- 异常与补偿机制
- 双写失败时(例如新库宕机),要记录失败日志或写入消息队列,后续重试补偿。
- 可设计一个数据补偿任务(定时比对旧库与新库的数据),保证最终一致性。
- 顺序与幂等
- 确保双写执行顺序与业务一致(例如先
INSERT再UPDATE),避免错序写入。 - 所有操作必须支持幂等,防止重试引起脏数据。
- 建议使用幂等写入,比如 MySQL 的
INSERT ... ON DUPLICATE KEY UPDATE。
- 确保双写执行顺序与业务一致(例如先
- 更新操作特殊处理
- 新库数据可能不存在,直接执行
UPDATE可能更新不到任何行。 - 建议采用 “存在则更新,否则插入” 的更新策略,例如 MySQL 的
INSERT ... ON DUPLICATE KEY UPDATE。
- 新库数据可能不存在,直接执行
- 全量迁移与增量校验
- 在正式双写之前,最好先做一次全量迁移(减少新库缺少数据的概率)。
- 启用双写后,后台跑比对和补偿任务,抽查或全量校验新旧库数据,自动补偿不一致的数据。
这种方案在迁移过程中业务不会中断,用户几乎无感知,可靠性高。因此,目前大多数公司在迁移生产系统的数据时,都会采用这种 “双写 + 校验” 的方案。
扩展知识内容
数据库在线迁移工具
主流的数据库在线迁移工具有以下几种,支持迁移在线业务,通常依赖 “全量复制 + 增量实时同步 + 切换” 的方案。
(1) MySQL 原生工具
- MySQL Replication (主从复制)
- 概述:
- 将新库作为旧库的从库,开启基于 Binlog 的主从复制。通常先做全量数据同步,再实时同步增量数据,追平后切换主库。
- 优点:
- MySQL 自带、成熟稳定、社区支持广、对业务代码零侵入。
- 基于 Binlog,增量数据实时同步,保证数据一致性。
- 缺点:
- 跨机房或网络延迟大时,复制延迟问题明显。
- 切换为主库需要停写或短暂停机(秒级)。
- 不适用于大规模 DDL 变更或 MySQL 版本跨度过大的迁移。
- 概述:
- MySQL Group Replication / InnoDB Cluster
- 概述:
- 基于 MySQL 官方的高可用集群,支持多主、多从、动态扩容。
- 严格来说,这不是专门的 “在线迁移工具”,本质上是 MySQL 的高可用方案,只是可以顺便解决迁移问题。
- 优点:
- 支持多主写入、自动故障转移。
- 支持自动处理节点加入 / 离开,迁移过程中可降低人为操作风险。
- 缺点:
- 部署复杂,对网络延迟和数据库版本有严格要求。
- 多主模式下存在冲突处理问题。
- 性能相对普通主从复制略差。
- 跨机房支持不理想。
- 概述:
- MySQL Replication (主从复制)
(2) 企业常用迁移工具
- Percona XtraBackup
- 概述:
- 用于 MySQL 的热备份,全量复制旧库数据到新库,然后结合 Binlog 或其他增量方案追平。
- 优点:
- 支持热备份,不锁表。
- 备份速度快,适合大数据量(TB 级别)场景。
- 一致性好,可与 MySQL 主从复制结合实现无缝切换。
- 开源、社区成熟,企业使用广泛。
- 缺点:
- 只解决全量迁移问题,增量追平仍需依赖 Binlog 或其他方案;
- 对磁盘空间和 I/O 有较高要求。
- 概述:
- pt-table-sync(Percona Toolkit)
- 概述:
- 用于全量数据校验、增量数据同步和比对,基于对比源库和目标库数据差异,按需补齐。
- 优点:
- 支持在线比对数据差异,自动修复,保证一致性。
- 支持断点续传,适合长时间迁移。
- 可用作双写迁移、切换后的数据一致性保障。
- 可以作为 Binlog 同步之后的校验工具。
- 缺点:
- 性能开销大,对源库影响明显,尤其是大表,适合流量低峰期运行。
- 仅适合 MySQL,且在大表场景下校验速度慢。
- 配置和使用复杂,需要 DBA 熟悉其工作原理。
- 概述:
- Percona XtraBackup
(3) 增量同步中间件
- Canal(阿里开源)
- 基于 Binlog 订阅,支持异构同步(MySQL → MySQL / ElasticSearch / Kafka)。
- Debezium(国外开源)
- 基于 CDC(Change Data Capture),是一个开源的分布式平台,支持多种数据库实时同步。
- Otter(阿里开源)
- 基于 Canal + 分布式调度,适合分库分表场景。
- DTS(阿里云数据库传输服务) / DMS(腾讯云数据库传输服务)
- 主流云厂商的在线迁移工具,支持断点续传、实时增量同步。
- Canal(阿里开源)
数据库在线迁移工具的最佳实践
- 自建环境推荐使用 XtraBackup(全量复制) + MySQL Replication(增量追平) + Canal(增量消费、双写)
- 云环境推荐使用云厂商的 DTS / DMS,直接提供 “全量 + 增量 + 切换” 的托管能力。
数据库在线迁移数据的典型步骤如下:
- (1) 新增 MySQL 服务器,创建好库表结构(与现有表结构保持一致)。
- (2) 全量迁移历史数据(XtraBackup / 全量 Dump)。
- (3) 开启 Binlog 增量订阅(Canal / DTS / MySQL 原生主从复制)。
- (4) 实时同步增量数据,并进行数据校验。
- (5) 业务切换流量到新库(通过中间件配置或 DNS / 服务发现)。
- (6) 平滑下线旧库。
