Sleep连接可能持锁,因其仅反映连接层空闲状态,不体现事务层真实锁持有情况;需结合INFORMATION_SCHEMA.INNODB_TRX中TRX_STATE='RUNNING'且TRX_ROWS_LOCKED>0确认长事务锁表。

为什么SHOW PROCESSLIST里看到的Sleep连接可能正在持锁
因为SHOW PROCESSLIST只反映连接层状态,不反映事务层状态。一个连接执行了BEGIN但没COMMIT或ROLLBACK,它在PROCESSLIST里就显示为Command = 'Sleep'、State = NULL、Time值很大(比如 1800),但INFORMATION_SCHEMA.INNODB_TRX里它的TRX_STATE仍是'RUNNING',且TRX_ROWS_LOCKED > 0——锁早就拿上了,只是人“睡着”了没放手。
常见诱因包括:应用异常退出未清理事务、ORM自动开启事务后忘记提交、Navicat 等客户端长时间空闲后关闭导致连接残留。
必须查INFORMATION_SCHEMA.INNODB_TRX,而不是只看PROCESSLIST
INFORMATION_SCHEMA.INNODB_TRX才是唯一能确认“事务是否活着+是否持锁”的视图。重点关注以下字段:
-
TRX_MYSQL_THREAD_ID:对应可被KILL的线程 ID(不是PROCESSLIST.ID,但通常一致) -
TRX_STATE:值为'RUNNING'且TRX_STARTED很早,大概率是长事务挂起 -
TRX_ROWS_LOCKED> 0:明确表示该事务当前持有行锁 -
TRX_WAITING_TRX_ID IS NOT NULL:说明它正被其他事务阻塞,自己却还在锁别人 -
TRX_QUERY常为空:别指望这里看到 SQL,尤其对纯BEGIN后无操作的连接
执行这句就能筛出可疑的“睡着还锁着”的连接:
SELECT TRX_MYSQL_THREAD_ID, TRX_STARTED, TRX_STATE, TRX_ROWS_LOCKED, TRX_QUERY FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_STATE = 'RUNNING' AND TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW()) > 60 AND TRX_ROWS_LOCKED > 0;
如何把TRX_MYSQL_THREAD_ID和真实可杀的连接对上
TRX_MYSQL_THREAD_ID就是你要KILL的目标,但它不一定出现在SHOW PROCESSLIST里——比如权限不足、连接已断开但事务未回滚、或云数据库屏蔽了部分PROCESSLIST内容。
验证方式:
- 先用
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = ?查对应 ID 是否还在列表中 - 如果查不到,再运行
SELECT COUNT(*) FROM sys.processlist WHERE id = ?(sys库做了权限适配) - 若仍不可见,直接
KILL CONNECTION ?试试——MySQL 会静默失败,但不会报错;成功则返回Query OK - 注意:
KILL QUERY对Sleep连接无效,必须用KILL CONNECTION
别依赖INFO字段判断 SQL 内容:很多 ORM(如 Laravel 的DB::transaction())或预处理模式下,INFO为空,但锁真实存在。
容易忽略的权限与版本差异
普通账号默认看不到其他用户的INNODB_TRX记录,需要PROCESS权限(RDS 上常需申请)。没权限时,SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_TRX可能返回 0,不代表没锁,只是你看不见。
MySQL 8.0.15+ 默认禁用sys.innodb_locks,需手动启用:SET GLOBAL innodb_monitor_enable = 'lock_mutex,lock';(不推荐长期开启,性能开销大);而performance_schema.data_locks替代了老版INNODB_LOCKS,字段名全变了,硬套旧脚本会查不到数据。
锁信息是瞬态的,查到INNODB_LOCK_WAITS非空时,立刻记下WAITING_TRX_ID和BLOCKING_TRX_ID,再回头查对应事务详情——晚半秒,锁可能就释放了。


















