最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
MySQL执行DDL操作时怎样处理MDL锁阻塞其他查询
时间:2026-06-19 08:55:52 编辑:袖梨 来源:一聚教程网
DDL卡住时SELECT挂起是因MDL锁队列机制:ALTER需MDL_EXCLUSIVE锁但被长事务的MDL_SHARED_READ占用,导致后续所有请求(含SELECT)排队等待;ALGORITHM=INPLACE和LOCK=NONE不规避MDL锁等待,NO_WAIT可实现快速失败。
DDL卡住时,为什么SELECT也跟着挂起?
不是DDL本身阻塞了SELECT,而是MySQL的MDL锁队列机制导致的连锁等待。当一个ALTER TABLE需要MDL_EXCLUSIVE锁但拿不到(比如被长事务的MDL_SHARED_READ锁占着),它就会排在锁队列里;后续所有对该表的新请求(包括SELECT)都必须等这个排队中的MDL_EXCLUSIVE锁“先过”,哪怕它们只需要MDL_SHARED_READ。
常见现象:show processlist里看到多个线程状态都是Waiting for table metadata lock,但真正卡住源头的可能只是一个没提交的BEGIN; SELECT ...。
- MySQL 5.7默认不保证“先到先得”——排队中的
SELECT可能比ALTER更早拿到锁(尤其当ALTER是online且只短暂加锁时) - MySQL 8.0优化了调度逻辑,
ALTER在队列中不会被“饿死”,但依然会拖慢后续所有请求 -
mysqldump --single-transaction也会按表逐个加MDL_SHARED_READ锁,虽单次很短,但在高并发DDL场景下仍可能成为瓶颈
ALGORITHM=INPLACE 和 LOCK=NONE 真的不阻塞吗?
不完全。这两个参数只影响**DML操作是否能并发执行**,和MDL锁的获取/等待无关。
ALGORITHM=INPLACE意味着DDL过程不拷表,但准备阶段和提交阶段仍需短暂获取MDL_EXCLUSIVE锁;LOCK=NONE表示“不阻塞DML”,但它**不跳过MDL锁等待**——如果此时已有长事务持着MDL_SHARED_READ,DDL照样卡在第一步,后面所有查询照常排队。
- 真正决定是否阻塞的是“能否立刻拿到MDL锁”,而不是用了什么算法
- 加字段、建索引这类操作,在InnoDB上多数支持
INPLACE,但只要表上有未提交事务,就逃不开MDL等待 - MyISAM表不支持
INPLACE,任何ALTER都全程持有MDL_EXCLUSIVE锁,阻塞更彻底
如何让DDL失败快、不拖垮业务?
用NO_WAIT或WAIT N显式控制等待行为,避免无限期挂起。
阿里云RDS和MySQL 8.0+支持在DDL语句末尾加NO_WAIT或WAIT 1(单位秒)。例如:ALTER TABLE t ADD COLUMN c INT NO_WAIT。一旦无法立即获取MDL_EXCLUSIVE锁,立刻报错ERROR 3572 (HY000): Statement aborted: Lock wait timeout exceeded,而不是让线程一直挂着。
-
NO_WAIT:不等,直接失败 -
WAIT 2:最多等2秒,超时即报错 - 该语法适用于
ALTER、RENAME、DROP、TRUNCATE、CREATE INDEX等绝大多数DDL - 注意:这不能解决长事务问题本身,只是把“阻塞”变成“快速失败”,便于上层重试或告警
查不出谁在占MDL锁?试试这几个关键视图
别只看show processlist,它只显示当前活跃连接,而真正持锁的可能是已Sleep但事务未提交的连接。
优先查这三个地方:
-
SELECT * FROM performance_schema.threads WHERE PROCESSLIST_STATE = 'Sleep' AND PROCESSLIST_INFO IS NULL—— 找出看似空闲但可能挂着事务的连接 -
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60—— 查运行超1分钟的事务,重点看trx_state = RUNNING且trx_started很老的 -
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS FROM performance_schema.metadata_locks WHERE OWNER_THREAD_ID IN (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_STATE = 'Sleep')—— 直接看哪些表被哪些Sleep线程锁着
最隐蔽的坑是:某个应用连接池里的连接执行完SELECT后没COMMIT也没ROLLBACK,就一直Sleep在那里,默默卡住所有DDL——这种连接在processlist里看起来完全正常,但INNODB_TRX里能看到它。
相关文章
- 明末渊虚之羽防具有哪些排名 07-02
- 如何获取和平精英皮肤照片 07-02
- 空洞骑士丝之歌如何获取制造金属 07-02
- 鱼骨头螃蟹阵容如何搭配 07-02
- 战魂旅人玩法是什么 07-02
- 无限暖暖祝你幸福发饰如何获取 07-02