大表迁移禁用 insert into ... select,因其锁表、耗内存、拖垮主库;推荐 mysqldump --where 分批导出或 mydumper 并行导出,并注意切分 chunk、控制线程数、避免一致性锁;导入时用 truncate 或 insert ignore 防重复,迁移后须校验数据。

大表迁移时为什么不能直接用 INSERT INTO ... SELECT
因为会锁表、占满内存、拖垮主库。MySQL 在执行 INSERT INTO t2 SELECT * FROM t1 时,如果 t1 是千万级大表,事务会长时间持有 MDL 锁,阻塞所有 DDL 和部分 DML;同时 binlog 写入压力剧增,从库容易延迟数小时甚至断连。
实操建议:
- 禁用该方式迁移单表 >500 万行的数据
- 如必须用,务必在业务低峰期 + 设置
innodb_lock_wait_timeout=30防死锁 - 提前在目标库关闭
autocommit,手动分段提交(但依然不推荐)
pt-online-schema-change 能否用于纯数据迁移
可以,但它本质是「在线改表」工具,不是迁移工具。它通过创建影子表 + 触发器同步增量,适合结构变更场景;若只迁数据不改结构,反而引入额外风险:触发器可能丢失更新、主从 GTID 不一致、触发器堆积导致主库 CPU 暴涨。
实操建议:
- 仅当需同步迁移 + 同时修改表结构(如加索引、改字段)时才启用
pt-online-schema-change - 纯数据迁移请用
mysqldump --where分批导出,或mydumper并行导出 - 使用前确认源库
binlog_format=ROW,否则触发器无法捕获变更
用 mydumper 导出大表要注意哪些参数
mydumper 是 C 实现的并行逻辑导出工具,比 mysqldump 快 3–5 倍,但默认行为对大表不友好:它会把整张表当一个 chunk 导出,内存占用高、失败后重跑成本大。
实操建议:
- 强制按主键切分:加上
--chunk-filesize=64(单位 MB)或--rows=100000控制每 chunk 行数 - 指定导出线程数:
-t 8(根据源库 CPU 核数设,别超 2×CPU 数) - 跳过统计信息避免卡住:
--no-views --no-triggers --no-sysinfo - 导出时加
--trx-consistency-only,不开启全局一致性快照(避免 FLUSH TABLES WITH READ LOCK)
导入时如何避免主键冲突和重复写入
大表迁移常跨实例、跨版本,目标库可能已有部分数据(比如历史归档),或迁移中断后重试,此时直接 myloader 会报 Duplicate entry 'xxx' for key 'PRIMARY'。
实操建议:
- 导入前先清空目标表(如果允许):
TRUNCATE TABLE t1,比DELETE FROM快且不记 binlog - 如需追加或去重,用
INSERT IGNORE或REPLACE INTO:需提前在myloader的 SQL 文件里批量替换INSERT INTO→INSERT IGNORE INTO - 更稳妥的做法:导入到临时表
t1_tmp,再用INSERT INTO t1 SELECT ... ON DUPLICATE KEY UPDATE合并 - 导入后立刻校验:
SELECT COUNT(*) FROM t1和CHECKSUM TABLE t1(注意 CHECKSUM 在 8.0.26+ 已弃用,可用SELECT MD5(GROUP_CONCAT(...))替代)
大表迁移真正难的不是命令怎么写,而是中间状态不可见——没日志、没进度条、不知道卡在哪一行。哪怕用了 mydumper,也得自己加 pv 或定时查 information_schema.PROCESSLIST 看当前正在 dump 哪个 chunk。










