锁机制行锁表锁间隙锁

一、锁(Lock)— MVCC 不覆盖的最后一道防线

MVCC 解决了”读-写”冲突(读不加锁),但”写-写”冲突仍然要加锁。

1.1 行锁的两种模式

锁类型符号含义什么时候加
共享锁(S锁 / 读锁)S你可以读,别人也能读,但不能改SELECT ... LOCK IN SHARE MODE
排他锁(X锁 / 写锁)X只有你能读写,别人读和写都不行UPDATE, DELETE, INSERT, SELECT ... FOR UPDATE
 
兼容矩阵:
 
    | S | X |
 
  S | ✅| ❌|    ← S 和 S 可以共存(多个人同时读,互不干扰)
 
  X | ❌| ❌|    ← X 和谁都不能共存
 

1.2 间隙锁(Gap Lock)— RR 防幻读的补充手段

 
表中 id:1, 5, 10
 
 
 
SELECT * FROM user WHERE id > 5 FOR UPDATE;
 
 
 
这个语句不止锁 id=5 和 id=10 两行,还会锁 (5, 10) 和 (10, +∞) 之间的**间隙**
 
防止其他事务在这个范围内插入新行(幻读)
 

RR 用 MVCC 防快照读幻读,用间隙锁防当前读幻读。两者配合,RR 才能”几乎”防住幻读。

1.3 表锁 vs 行锁

表锁行锁
粒度锁整张表只锁某几行
并发度低(其他事务全等着)高(不冲突的行可以并行改)
开销小(管一张表就行)大(每行都要维护锁信息)
InnoDB 默认✅ 行锁(通过索引找到的行

关键易错点:InnoDB 行锁是锁索引。如果 WHERE 条件没走索引,行锁退化成表锁!

 
-- 假设 name 列没建索引
 
UPDATE user SET age = 30 WHERE name = '张三';
 
-- 虽然是行锁引擎,但因为 name 没索引,MySQL 不知道张三在哪一行
 
-- 只能扫全表 → 每一行都加锁 → 等于锁表
 
-- 其他事务改任何行都要等(灾难)
 

一图流(Mermaid)


flowchart TD

    A[写-写冲突] --> B{锁的粒度?}

    B -->|整张表| C[表锁: 并发低, 开销小]

    B -->|走索引的行| D[行锁: 并发高 InnoDB默认]

    D --> E{WHERE 走索引吗?}

    E -->|走索引| F[只锁命中的几行]

    E -->|没走索引| G[行锁退化成表锁 ⚠️]

    D --> H{RR 当前读?}

    H -->|是| I[Next-Key Lock = 行锁 + 间隙锁 防幻读]

速记卡(面试闪卡)

Q1:一句话讲清「锁机制行锁表锁间隙锁」到底是什么?

A:InnoDB 的行锁锁的是索引,WHERE 没走索引时退化成表锁;间隙锁在 RR 下防幻读。

Q2:六、锁(Lock)— MVCC 不覆盖的最后一道防线 —— 怎么理解?

A:像最后一道防盗门:MVCC(多版本并发控制)解决了读-写冲突(读不加锁),但写-写冲突仍要加锁(Lock),锁是并发的最后防线。

Q3:行锁的两种模式(S锁与X锁) —— 怎么理解?

A:像只读许可 vs 独占钥匙:共享锁 S(Shared Lock)允许多人同时读互不扰;排他锁 X(Exclusive Lock)独占,别人读写都不行。S 与 S 兼容,X 与谁都不兼容。

Q4:间隙锁 Gap Lock(RR 防幻读) —— 怎么理解?

A:像在空位上也贴封条:间隙锁(Gap Lock)锁住索引记录间的空隙,RR 隔离级别下当前读时防止别的事务 INSERT 产生幻读,配合行锁成 Next-Key Lock。

Q5:表锁 vs 行锁(行锁锁索引) —— 怎么理解?

A:像锁整栋楼 vs 锁一间房:表锁(Table Lock)粒度大、并发低、开销小;行锁(Row Lock)并发高、开销大,且锁的是索引——WHERE 没走索引就退化成表锁。

Q6:核心速记主线有哪些?

  • MVCC 解决读-写冲突(快照读不加锁),锁解决写-写冲突,是最后一道防线

  • 行锁两种模式:S 共享锁(多读兼容)、X 排他锁(独占,与谁都不兼容)

  • 间隙锁 Gap Lock 在 RR 下锁索引间隙,防 INSERT 幻读;行锁加间隙锁等于 Next-Key Lock

  • InnoDB 行锁锁的是索引,WHERE 没走索引时行锁退化成表锁(大坑)

口诀

A:MVCC 管读写,锁管写写防冲突

S 读共享 X 写独占,兼容矩阵记清楚

间隙锁封空位,RR 防幻靠它护

行锁锁索引,没走索引变表锁苦

相关链接