大事务会让从库 SQL 线程卡住,因为 MySQL 默认串行回放,即使主库并发提交,从库仍需单线程依次执行;耗时长的 DELETE/UPDATE 若未走索引或操作百万级数据,会持续阻塞 SQL Thread,调大 innodb_buffer_pool_size 无效,根本解法是拆分事务、启用 LOGICAL_CLOCK 并行复制,并规避 GTID 下隐式锁升级引发的锁等待。

大事务为什么会让从库 SQL 线程卡住
因为 MySQL 默认的复制是串行回放——哪怕主库用 20 个线程并发提交了 20 个事务,从库的 SQL Thread 仍得一个一个按顺序执行。而一个耗时 8 秒的 DELETE FROM logs WHERE create_time ,会直接堵住后面所有事务的回放,<code>Seconds_Behind_Master 就开始指数级上涨。
这不是从库“慢”,而是它被设计成必须等前一个事务彻底提交后,才能启动下一个。大事务在主库可能只是“写 binlog 快”,但它的实际执行逻辑(比如逐行加锁、刷脏页、生成 undo log)全都要在从库重演一遍。
怎么确认当前延迟就是大事务导致的
先看 SHOW SLAVE STATUS\G 中的关键字段:
-
Slave_SQL_Running_State如果长期停留在Executing event或Waiting for table metadata lock,大概率正在执行某个长事务 -
Executed_Gtid_Set和Retrieved_Gtid_Set差值很小(比如只差 1~2 个 GTID),说明 relay log 已拉完,瓶颈在 SQL 回放 -
Seconds_Behind_Master持续 >5 秒且不下降,配合从库SHOW PROCESSLIST能看到一个长时间运行的Query状态
再用 mysqlbinlog 定位具体语句:
mysqlbinlog --no-defaults --base64-output=decode-rows -v \
--start-datetime="2026-07-01 20:00:00" \
mysql-bin.000012 | grep -A 10 -B 5 "DELETE\|UPDATE.*WHERE.*[0-9]\{6,\}"
重点盯那些带百万级 WHERE 条件、没走索引、或涉及 TEXT/BLOB 字段更新的语句。
为什么不能靠调大 innodb_buffer_pool_size 解决
缓冲池调大对读密集型有效,但对大事务同步延迟基本无效——因为问题不在内存不足,而在单线程串行执行本身。即使把 innodb_buffer_pool_size 设为物理内存的 75%,那个 100 万行的 UPDATE 还是要在 SQL Thread 里一条条加锁、更新、刷盘。
真正起作用的是:
- 拆分事务:把
DELETE FROM t WHERE ts < '2023-01-01'改成每次删 5000 行 +SLEEP(0.1) - 启用并行复制:
slave_parallel_type = LOGICAL_CLOCK+slave_parallel_workers = 4(MySQL 5.7+) - 避免在从库执行任何 DDL 或备份:
FLUSH TABLES WITH READ LOCK会直接阻塞SQL Thread
最容易被忽略的坑:GTID + 大事务的隐式锁升级
开启 GTID 后,MySQL 会对大事务做额外校验,比如检查 gtid_next 是否冲突、验证事务前后状态一致性。这本身不慢,但一旦遇到 UNIQUE KEY 冲突或 FOREIGN KEY 约束失败,整个事务会回滚重试,而重试过程又可能触发表级锁等待——此时 Seconds_Behind_Master 不是稳定增长,而是“跳变式飙升”,查 SHOW ENGINE INNODB STATUS 会看到大量 lock wait timeout 记录。
这种场景下,光看 binlog 日志看不出问题,必须结合从库的 error log 和 INFORMATION_SCHEMA.INNODB_TRX 查活跃事务锁状态。


















