答案是:通过SHOW PROCESSLIST识别“Waiting for table metadata lock”状态的DDL线程,再用sys.schema_table_lock_waits或performance_schema.metadata_locks定位持有SHARED_READ_ONLY锁的阻塞会话,KILL该会话而非等待中的DDL线程。

MySQL 中存储过程执行 DDL 会卡住,根本不是锁表而是等 MDL
MySQL 存储过程中执行 ALTER TABLE、CREATE INDEX 等 DDL 语句时「卡住不动」,99% 的情况不是因为行锁或事务没提交,而是被元数据锁(MDL)阻塞。DDL 操作必须获取 EXCLUSIVE 级别的 MDL,而此时如果有长事务正在读这张表(哪怕只是 SELECT),它就持着 SHARED_READ_ONLY 锁不放,DDL 就只能干等。
- 用
SHOW PROCESSLIST查看状态,如果卡在Waiting for table metadata lock,基本就是这个原因 - 别急着调
lock_wait_timeout——它只让报错更快,不解决谁在 hold 锁 - 真正该查的是
sys.schema_table_lock_waits,它直接告诉你哪个会话在 blocking - 执行
SELECT sql_kill_blocking_connection FROM sys.schema_table_lock_waits WHERE blocking_lock_type = 'SHARED_READ_ONLY',结果里那个KILL NNN才是该杀的线程
Oracle 中编译存储过程被阻塞,关键看 DBA_DDL_LOCKS
Oracle 里「重新编译存储过程卡住」,说明有人正以共享模式(Share DDL Lock)持有该对象,而你试图加排他锁(Exclusive DDL Lock)。典型场景是:另一个会话正在执行该存储过程、调试中未退出、或有未提交的 PL/SQL 块引用了它。
- 查阻塞源:
SELECT session_id, owner, name, mode_held, mode_requested FROM dba_ddl_locks WHERE name = 'YOUR_PROC_NAME' -
mode_held = 'Share'的那条记录对应的就是正在运行/调试的会话 ID - 结合
v$session查它的SID和SERIAL#:SELECT sid, serial#, status, event FROM v$session WHERE sid = <session_id> - 执行
ALTER SYSTEM KILL SESSION '<sid>,<serial#>',注意别 kill 自己的编译会话(那是受害者)
DDL 操作本身要不要加锁,取决于你用什么方式执行
在 MySQL 中,并非所有 DDL 都必然锁表。InnoDB 支持 ALGORITHM=INPLACE,但是否真能避免阻塞,要看具体操作和参数组合。
- 支持无锁(
LOCK=NONE)的操作极少:仅限添加/删除二级索引、改DEFAULT、扩大VARCHAR长度(utf8mb4 且未超行限制)等 -
ALTER TABLE t ADD COLUMN c INT默认可 INPLACE,但若加在非末尾位置,或字段类型变更(如MODIFY COLUMN),就会降级为 COPY 模式,触发全表拷贝和锁表 - 执行前先试跑:
ALTER TABLE t ALGORITHM=INPLACE, LOCK=NONE, ADD INDEX idx_x (x);—— 不支持时直接报错,不会静默失败 - 即使 INPLACE 成功,仍需 MDL,所以长事务仍是瓶颈;务必先确认
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5没有运行超 10 秒的事务
别依赖重试,先确认是不是 DDL 在「等别人」而不是「自己慢」
很多团队把 DDL 卡住归因于「表太大」,于是反复重试或切窗口,结果发现每次都在同一秒卡住——这说明问题不在 DDL 本身耗时,而在等待某个固定源头释放锁。真正的瓶颈往往藏在监控盲区里:一个凌晨跑的报表查询没提交、一个开发环境的 debug session 忘关、或者某条 SELECT ... FOR UPDATE 被异常中断后连接没断。
复杂点在于,MySQL 的 MDL 持有者可能根本不在 SHOW PROCESSLIST 里显示活跃(比如已 sleep 但事务未 commit),Oracle 的 DBA_DDL_LOCKS 却明确暴露了 session ID。最容易被忽略的,是误杀正在执行 DDL 的线程——它状态是 Waiting for table metadata lock,但其实是受害者,不是加害者。

















