跳转至

锁机制与死锁

核心问题:间隙锁是什么?死锁是如何产生的?如何避免?


它解决了什么问题?

MVCC 解决了读写冲突,但写写冲突仍需要锁来解决。理解锁机制能帮你: - 排查线上死锁报警 - 理解为什么 RR 隔离级别下某些操作会锁住大范围 - 在高并发写入场景做出正确的隔离级别选择


锁的分类

flowchart TD
    Lock[MySQL 锁] --> TableLock[表级锁]
    Lock --> RowLock[行级锁]

    TableLock --> TL1[表共享锁 S\nSELECT ... LOCK IN SHARE MODE]
    TableLock --> TL2[表排他锁 X\nSELECT ... FOR UPDATE]
    TableLock --> TL3[意向锁 IS/IX\n行锁前自动加,表示意图]

    RowLock --> RL1[记录锁 Record Lock\n锁定具体行]
    RowLock --> RL2[间隙锁 Gap Lock\n锁定索引间隙,防止插入]
    RowLock --> RL3[临键锁 Next-Key Lock\n记录锁+间隙锁,RR默认]

间隙锁详解

为什么需要间隙锁? 防止幻读——在 RR 隔离级别下,如果只锁定已有行,其他事务仍可在间隙中插入新行,导致同一事务两次查询结果不同。

-- 假设表中有 id: 1, 5, 10, 15
-- 执行以下查询(RR 隔离级别)
SELECT * FROM t WHERE id BETWEEN 5 AND 10 FOR UPDATE;

-- 加锁范围(临键锁):
-- (1, 5] + (5, 10] = 锁定 id 在 (1, 10] 范围
-- 其他事务无法在此范围内插入 id = 3、7、8 等值

为什么间隙锁只在 RR 级别存在:RC 级别不需要防止幻读(允许幻读),所以没有间隙锁,并发性更高。这也是为什么高并发写入场景有时会把隔离级别降到 RC——减少间隙锁的范围,降低死锁概率。

工作中的坑:间隙锁锁住大范围

-- 表中 id 只有 1, 5, 10
-- ⚠️ 以下语句在 RR 下会锁住 (5, +∞) 的间隙!
SELECT * FROM t WHERE id > 5 FOR UPDATE;
-- 原因:RR 级别下,范围查询会加临键锁,锁住查询范围内的所有间隙
-- 导致其他事务无法插入 id=6、7、8... 的数据,引发大量锁等待

死锁产生与检测

死锁产生的四个必要条件:互斥、占有并等待、不可剥夺、循环等待

sequenceDiagram
    participant T1 as 事务1
    participant T2 as 事务2

    T1->>T1: 锁定 id=1 的行
    T2->>T2: 锁定 id=2 的行
    T1->>T2: 请求锁定 id=2(等待)
    T2->>T1: 请求锁定 id=1(等待)
    Note over T1,T2: 死锁!InnoDB 检测到循环等待
    Note over T1,T2: 自动回滚 undo log 量较小的事务

InnoDB 死锁检测:InnoDB 有内置的死锁检测机制,检测到死锁后会自动回滚其中一个事务(通常是 undo log 量较小的那个),并返回错误 ERROR 1213: Deadlock found

为什么回滚 undo log 量小的事务:回滚代价最小,对业务影响最小。被回滚的事务可以重试,而代价大的事务已经做了很多工作,重试成本更高。


死锁排查

-- 查看最近一次死锁信息
SHOW ENGINE INNODB STATUS;

-- 输出中找到 LATEST DETECTED DEADLOCK 部分
-- 可以看到:
-- 1. 哪两个事务发生了死锁
-- 2. 各自持有的锁和等待的锁
-- 3. 哪个事务被回滚

避免死锁的实践

实践 说明
固定加锁顺序 所有事务按相同顺序访问资源,打破循环等待
缩短事务 减少锁持有时间,降低死锁概率
避免大事务 拆分为小事务,减少锁的范围和持有时间
精确查询条件 SELECT ... FOR UPDATE 时尽量精确条件,减少间隙锁范围
降低隔离级别 高并发写入场景考虑用 RC,消除间隙锁

面试高频问题

Q:间隙锁是什么?为什么需要间隙锁?

间隙锁锁定索引间隙(不存在的区间),防止其他事务在间隙中插入数据,从而防止幻读。只在 RR 隔离级别下存在,RC 级别没有间隙锁。

Q:如何排查死锁?如何避免死锁?

排查:SHOW ENGINE INNODB STATUS 查看最近一次死锁信息。避免:保持固定加锁顺序、缩短事务、减少锁范围、避免大事务。

Q:为什么高并发写入场景有时会把隔离级别降到 RC?

RC 级别没有间隙锁,锁的范围更小,死锁概率更低,并发性更高。代价是允许幻读,但很多业务场景可以接受。