事务卡住不提交的首要排查方法是查询information_schema.INNODB_TRX,重点关注trx_state为LOCK WAIT或RUNNING且trx_started异常久远的情况,并结合INNODB_LOCK_WAITS等定位阻塞源。

事务卡住不提交,先查 information_schema.INNODB_TRX
MySQL 事务看似“没反应”,大概率不是代码写错了,而是被别的事务堵住了。直接看当前活跃事务最靠谱:
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id, trx_query FROM information_schema.INNODB_TRX;重点关注
trx_state 是 LOCK WAIT 还是 RUNNING;如果是前者,说明它正等着锁释放;后者但长时间不动,就得结合 trx_started 判断是否真卡死。常见错误现象:
COMMIT 一直 hang 住、应用超时、SHOW PROCESSLIST 里状态是 update 或 waiting for table metadata lock。实操建议:
- 立刻查
INNODB_LOCK_WAITS和INNODB_LOCKS(8.0+ 已废弃,用performance_schema.data_locks替代)定位谁在持锁 - 别只盯着自己的 SQL——可能是个没加
WHERE的UPDATE锁了全表,或者一个长事务没提交,把后续所有修改都拦住了 - 开发环境可临时设
innodb_lock_wait_timeout=5快速暴露阻塞问题 - 用
SELECT @@read_only, @@super_read_only;一查便知 - 主从架构下,从库默认开
read_only=ON,但运维手动改过配置后忘记关super_read_only是高频翻车点 - 某些云厂商 RDS 控制台“只读实例”开关会静默设置这两个变量,且重启不恢复——得进控制台关,不能只改配置文件
- 临时解除:需有
SUPER权限,执行SET GLOBAL super_read_only = OFF;(注意:必须先关super_read_only,再关read_only) - 立刻执行
df -h和df -i(inode 也可能耗尽) - 检查 MySQL 数据目录挂载状态:
mount | grep $(dirname $(my_print_defaults --mysqld | grep datadir | awk '{print $2}')),确认不是ro - 留意
innodb_log_file_size过大 + 磁盘小的组合:redo log 循环写失败会直接让实例拒绝写入 - 别信监控里“磁盘使用率 85% 就安全”——InnoDB 写放大和临时排序区会让实际需求翻倍
- 用
SHOW PROCESSLIST查该线程状态:如果是Sleep且Command是Sleep,Time很大,说明连接空闲但事务没结束 - 检查应用层连接池配置:比如 HikariCP 的
connection-timeout和idle-timeout是否过短,导致连接被池回收但事务未清理 - 避免在业务逻辑里长期保持
autocommit=0:它本质是把事务生命周期交给应用控制,一旦出错(如未捕获异常、未调用commit/rollback),就会留下长事务拖垮性能 - 必要时用
KILL [thread_id]主动终结可疑连接(注意:KILL 不等于 ROLLBACK,它只是断连,事务由 MySQL 自动回滚)
事务卡住的真实原因,往往藏在“看起来无关”的地方:可能是隔壁机器 rsync 正在同步 binlog 目录把磁盘 IO 打满,也可能是 SELinux 强制挂载了
误入只读模式?检查 super_read_only 和 read_only
执行 UPDATE 或 INSERT 报错 ERROR 1792 (HY000): Cannot execute statement in a READ ONLY transaction,不是权限问题,八成是 MySQL 被切到只读了。
两个开关必须同时为 OFF 才能写:read_only(影响普通用户)、super_read_only(连 root 也拦)。
实操建议:
磁盘满或只读挂载导致 COMMIT 失败
事务能开始、能改数据,但死活 COMMIT 不了,甚至报错 ERROR 1030 (HY000): Got error 28 from storage engine(对应系统错误 ENOSPC),基本就是磁盘空间见底或文件系统被 mount 为 ro。
InnoDB 在 COMMIT 阶段要刷 redo log、更新 doublewrite buffer、写 undo 日志,任一环节写磁盘失败都会回滚整个事务,但错误信息常被掩盖成“连接断开”或“超时”。
实操建议:
为什么 autocommit=0 下手动 COMMIT 没反应?
代码里显式设了 SET autocommit = 0,之后执行 DML,再发 COMMIT 却像石沉大海,日志也没报错——大概率是客户端连接异常中断了,但 MySQL 服务端还维持着连接状态,事务一直挂着。
这种场景下,COMMIT 命令其实没发出去,或者发了但连接已断,客户端收不到响应。
实操建议:
ro,还可能是某个定时脚本悄悄执行了 SET GLOBAL read_only = ON 却没通知任何人。排查时别只盯 SQL 本身。










