MySQL 8.0中MDL SHARED_READ锁引发死锁的原因是什么?
核心逻辑:MDL锁升级规则+循环等待触发死锁
要理清这个问题,先明确MySQL元数据锁(MDL)的关键类型及兼容性:
- MDL_SHARED_READ:由普通SELECT触发,允许当前会话读操作,也允许其他会话获取SHARED_READ/SHARED_WRITE锁,但会阻塞MDL_EXCLUSIVE锁(如ALTER TABLE这类DDL操作)
- MDL_SHARED_WRITE:由INSERT/UPDATE/DELETE触发,允许当前会话读写操作,同样阻塞MDL_EXCLUSIVE锁,且与其他SHARED_READ/SHARED_WRITE锁兼容
- MDL_EXCLUSIVE:由DDL操作触发,需要独占元数据锁,会阻塞所有其他类型的MDL锁
场景一的死锁形成链条
- Session 1开启事务,执行
select * from account,获取MDL_SHARED_READ锁 - Session 2执行
alter table account add columnA int,请求MDL_EXCLUSIVE锁,由于Session 1持有SHARED_READ锁,Session 2进入等待队列 - Session 1此时执行INSERT操作,需要将自身持有的MDL_SHARED_READ升级为MDL_SHARED_WRITE锁,但MySQL的MDL锁等待队列遵循**FIFO(先进先出)**规则,升级请求会排在Session 2已提交的EXCLUSIVE锁请求之后,因此Session 1被阻塞
- 此时形成循环等待:Session 1等待Session 2的EXCLUSIVE锁释放,Session 2等待Session 1的SHARED_READ锁释放,触发MySQL死锁检测机制,最终回滚Session 1的事务
场景二无死锁的原因
- Session 1开启事务,执行
insert into account values ('test'),直接获取MDL_SHARED_WRITE锁 - Session 2执行ALTER TABLE请求EXCLUSIVE锁,进入等待队列
- Session 1后续执行INSERT或SELECT操作,不需要升级MDL锁(SHARED_WRITE已兼容读写操作),因此不会产生新的锁等待请求,自然不会形成循环等待,操作可正常执行
关键细节
MySQL 8.0中MDL锁的升级请求不会插队,必须排在已有的等待锁请求之后,这是场景一死锁的核心触发点。如果先持有SHARED_WRITE锁,后续操作无需升级锁,就不会触发等待链。
内容的提问来源于stack exchange,提问作者yk wu
相关产品推荐
相关产品推荐

