waiting for table metadata lock 表示 mdl 读锁被未提交事务长期持有,导致 ddl 无法获取写锁;常见于 begin 后未 commit、长事务或 navicat 自动保存触发的隐式锁竞争。

SHOW PROCESSLIST 看到 Waiting for table metadata lock 就是元数据锁没释放
Navicat 点「保存」改表结构时卡住,不是 Navicat 坏了,而是 MySQL 正在等一个它永远等不到的锁。关键信号就是 SHOW PROCESSLIST 里某行的 State 列显示 Waiting for table metadata lock——这说明有别的会话正拿着这张表的元数据锁不放,而 ALTER TABLE 必须拿到独占锁才能执行。
常见源头:BEGIN 后没 COMMIT 或 ROLLBACK 的事务、长时间运行的 SELECT(尤其带 FOR UPDATE)、甚至 Navicat 自己上一次建表/改字段失败后残留的未清理状态。
- 别只看
Time大就 KILL,先确认该连接的User和db是你自己,避免误杀生产应用连接 -
Command是Sleep但Time超过 300 秒,且紧挨着一条State = Waiting for table metadata lock的记录,大概率就是它 - MySQL 8.0+ 可用
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';直接查锁归属,比INNODB_TRX更准
KILL 之前必须确认 trx_state 是 RUNNING 且没提交
盲目 KILL 可能丢数据,尤其是正在写入的事务。先查活跃事务:SELECT trx_id, trx_mysql_thread_id, trx_state, trx_started, trx_query FROM information_schema.INNODB_TRX ORDER BY trx_started DESC LIMIT 5;
重点盯两个字段:trx_state = 'RUNNING'(说明真在干活,不是挂起)且 trx_query 是 UPDATE、INSERT 或裸 BEGIN——这种才值得干预。如果 trx_state = 'LOCK WAIT',说明它自己也在等别人,KILL 它没用,得往前找根因。
- 查完再执行
KILL <code>thread_id;,注意不是KILL QUERY <code>thread_id,后者只中断当前语句,锁还在 - 别碰
User = 'system user'或Command = 'Daemon'的线程,那是 MySQL 内部线程 - 如果
trx_query是空,但trx_state = 'RUNNING'且trx_started很老,大概率是客户端断连但事务没自动回滚(取决于wait_timeout和innodb_lock_wait_timeout设置)
PostgreSQL 里不能直接用表名查锁,必须转 oid
PostgreSQL 没有“表被锁定”这种直白提示,pg_locks 里的 relation 字段存的是整数 oid,不是字符串表名。直接写 WHERE relation = 'users' 永远查不到结果。
正确三步走:SELECT oid FROM pg_class WHERE relname = '<code>your_table_name'; → 拿到 oid 后,SELECT pid, mode FROM pg_locks WHERE relation = <code>oid_from_above AND mode ~ 'Exclusive|RowExclusive'; → 最后 SELECT pg_terminate_backend(<code>pid);
-
mode包含ExclusiveLock表示表级独占锁,RowExclusiveLock是普通 DML 锁,但 ALTER 仍需等前者释放 - 如果
pid对应的会话在pg_stat_activity中state = 'idle in transaction',说明它 BEGIN 了但没提交,这就是元凶 - 想只中断当前查询不杀会话,用
pg_cancel_backend(<code>pid);但对idle in transaction无效,必须pg_terminate_backend
Navicat 自动保存 + 长事务 = 卡死组合拳
Navicat 16 默认勾选「自动保存」,点「保存」就立刻发 ALTER TABLE。但如果此时你或别人有个未提交事务正读这张表,ALTER 就会卡在元数据锁上,而 Navicat UI 不报错、不超时、不提示,只显示「正在保存…」——这是最坑人的静默失败。
- 临时解法:右键表 →「设计表」→ 修改完先点「预览 SQL」,确认语句无误后,复制到新查询窗口手动执行(绕过自动保存封装)
- 长期规避:连接属性 → 高级页签 → 关闭「自动保存」,把控制权拿回来
- 别在修改表结构时开着另一个查询窗口执行
BEGIN,哪怕只是测试——这个习惯在 Navicat 里等于主动埋雷
真正卡住的时候,问题从来不在 Navicat 界面按钮是否点亮,而在背后那个没人收尾的事务是否还攥着锁。查锁要快,杀进程要准,但最省事的永远是——改表前先确保自己没留着没提交的 BEGIN。











