
mysqldump 导出超大表会卡死或中断
导出 100GB 以上的表时,mysqldump 默认单线程、全表锁(加 --single-transaction 也挡不住长事务阻塞),容易触发超时、OOM 或网络断连。
实操建议:
- 改用
mydumper:支持多线程、行级快照、断点续传,命令如mydumper -h localhost -u root -B db1 -T large_table -o /backup/ - 避免全库导出,明确指定
-T table_name,否则元数据锁和 I/O 压力翻倍 - 导出前先查
SELECT COUNT(*)和SHOW TABLE STATUS LIKE 'large_table',确认实际行数和Data_length,别信“看起来不大” - 禁用
--skip-tz-utc(默认开启),否则跨时区恢复后时间字段错乱
直接 INSERT INTO ... SELECT 迁移卡主或主从延迟爆炸
在生产库上执行 INSERT INTO new_db.large_table SELECT * FROM old_db.large_table,会持续持有源表 MDL 锁、产生巨量 binlog,从库追不上,业务写入也可能被堵住。
实操建议:
- 停写迁移:如果业务允许短时停写,用
FLUSH TABLES WITH READ LOCK+mysqldump --no-create-info --skip-triggers最稳 - 分批迁移:用主键范围切分,例如
WHERE id BETWEEN 1 AND 1000000,每次控制在 5–10 万行,配合ORDER BY id防止重复或遗漏 - 目标库关掉 binlog:
SET sql_log_bin = 0再导入,避免二次复制放大延迟 - 别用
REPLACE INTO或INSERT IGNORE替代去重逻辑——它们仍会触发唯一键校验和自增 ID 跳变
分片后应用流量切换时连接池报 Unknown database
切流后部分请求还在打老库,或新库名/表名没同步到所有服务配置,连接池初始化失败,日志里反复刷 Unknown database 'old_shard_01'。
实操建议:
- 切流前必须验证 DNS 或中间件路由规则:用
mysql -h proxy-host -P 3307 -e "SELECT DATABASE();"手动连几次,确认返回的是新库名 - 应用侧禁用连接池的“自动重连”(如 HikariCP 的
connection-test-query设为空,automatic-test-on-return=false),否则故障掩盖真实路由问题 - 数据库代理层(如 ProxySQL、ShardingSphere)要配双写+读取权重,切流不是二进制开关,而是灰度调
weight从 100→0 分 3 轮,每轮观察SHOW PROCESSLIST和慢查日志 - 检查
init_connect配置:若老库有SET NAMES utf8mb4,新库未同步该变量,会导致字符集隐式转换失败
分片表主键冲突或自增 ID 重复
原单表 id INT AUTO_INCREMENT 拆成多个分片后,各分片继续用本地自增,很快出现相同 ID 插入不同库,关联查询或下游消费崩掉。
实操建议:
- 迁移前必须改主键:要么用
BIGINT UNSIGNED+ 雪花 ID(如twitter-snowflake算法生成),要么用shard_id + timestamp + seq拼接字符串主键 - 别依赖
auto_increment_offset和auto_increment_increment做分片——它们只对单实例有效,跨实例不保证全局唯一 - 已有数据需补全局唯一标识:用
UPDATE large_table SET global_id = CONCAT(shard_no, '_', id) WHERE global_id IS NULL,再建唯一索引 - 应用层插入前必须显式指定主键值,禁止留空让 MySQL 自动生成
分片迁移最耗神的从来不是导数据,而是确认每一处连接、每一个 SQL、每一条 binlog 是否真的走到了预期路径上。漏一个 init_connect、少一次 FLUSH PRIVILEGES、忘改一个 spring.datasource.url,就可能让流量在新旧库之间来回跳 ping-pong。










