先查information_schema.innodb_trx中trx_state='RUNNING'且trx_started时间早的事务,获取trx_mysql_thread_id后执行KILL trx_mysql_thread_id终止连接释放锁。

怎么快速定位哪个事务卡住了 Lock wait timeout exceeded
报错 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 时,不是代码写错了,而是有别的事务正拿着锁不放——你得先找到它。最直接的办法是查 information_schema.innodb_trx:
SELECT trx_id, trx_mysql_thread_id, trx_started, trx_state, trx_query FROM information_schema.innodb_trx ORDER BY trx_started;
重点关注 trx_state = 'RUNNING' 且 trx_started 时间很早的记录,说明它可能长期没提交;trx_query 字段能看到它正在执行什么 SQL。如果发现某个事务卡了几十秒甚至几分钟,基本就是罪魁祸首。
接着用 KILL 干掉它:
KILL <code>trx_mysql_thread_id</code>;
注意:不能用 KILL QUERY,必须用 KILL(即终止整个连接),因为锁是在事务级别持有的,只杀查询不释放锁。
为什么改了 innodb_lock_wait_timeout 还是报错
这个参数只控制“等多久就放弃”,不解决“为什么等”。设成 300 秒只是让错误来得晚一点,反而更容易掩盖真实问题。
-
set innodb_lock_wait_timeout = 300只对当前会话生效,新连接仍用默认值(50 秒) -
set global innodb_lock_wait_timeout = 300要求 SUPER 权限,且对已存在的连接无效 - 真正生效要靠配置文件修改(如
/etc/my.cnf或my.ini),加一行innodb_lock_wait_timeout = 30,然后重启 MySQL
但别盲目调大——线上业务等待超过 10 秒通常就该报警了,拖到分钟级只会让阻塞链更长、影响面更大。
怎么从死锁日志里看出谁在抢同一行
MySQL 每次发生死锁,都会把冲突双方的事务快照写进错误日志(mysqld.err)。搜索关键词 LATEST DETECTED DEADLOCK 就能找到最近一次记录。
里面关键信息包括:
-
*** (1) TRANSACTION:和*** (2) TRANSACTION:—— 两个互相等待的事务 -
*** (1) HOLDS THE LOCK(S):—— 哪个事务持有了哪些锁(比如 X lock on index `idx_col1` of table `test_tb`) -
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:—— 另一个事务在等什么锁 -
INSERT INTO test_tb ...或UPDATE test_tb SET ... WHERE col1 = 'a'—— 实际 SQL,能帮你反推是否用了相同索引条件
常见陷阱:两个事务都走 WHERE col1 = ?,但 col1 上没索引 → 会升级为表锁或间隙锁,大幅增加冲突概率。
长连接不提交事务是最大隐患
很多 1205 报错背后,其实是应用层没正确管理事务生命周期。比如:
- Java 应用开了
Connection.setAutoCommit(false),但忘了commit()或rollback() - Python 的
with conn.cursor() as cur:没配事务上下文管理器,异常后连接还挂着 - PHP 中
mysqli_begin_transaction()后没跟mysqli_commit()
这类连接一旦空闲,事务状态保持 RUNNING,锁一直不释放。查 SHOW PROCESSLIST 时会看到状态是 Sleep,但 information_schema.innodb_trx 里仍显示 RUNNING —— 这就是典型的“静默阻塞”。
建议在应用层加事务超时兜底(如 Spring 的 @Transactional(timeout = 5)),数据库侧配合 wait_timeout 和 interactive_timeout 控制空闲连接自动断开。


















