mysql 5.7 更新卡死主因常是锁等待而非sql错误,应优先用show engine innodb status定位lock wait事务,结合innodb_trx、processlist和innodb_locks三表排查sleep但running的隐性持锁事务。

MySQL 5.7 更新卡死,大概率不是 SQL 写错了,而是被别的事务锁住了——得先确认是不是锁等待,再决定杀不杀、怎么杀。
查不到 INNODB_LOCK_WAITS?别硬刷,5.7 里它还在但字段已不全
MySQL 5.7 中 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 表仍存在,但字段精简、关联不稳定,常返回空或漏掉阻塞源头。不能只靠它定位谁在等谁。
- 优先用
SHOW ENGINE INNODB STATUS\G,直接搜TRANSACTIONS段落,找状态为LOCK WAIT的事务,里面会明确写出WAITING FOR THIS LOCK TO BE GRANTED和持有锁的trx_id - 配合
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT'找出等待中的事务 ID 和开始时间 - 再用
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE ID = [waiting_trx_mysql_thread_id]看它在执行什么语句、卡在哪一步(比如State: Sending data或Locked)
innodb_lock_wait_timeout 不是万能解药,设太小反而掩盖问题
这个参数只控制“等多久就报错”,不解决“为什么等”。默认 50 秒,调成 5 秒只会让报错更快,但阻塞依旧存在。
- 查当前值:
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout' - 临时调高(排查用):
SET innodb_lock_wait_timeout = 120,避免误判为超时而非真卡死 - 永久修改要写进
/etc/my.cnf,但上线前必须验证:有些 ORM 框架对短超时有重试逻辑,改小可能引发雪崩式重试 - 注意:它只影响行锁等待(DML),不影响 DDL 的元数据锁(
Metadata lock),后者由lock_wait_timeout控制
真正卡住你的,很可能是没提交的“Sleep”事务
尤其在连接池场景下,应用拿了连接执行了 SELECT ... FOR UPDATE 或 BEGIN 后没 COMMIT 就归还连接,线程状态是 Sleep,但事务状态仍是 RUNNING——它一直霸着行锁或间隙锁。
- 必须联合查三张表:
INNODB_TRX+PROCESSLIST+INNODB_LOCKS(5.7 可用) - 重点盯:
COMMAND = 'Sleep'且trx_state = 'RUNNING'的记录,trx_started越早越可疑 - 用
SHOW ENGINE INNODB STATUS\G对照看该trx_id下的mysql tables in use和locked tables是否为 0 —— 是的话,基本可安全KILL - 别一上来就
KILL [ID],先KILL QUERY [ID]中断当前语句,观察是否释放锁
UPDATE 卡死,先看条件列有没有索引
没索引的 WHERE 条件会让 UPDATE 变成全表扫描+逐行加锁,不仅慢,还容易把整张表的行锁都占满,后续所有更新全被堵住。
- 执行
EXPLAIN UPDATE ...(5.7 支持对 UPDATE 做 EXPLAIN)看type是否为ALL,key是否为NULL - 如果条件列是
col1,而只有主键索引,立刻加ALTER TABLE t ADD INDEX idx_col1 (col1) - 注意:加索引本身是 DDL,在大表上也会拿 MDL 锁,避开业务高峰;若表太大,考虑用
pt-online-schema-change - 间隙锁(gap lock)也常被忽略:RR 隔离级别下,
WHERE col1 > 10这种范围条件会锁住间隙,即使没匹配到行——用SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS查lock_mode含gap就是它
最麻烦的不是锁本身,而是“谁持有了锁却没动静”——那个 Sleep 状态但 trx_state = RUNNING 的连接,往往藏在应用日志背后,数据库里只留下一个沉默的 trx_started 时间戳。











