根本原因是主库批量写入生成的binlog事件在从库必须串行回放,而row格式使每行变更都记为独立event,放大relay log体积与sql线程解析开销;即使开启并行复制,单个大事务内部仍强制串行执行。

批量插入导致从库延迟的核心机制
根本原因不是“插入慢”,而是主库批量写入生成的 binlog 事件,在从库必须串行回放。主库可以并发执行 INSERT INTO ... SELECT 或 LOAD DATA INFILE,但默认情况下,从库只有一个 SQL_THREAD 按事务顺序逐条执行——哪怕主库 1 秒内插入 50 万行,从库可能要花几十秒甚至几分钟去重放同一事务。
ROW 格式放大延迟:每行变更都记进 binlog
当 binlog_format=ROW(生产环境主流配置)时,批量插入会为每一行生成完整的前镜像/后镜像记录。例如插入 10 万行,binlog 就写入 10 万个独立 event,relay log 体积暴增,磁盘 IO 和 SQL 线程解析开销同步飙升。
- 对比
STATEMENT格式:只记一条INSERT ... SELECT语句,体积小但有函数、临时表等不安全风险,MySQL 8.0+ 默认禁用 -
binlog_row_image=FULL(默认)会让延迟更明显;设为MINIMAL可减少日志量,但要求主从表结构严格一致且不能有触发器 - 即使开启并行复制,单个大事务内部仍强制串行回放——
slave_parallel_type=LOGICAL_CLOCK对跨事务并行有效,对事务内无效
从库资源被“卡死”的真实表现
延迟不是“慢慢追”,而是 SQL 线程在某个点上长时间阻塞。常见现象包括:
-
SHOW PROCESSLIST中SQL_THREAD状态长期为Updating或Writing to net,但实际没进展 - 从库
innodb_buffer_pool_wait_free飙高,说明 buffer pool 不足,频繁刷脏页拖慢回放 - 磁盘 IO 利用率持续 100%,尤其 relay log 所在磁盘写满或使用 HDD
- 主库已执行完,
Seconds_Behind_Master却显示 0 —— 这是假象,因事务未提交前不更新该值;真正延迟藏在Relay_Log_Space持续增长中
为什么调高 slave_parallel_workers 有时没用
并行复制只对“无依赖的多个事务”生效。批量插入如果包裹在一个显式事务里(比如 BEGIN; INSERT ...; COMMIT;),整个事务就是一个原子单元,所有 worker 都得等它结束才能处理下一个事务。
- 检查是否用了事务包装:查主库 binlog,看对应 insert 是否落在同一个
Xid_log_event内 - 拆分方式必须是“多事务 + 小批次”,例如:
INSERT ... LIMIT 1000每次单独提交,才能被不同 worker 并行消费 -
slave_parallel_workers=4不代表一定能跑满 4 个线程——还要看slave_preserve_commit_order=1(默认开启)是否限制了提交顺序
最易被忽略的一点:从库的 innodb_flush_log_at_trx_commit 和 sync_binlog 如果和主库一样设为 1,每次事务提交都强制刷盘,会把本来能靠缓冲扛住的批量压力直接打到磁盘,让延迟雪上加霜。这类参数在从库完全可以适当放宽。











