grant被阻塞是因为等待mysql系统表的mdl写锁,而非innodb行锁死锁;show engine innodb status不显示其信息,因其属server层mdl机制,不经过innodb锁路径。

GRANT 语句本身不会被死锁阻塞,它被卡住的真正原因是正在执行的事务持有 mysql 系统表的元数据锁(MDL),而 GRANT 需要修改这些表(如 mysql.user、mysql.db)——它在等 MDL 写锁,不是行锁死锁。
为什么 SHOW ENGINE INNODB STATUS 看不到 GRANT 的死锁信息
GRANT 操作不走 InnoDB 行锁路径,它本质是 DDL(对系统表的更新),依赖的是 Server 层的元数据锁(MDL),而非引擎层的 X 或 gap 锁。所以:
-
SHOW ENGINE INNODB STATUS\G的LATEST DETECTED DEADLOCK段完全不会包含GRANT相关内容 - 你看到的“卡住”,大概率是
GRANT在等待某个已开启但未提交事务释放对mysql库中表的 MDL 读锁或写锁 - 检查
information_schema.INNODB_TRX也查不到它,因为GRANT可能还没进入事务阶段,或已自动开启隐式事务但被 MDL 卡在第一步
快速定位谁在堵 GRANT:查 MDL 等待链
用以下查询直接揪出持有 mysql 表锁、且尚未提交的会话:
SELECT b.THREAD_ID AS blocking_thread, b.PROCESSLIST_ID AS blocking_pid, b.PROCESSLIST_INFO AS blocking_query, b.PROCESSLIST_TIME AS blocked_for_sec, a.OBJECT_SCHEMA, a.OBJECT_NAME, a.LOCK_TYPE, a.LOCK_DURATION, a.LOCK_STATUS FROM performance_schema.metadata_locks a JOIN performance_schema.threads b ON a.OWNER_THREAD_ID = b.THREAD_ID WHERE a.OBJECT_SCHEMA = 'mysql' AND a.LOCK_STATUS = 'PENDING' AND a.LOCK_TYPE = 'EXCLUSIVE';
如果没结果,再查当前活跃的、操作过 mysql 库的事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TRX_QUERY LIKE '%mysql.%' OR TRX_QUERY IS NULL;
重点关注 TRX_STARTED 时间早、TRX_STATE = 'RUNNING' 且 TRX_QUERY 为空(说明在做隐式操作或已卡住)的记录。
GRANT 被阻塞时的典型错误与响应方式
你不会看到 Deadlock found when trying to get lock (1213),而是:
-
ERROR 1205 (40001): Deadlock found when trying to get lock—— 这几乎不可能,除非你手动对mysql.user做了UPDATE ... FOR UPDATE,极罕见 -
ERROR 1099 (HY000): Table 'user' was locked with a READ lock and can't be updated—— 有人执行了FLUSH TABLES WITH READ LOCK - 最常见:命令无响应、超时(
Lock wait timeout exceeded),对应错误码1205或3024(MySQL 8.0+ 的 MDL 超时)
此时不要重试 GRANT,先做:
- 用
SHOW PROCESSLIST找出Command为Sleep且Time很大的连接,尤其是User是root或 DBA 账号的 - 确认是否有人在用
mysqldump --single-transaction或其他长事务连着mysql库(这类工具常隐式开启事务并长时间 hold 住 MDL) - 必要时
KILL掉可疑连接:KILL <code>blocking_pid;
如何避免 GRANT 被阻塞成常态
这不是应用逻辑问题,而是运维协同问题:
- 禁止在业务高峰期执行
GRANT;DBA 应有专用维护窗口 - 禁用任何对
mysql库的手动UPDATE/DELETE/INSERT—— 必须用GRANT、DROP USER等安全接口 - 监控
performance_schema.metadata_locks中mysql库的PENDING锁数量,持续 > 0 且时间 > 30 秒就告警 - MySQL 8.0+ 可设
lock_wait_timeout = 30(全局或会话级),让GRANT失败得更快,别傻等
真正麻烦的不是 GRANT 卡住,而是没人意识到它依赖的是 MDL 而非 InnoDB 行锁——所有排查都从错的方向开始,比如翻 INNODB_TRX 或等 SHOW ENGINE INNODB STATUS 出死锁日志。











