大表迁移时为什么不能直接用 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。
