存储过程重编译会触发mdl_exclusive锁,因其需打开表、校验依赖对象一致性,必须获取排他元数据锁;该过程隐式发生,不支持inplace规避,遇长事务即阻塞所有dml/select。

存储过程重编译为什么会触发 MDL_EXCLUSIVE 锁?
MySQL 5.7 在调用已存在的存储过程时,如果发现其定义被修改过(比如通过 ALTER PROCEDURE),或其依赖的表结构发生变化(如字段增删、索引变更),就会触发「重编译」——即重新解析、校验并生成执行计划。这个过程不是轻量级的元数据刷新,而是要打开表、读取数据字典、检查所有依赖对象一致性,因此必须获取 MDL_EXCLUSIVE 锁。一旦拿到该锁,所有其他会话对这张表的 SELECT、INSERT、UPDATE 都会被阻塞,直到重编译完成。
哪些操作会意外触发存储过程重编译?
你没改存储过程本身,但它照样可能被强制重编译,进而锁表:
-
ALTER TABLE修改了存储过程中SELECT或UPDATE的目标表(哪怕只是加个字段) - 表上新增/删除了索引,而存储过程里用了
FORCE INDEX或优化器因统计信息变化选择了不同执行路径 - 执行了
ANALYZE TABLE,导致存储过程缓存的执行计划失效 - 表被
TRUNCATE或DROP后重建(即使表名相同,内部 object_id 已变)
为什么 SHOW PROCESSLIST 看不到“正在重编译”,只看到 Waiting for table metadata lock?
因为重编译是隐式、自动发生的,不对应任何显式 SQL 语句。你在 SHOW PROCESSLIST 里看到的通常是调用该存储过程的那条 CALL proc_name(),它的 State 就是 Waiting for table metadata lock —— 实际卡在等 MDL 写锁,而不是卡在执行逻辑里。此时真正持有 MDL_SHARED_READ 的,很可能是另一个未提交的事务(比如一个长 SELECT),它让重编译拿不到写锁,进而把所有后续请求拖入等待链。
怎么验证是不是存储过程重编译导致的锁?
别只盯着 SHOW PROCESSLIST,得查底层状态:
- 查
performance_schema.metadata_locks,过滤目标表名,看是否有LOCK_TYPE = 'EXCLUSIVE'且LOCK_DURATION = 'TRANSACTION'的记录 - 查
information_schema.INNODB_TRX,找运行时间超 60 秒的事务,尤其是trx_state = 'RUNNING'但COMMAND = 'Sleep'的连接 - 执行
SELECT * FROM mysql.proc WHERE name = 'your_proc_name'\G,对比modified和created时间戳,若近期被改过,重编译概率极高
真正麻烦的是:重编译锁表不可跳过,也不能用 ALGORITHM=INPLACE 规避——它是语句执行前的准备阶段,和 DDL 无关,但共享同一套 MDL 机制。只要依赖表有活跃事务,它就只能等。











