直接查SHOW PROCESSLIST中system user的SQL_THREAD行,若State为executing event且Time持续增长(如>60秒),基本可判定被长事务阻塞;再结合INNODB_TRX中trx_state='RUNNING'、trx_rows_modified>100000、trx_started远早于当前时间等特征确认大事务。

SQL线程卡在executing event,怎么确认是大事务?
直接查 SHOW PROCESSLIST 里 system user 对应的 SQL_THREAD 行,如果 State 是 executing event 且 Time 持续增长(比如 >60),基本就是被一个长事务堵死了。这时别急着 kill,先确认它到底在回放什么:执行 SELECT * FROM performance_schema.replication_applier_status_by_worker\G,看 LAST_APPLIED_TRANSACTION 和 APPLYING_TRANSACTION 字段——如果后者长时间不变,说明当前事务还没跑完。
从库上怎么快速定位正在回放的大事务?
大事务不一定还在主库运行,但从库回放时会暴露痕迹。重点查两个地方:
-
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX\G,盯trx_state = 'RUNNING'、trx_rows_modified > 100000、trx_started时间远早于当前时间——这种就是典型卡住 SQL 线程的“巨无霸” -
SHOW SLAVE STATUS\G中Exec_Master_Log_Pos长时间不动,而Read_Master_Log_Pos在涨,说明 relay log 已收到但压根没开始处理,十有八九是前一个事务没结束
为什么用 pt-heartbeat 比 Seconds_Behind_Master 更靠谱?
Seconds_Behind_Master 在大事务场景下完全失效:它只记录事务 BEGIN 时间戳,不反映实际回放进度。比如主库一个 UPDATE 改 200 万行,从库 SQL 线程刚跑 10 分钟,它仍显示“0”。pt-heartbeat 是主动写心跳表 + 主从时间差比对,秒级精度,且不受事务大小影响。部署后执行:pt-heartbeat --master-server-id=1 --monitor --interval=1,看到输出持续 >5 秒就该干预了。
拆分大事务时最容易踩的三个坑
不是所有分批 UPDATE 都能缓解延迟,错用反而更糟:
- 用
LIMIT 1000 OFFSET N:OFFSET 越大越慢,且并发执行时可能漏行或重复,必须换成游标式推进(如WHERE id > @last_id) - 没给 WHERE 字段建索引:比如按
status = 'pending'批量更新,但status上没索引,每次都是全表扫 - 忘记显式
COMMIT:即使写了 LIMIT,若整个脚本包在一个事务里,还是单一大事务,从库照样卡死
真正卡住 SQL 线程的大事务,往往在从库 INNODB_TRX 里看不到活跃记录——因为它已经提交,只是回放逻辑本身阻塞在某条 Update_rows_log_event 解析上。这时候得结合 relay-log 文件解析和 pt-heartbeat 延迟曲线交叉验证,不能只盯一个指标。











