应结合performance_schema.data_lock_waits或SHOW ENGINE INNODB STATUS\G的TRANSACTIONS部分定位阻塞源;PROCESSLIST中Command=Query且State异常(如updating)比State=Locked更可靠;Kill前须确认线程状态及SQL是否含大范围未索引操作。

怎么快速定位哪个SQL在阻塞别人
直接查 information_schema.PROCESSLIST,但别只看 State 为 Locked 的行——MySQL 8.0+ 默认不显示真实锁等待状态,得结合 performance_schema 才能看清谁卡了谁。
更实用的做法是:先执行 SELECT * FROM performance_schema.data_lock_waits;(需开启相关消费者),或退而求其次,用 SHOW ENGINE INNODB STATUS\G 翻到 TRANSACTIONS 部分,找 trx_mysql_thread_id 和 trx_wait_started 字段。
-
PROCESSLIST里Command是Sleep但Time特别长,不一定代表阻塞,可能是连接空闲;真阻塞往往表现为Command是Query、State是updating或sending data却迟迟不返回 - 注意
ID列对应线程 ID,和performance_schema.threads.THREAD_ID不同,不能混用 - 如果应用用了连接池,
PROCESSLIST里可能看到大量Sleep连接,但真正干活的只有几个活跃线程,优先盯住Time > 60且State非空闲的
kill掉阻塞源前要确认什么
别一看到“可疑线程”就 KILL。InnoDB 的锁等待链可能嵌套多层,杀错一个中间节点,反而让上游事务继续卡住,甚至触发更长的回滚。
关键确认点:
- 用
SELECT * FROM performance_schema.threads WHERE PROCESSLIST_ID = <code>xxx; 查该线程是否还在运行,还是已卡死在锁上 - 检查
PROCESSLIST中Info字段是否包含UPDATE/DELETE+ 大范围条件(比如没走索引的WHERE created_at ),这类语句最容易持锁久 - 确认该线程所属应用模块——有些定时任务或后台 job 的 SQL 故意长时间运行,杀掉可能影响业务逻辑,不是所有阻塞都该立刻干预
为什么 KILL 后事务没立刻释放锁
因为 KILL 只是给线程发中断信号,InnoDB 要等当前语句执行完、进入安全点后才真正回滚。如果正在写入大量数据或清理二级索引,回滚本身就会持续几十秒甚至几分钟。
常见表现:
-
SHOW ENGINE INNODB STATUS里仍能看到ROLLING BACK状态,trx_state是ROLLING BACK,trx_operation_state显示recovering data或类似内容 - 被阻塞的其他事务依然等在原地,直到回滚完成才收到
Lock wait timeout exceeded - 此时再
KILL一次无效,只能等它自己结束;强行重启 MySQL 会强制终止回滚,但可能导致数据字典不一致,不推荐
如何避免下次又卡住
临时解法是治标,长期得从 SQL 和事务设计入手。最常被忽略的是隐式事务和自动提交控制。
- 确保应用层显式使用
BEGIN/COMMIT,而不是依赖AUTOCOMMIT=1下单条语句事务——后者看似安全,但遇到INSERT ... SELECT或触发器时,实际事务范围可能远超预期 - 所有
UPDATE/DELETE必须带有效索引条件,用EXPLAIN确认type不是ALL或index,尤其警惕OR条件、函数包裹字段(如WHERE DATE(created_at) = '2024-01-01') - 大表变更操作(比如加索引、改列类型)避开高峰期,用
pt-online-schema-change或 MySQL 8.0+ 的ALGORITHM=INSTANT替代直接ALTER,减少元数据锁持有时间
锁冲突不是配置调高就能解决的问题,本质是读写路径上的资源竞争。越晚发现阻塞,说明监控越滞后;真正难的从来不是 kill 哪个线程,而是让锁等待在 5 秒内就被告警捕获并人工介入。


















