为什么大事务最伤复制
复制重放以事务为单位,单个事务无论多大都只能由一个 worker 执行,并行复制无法把它拆开。所以一次删几百万行、一次大 LOAD,在主库可能几分钟跑完,在从库却要单线程啃到分钟级延迟。表现是延迟阶梯式跳升、relay log 暴涨、从库锁等待升高,治理重心在业务侧拆批与限流。
基本信息
| 适用 | MySQL 5.7 / 8.0 主从复制与组复制 |
|---|---|
| 机制 | 事务不可分割,并行复制不能拆分单个事务 |
| 治理 | 拆成 1000-5000 行一批,控制批量窗口与并发 |
| 附带收益 | 缩短备份窗口、降低提交延迟与 undo 增长 |
现象与判断
| 现象 | 含义 | 下一步 |
|---|---|---|
| 延迟从秒级跳到分钟级 | 很可能正在重放单个大事务 | 从库看 SQL 线程状态与 innodb_trx 当前事务 |
| relay log 快速增长不回落 | 拉取快于重放,反压严重 | 限制批量窗口,或评估并行复制配置 |
| 从库锁等待升高 | 重放的行锁与业务查询冲突 | 错峰执行批量任务,或把批拆得更小 |
| 主库提交耗时变长 | 大事务占用 undo 与 redo,刷盘压力集中 | 拆批并控制单事务行数 |
| 备份期间写入停滞 | 大事务把备份窗口拉长 | 把批量任务移出备份窗口 |
批处理拆分示例
批处理拆分示例
-- 反例:一次删 500 万行,从库要单线程啃完
DELETE FROM logs WHERE created_at < '2026-01-01' LIMIT 5000000;
-- 正例:循环小批,每批 2000 行,直到影响行数为 0
DELETE FROM logs WHERE created_at < '2026-01-01' LIMIT 2000;
-- 外壳脚本里循环执行下面这句并判断 affected rows
SELECT COUNT(*) FROM logs WHERE created_at < '2026-01-01'; # 用剩余量判断进度
-- 批量导入同理:LOAD DATA 拆成多个小文件,或分批 COMMIT
-- 监控侧开盘中发现:SHOW REPLICA STATUS\G 观察 Seconds_Behind_Source 是否阶梯跳升典型使用场景
- 历史数据归档清理,从单条大 DELETE 改成循环小批
- 批量导入报表数据,按文件或行数分批提交
- 从库延迟告警复盘,定位到某个时间点的阶梯式跳升
- 大表加索引前评估写入影响,避免 DDL 与大事务叠加
- 组复制环境中控制单事务大小,避免触发大小限制被驱逐
大事务还会拉长主库提交、放大 undo 与 redo 增长,并挤占备份窗口;拆批的收益是全局的,不只是复制延迟。
大事务重放期间 Seconds_Behind_Source 可能长时间不更新,监控看起来没有延迟,实际重放要跑十几分钟,延迟告警要结合 relay log 大小一起判断。