
mysql lock_wait_timeout 是什么,改它真能解决“卡住”的问题?
lock_wait_timeout 控制的是事务在等待行锁(或元数据锁)时最多忍多久,超时就报 Lock wait timeout exceeded。但它**不控制元数据锁(MDL)的等待超时**——这是很多人误配的根本原因。MySQL 5.7+ 的 MDL 等待由 lock_wait_timeout *表面覆盖*,但实际行为受更底层机制约束:比如 DDL 操作会持有强 MDL 锁,而普通 SELECT 在某些场景下也会被阻塞,此时改 lock_wait_timeout 并不能让 SELECT 快速失败,反而可能掩盖真正瓶颈。
怎么查当前 MDL 等待和锁冲突?
别猜,先看现场。核心是查 performance_schema 里的三张表:
-
metadata_locks:显示当前所有 MDL(包括类型、作用对象、持有/等待状态) -
threads:关联线程 ID 和 SQL(需 join 获取语句) -
events_statements_current:拿到正在执行或卡住的 SQL 文本
常用诊断语句:
SELECT m.OBJECT_SCHEMA, m.OBJECT_NAME, m.LOCK_TYPE, m.LOCK_DURATION, m.LOCK_STATUS, t.PROCESSLIST_ID, t.PROCESSLIST_INFO FROM performance_schema.metadata_locks m JOIN performance_schema.threads t ON m.OWNER_THREAD_ID = t.THREAD_ID WHERE m.LOCK_STATUS = 'PENDING';
注意:LOCK_STATUS = 'PENDING' 表示这个线程正卡在等锁;若没结果,说明不是 MDL 等待,可能是行锁或事务未提交导致的锁链。
真正影响 MDL 等待行为的关键参数有哪些?
除了 lock_wait_timeout,以下参数更直接决定 MDL 行为:
-
max_write_lock_count:默认 0(不限),设为小值可强制写锁让位,但极少用,副作用大 -
innodb_rollback_on_timeout:设为ON可让行锁超时后自动回滚事务,间接减少 MDL 持有时间 -
wait_timeout和interactive_timeout:空闲连接断开时间,避免长事务意外占着 MDL 不放 - MySQL 8.0+ 新增
lock_wait_timeout对 MDL 生效,但仅限于显式加锁语句(如LOCK TABLES),对隐式 DDL 引发的 MDL 仍无效
所以,调低 lock_wait_timeout 对 ALTER 表卡住的场景基本没用;真正有效的是:缩短事务长度、避免在事务里做 DML 后再执行 DDL、用 pt-online-schema-change 替代原生 ALTER。
修改 lock_wait_timeout 的实操要点
它分 session 和 global 两级,且只对后续新事务生效,不影响已运行事务:
- 临时改当前会话:
SET SESSION lock_wait_timeout = 10;(单位秒) - 全局改(需 SUPER 权限):
SET GLOBAL lock_wait_timeout = 10;,但重启失效 - 永久生效要写进配置文件:
my.cnf中加innodb_lock_wait_timeout = 10(注意:这是 InnoDB 行锁超时,不是 MDL!变量名易混淆)
常见坑:lock_wait_timeout 默认是 50 秒,但很多 ORM(如 Django、Laravel)会默认设置成 60 或 300,覆盖 MySQL 侧配置;应用层超时比数据库层更早触发,此时你看到的错误其实是应用抛的,不是 MySQL 的 Lock wait timeout exceeded。
复杂点在于:MDL 锁的“等待不可中断”,哪怕你设了 1 秒超时,只要持有锁的线程没结束(比如一个慢查询卡着 SELECT),等待方就只能干等——直到持有方释放或被 kill。所以监控和快速 kill 长事务,比调参数更重要。


















