从库SQL线程被长事务阻塞的本质是单事务串行回放或锁等待,非网络/磁盘问题;需通过SHOW SLAVE STATUS查Slave_SQL_Running_State、Exec_Master_Log_Pos停滞与Seconds_Behind_Master稳定上涨,并在主库查INNODB_TRX中trx_rows_modified > 10000且状态为RUNNING的事务。

从库被长事务阻塞,本质是SQL线程卡在锁等待或单事务串行回放上,不是网络或磁盘问题,必须立刻定位持有锁的事务或正在执行的大事务本身。
怎么确认是长事务卡住SQL线程
别只看Seconds_Behind_Master跳变。先在从库执行SHOW SLAVE STATUS\G,重点盯三处:
-
Slave_SQL_Running_State显示Waiting for dependent transaction to commit或长时间停在Executing event -
Exec_Master_Log_Pos几乎不动,而Read_Master_Log_Pos持续前移 -
Seconds_Behind_Master稳定上涨(比如每秒+1、+2),不是突增后回落
再查主库是否存在活跃大事务:SELECT trx_id, trx_started, trx_rows_modified FROM information_schema.INNODB_TRX WHERE trx_rows_modified > 10000 AND trx_state = 'RUNNING'。有结果基本就是它。
为什么直接加LIMIT拆分UPDATE/DELETE反而更糟
直接在原SQL里加LIMIT但不控制事务边界,等于把一个大事务拆成一堆小事务——每条都单独记binlog,relay log体积暴增,从库IO和SQL线程压力更大。
- 必须显式
SET autocommit = 0,手动START TRANSACTION和COMMIT每批次 -
WHERE条件要可复用,比如WHERE id > ? AND id <= ?,避免OFFSET导致越删越慢 - 每次执行后必须记录最后处理的
id(写入临时表或文件),否则中断重跑会漏数据或重复 -
ORDER BY id LIMIT必须存在,防止幻读干扰分片边界
从库隔离级别调成READ-COMMITTED真能缓解吗
能,但只对特定场景有效:当从库上有长SELECT(尤其REPEATABLE-READ下)拖住purge线程,导致undo log无法清理,进而卡住含UPDATE/DELETE的SQL线程时,改隔离级别确实有用。
-
READ-COMMITTED下,每次语句只读取语句开始时的快照,不维护整个事务级read view,undo回收更及时 - 仅对从库生效,主库不影响;但建议主从保持一致,避免应用误依赖隔离行为
- 必须配合
binlog_format = ROW,否则STATEMENT格式下某些函数行为会异常 - 不能只改全局变量:
SET GLOBAL transaction_isolation = 'READ-COMMITTED'后,老连接仍用旧级别,需KILL CONNECTION或等其自然退出
DDL操作卡住SQL线程时能不能手动绕过
能,而且是目前最稳妥的做法——前提是DDL本身支持LOCK=NONE(如加普通索引、末尾加列),且主从数据已校验一致。
- 先
STOP SLAVE,暂停复制 - 用
pt-table-checksum或mysqldiff确认目标表结构和数据一致 - 在从库手动执行相同DDL(注意检查是否真支持无锁,否则仍会卡在
Waiting for table metadata lock) -
START SLAVE恢复,观察Seconds_Behind_Master是否快速归零
最容易被忽略的是:即使DDL在主库用了ALGORITHM=INPLACE,从库重放时仍需获取MDL锁,如果从库正有长查询访问该表,一样会阻塞。所以手动执行前,务必确认没有活跃连接在读这张表。


















