把数据库从一台服务器搬到另一台、从一个机房搬到另一个机房、或者从自建搬上云,是运维绕不开的一类任务。迁移本身不难,难的是"怎么在不长时间停业务的前提下完成"。如果选择半夜停机、导出再导入,数据量小还好,数据量一大就是好几个小时,对 7×24 的业务来说往往不可接受。
减少停机时间的核心思路是:把大部分准备工作放在不停机时做完,把真正需要停机的窗口压缩到只剩最后一次切换。这篇文章讲清楚停机时间从哪来、怎么用全量加增量同步把准备阶段前置、切换的完整流程和检查清单,以及迁移里容易忽略的坑。文中命令基于常见 MySQL 5.7 与 8.0,工具只讲通用原理,不编造具体产品的功能细节。
停机时间主要来自哪里
先把停机来源拆开看,才能有针对性地压缩。
第一类是全量数据搬迁的时间。数据导出、网络传输、目标端导入,这三个环节加起来可能几小时甚至更久。数据越大、网络越慢,这块越致命。
第二类是数据校验的时间。搬完之后要确认数据一致,行数、校验和、抽样比对都要花时间。如果把它放在停机窗口里做,窗口会被拉长。
第三类是应用切换的时间。停止写入、等待复制追平、切换连接指向、验证新库可用、必要时回滚,这些动作虽然单步不长,但加起来也不可忽视,而且每一步都要求操作熟练。
第四类是意外和返工。迁移中遇到字符集不兼容、自增 ID 冲突、DDL 差异,需要停下来处理,时间就失控了。
要压缩停机,思路就是把第一、第二类尽量挪到"不停机阶段"完成,只把第三类的收尾动作留在停机窗口里。
核心方案:全量加增量同步
这是缩短停机时间最有效的手段。做法分两个阶段:先做一次全量同步把历史数据搬过去,再开启增量同步持续追平这段时间产生的新数据,让目标库和源库保持接近一致。等增量几乎追平时,只需要一个很短的窗口停止写入,等最后一点增量同步完,就可以切换。
全量阶段
全量阶段要解决的是"把存量数据搬过去"。可以先用 mysqldump 或物理备份做一次快照,导入目标库。同时记下这一刻的 binlog 位点(或 GTID 位置),作为增量同步的起点。
# 记录源库当前 binlog 位点,作为增量同步的起点
mysql -uroot -p -e "SHOW MASTER STATUS\G"
# 全量导出(示例,注意 --single-transaction 对 InnoDB 提供一致性快照)
mysqldump -uroot -p \
--single-transaction --master-data=2 \
--routines --triggers --events \
--default-character-set=utf8mb4 shop > /backup/shop_full.sql
# 导入目标库
mysql -uroot -p --default-character-set=utf8mb4 < /backup/shop_full.sql
加 --master-data=2 时,mysqldump 会把当时的 binlog 位点以注释形式写进文件,方便确定增量起点。--single-transaction 能保证 InnoDB 的一致性快照,但要注意它只对事务型引擎生效,MyISAM 表仍会锁表。
增量阶段
增量阶段把全量快照之后发生的变更持续同步到目标库。实现方式有两种常见思路:一是用 CDC(变更数据捕获)工具解析源库的 binlog,把变更事件回放到目标库;二是自己写程序消费 binlog。无论哪种,都要关注三件事:从正确的位置开始、保证幂等、以及监控延迟。
# 在目标库确认复制状态(如果使用 MySQL 原生复制作为过渡)
mysql -uroot -p -e "SHOW SLAVE STATUS\G"
# 关注两个线程是否运行,以及延迟指标
# Slave_IO_Running / Slave_SQL_Running 应为 Yes
# Seconds_Behind_Master 反映延迟(8.0 语义有变化,可结合 applier 状态)
增量同步期间,目标库会持续接近源库。但有一个前提:目标库在增量阶段不能被业务写入(否则会和同步来的数据冲突),它只能被动接收同步的数据。所以增量阶段目标库是"准从库"的角色。
双写方案
除了 binlog 同步,还有一种更彻底的方案是双写:应用层同时把写请求发给源库和目标库。好处是目标库始终有最新数据,切换时几乎无感;代价是应用要改造,要处理两边的失败和一致性,还要考虑双写期间"一边成功一边失败"怎么补偿。双写适合对停机极度敏感的核心系统,但复杂度比 binlog 同步高不少,一般不作为首选。
切换流程
当增量同步基本追平(延迟很小且稳定)后,就可以进入切换阶段。切换讲究的是"有序、可回滚、每步可验证"。
先切读,再切写
一个稳妥的顺序是先切换读流量,观察一段时间,再切换写流量。具体来说:停止对源库的写入(把业务切到只读或停写模式),等待目标库把剩余增量同步完,校验两边一致后,把读请求指向目标库,观察业务是否正常;确认无误后,再把写请求也指向目标库,完成切换。
先切读的好处是,即使目标库有问题,因为写还在源库、数据还在源库,回滚成本很低——把读指回源库即可。如果直接切写,万一目标库有隐藏问题,回滚就要把新写入的数据同步回源库,复杂得多。
保留回滚路径
整个切换过程中,源库要一直保持可用且数据完整,至少保留到确认目标库稳定运行一段时间之后。回滚路径要提前演练清楚:读怎么切回、写怎么切回、切换期间在目标库产生的新数据怎么处理。千万不要在切换前就清掉源库,那是把自己逼上绝路。
切换检查清单
把关键动作列成清单,逐项确认,能显著降低人为失误。
| 检查项 | 内容 |
|---|---|
| 数据一致性 | 行数、校验和、抽样比对均通过 |
| 增量延迟 | 延迟接近零且稳定 |
| 结构一致性 | 表、索引、触发器、存储过程、事件齐全 |
| 连接指向 | 应用/中间件连接串已准备好切换 |
| 回滚路径 | 源库可用、回滚步骤明确、已演练 |
| 监控告警 | 新库的监控和告警已配置并生效 |
| 权限账号 | 应用账号在新库存在且权限正确 |
容易忽略的坑
迁移里有些问题平时不显眼,一旦踩上就很麻烦。
字符集与排序规则
源库和目标库的字符集、排序规则必须一致,否则中文可能变乱码,或者排序结果不同。迁移前先查清楚。
-- 查看库、表、列的字符集
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
FROM information_schema.schemata WHERE schema_name = 'shop';
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.tables WHERE table_schema = 'shop';
SHOW CREATE TABLE shop.orders;
时区
时区不一致会导致时间戳数据出现偏移。要确认服务器时区、MySQL 的 time_zone 设置,以及应用写入时间的方式(是应用生成还是数据库 NOW() 生成)。
mysql -uroot -p -e "SELECT @@global.time_zone, @@session.time_zone, NOW();"
自增序列与主键
增量和全量并行期间,如果目标库也在自增,主键可能和同步来的数据冲突。通常做法是目标库相关表暂时关闭自增或设置足够大的起始值,切换后再恢复。
-- 查看自增值(8.0 用 information_schema,或 SHOW TABLE STATUS)
SHOW TABLE STATUS LIKE 'orders'\G
DDL 变更
迁移期间如果有在线 DDL(加索引、改字段),要特别小心。DDL 在 binlog 里的记录方式、目标库是否支持同样的操作,都可能引发问题。建议在迁移窗口内冻结 schema 变更,或者提前把 DDL 在目标库执行好。
触发器、事件与存储过程
这些对象很容易在只导数据时被漏掉。用 mysqldump 时记得带 --routines --triggers --events,迁移后要确认它们在目标库存在且逻辑正确。触发器如果两边都生效,还可能造成数据被重复处理,要留意。
连接与会话状态
切换连接指向时,应用连接池里的旧连接要优雅关闭,避免仍指向老库。建议切换后重启应用或让连接池快速回收,并确认新连接确实连到了目标库。
一致性校验怎么做
校验是切换前的最后一道关卡,不能省。校验分几个层次。
# 1. 行数比对(在源库和目标库分别执行,人工或脚本对比)
mysql -uroot -p -e "SELECT COUNT(*) FROM shop.orders;"
# 2. 校验和比对(注意对大表的开销,可在低峰或从库执行)
mysql -uroot -p -e "CHECKSUM TABLE shop.orders;"
# 3. 抽样比对关键业务数据
mysql -uroot -p -e "SELECT id, user_id, amount, created_at FROM shop.orders ORDER BY id DESC LIMIT 20;"
行数相等不代表内容一致,所以还要做校验和与抽样。校验和可以用 CHECKSUM TABLE(对 InnoDB 是逐行计算、会扫全表)或自己对关键列做聚合校验。比对时要注意两边执行的是完全相同的表达式和排序。
出于校验和比对的目的,在目标库上执行全表扫描可能带来额外负载,建议在业务低峰进行,必要时先扩容或使用从库。
常见问题
一定要停机吗?
如果允许短暂只读,通常只需要一个很短的窗口来停写、追平增量和切换;如果连短暂只读都不能接受,可以考虑双写方案,但复杂度和风险都更高。
增量同步会丢数据吗?
取决于工具和配置。用 binlog 解析类工具时,要确保从正确的位点开始、处理幂等、并监控延迟和错误。切换前一定要做一致性校验,不能只信"同步没有报错"。
全量和增量的顺序能颠倒吗?
不能。必须先有全量快照作为基线,再从该快照对应的 binlog 位点开始增量,否则会漏数据或重复。--master-data=2 记录位点就是为了这个。
切换后旧库要保留多久?
没有固定答案,但至少要保留到新库稳定运行、业务确认无异常、且有一份最新备份为止。保留期间旧库最好设为只读,避免两边都被写入导致数据分叉。
迁移期间能加索引或改表结构吗?
最好冻结。DDL 会影响 binlog 同步和目标库结构一致性,如果必须做,要单独评估并在两边协调执行。
总结
- 停机时间主要来自全量搬迁、数据校验、应用切换和意外返工,减少停机就是把前两项前置到不停机阶段。
- 核心方案是先做全量快照并记录 binlog 位点,再从该位点开始增量同步持续追平。
- 双写可以让目标库始终最新,但复杂度高,适合对停机极度敏感的核心系统。
- 切换顺序先切读再切写,源库全程保持可用,回滚路径要提前演练并保留到确认稳定。
- 字符集、时区、自增序列、DDL、触发器与存储过程是迁移中最容易忽略的坑,务必逐项检查。
- 切换前必须做行数、校验和与抽样比对,不能只依赖"同步无报错"。
企业QQ咨询




