面试题库:MySQL 的锁类型有哪些?
题目
请介绍 MySQL 中有哪些锁类型,以及它们各自的作用和使用场景。
参考答案
开篇先打个预防针
MySQL 的锁机制,说简单也简单,说复杂嘛——复杂到能单独写一本书。面试的时候如果你一上来就背"全局锁、表级锁、行级锁......",面试官大概率会微微一笑,然后追问一句:"那行级锁又有哪几种?"
所以咱换个思路——从大到小,逐层剥洋葱。先看大局,再钻细节,保证你自己讲得清、对方听得懂。
一、按粒度分层——三大"祖宗锁"
MySQL 的锁如果按照锁的范围来分,从上到下有三个层级。就像公司的管理架构——管全公司的、管部门的、管个人的。

1. 全局锁——"整库冻结"
FLUSH TABLES WITH READ LOCK; -- 全局读锁,整个数据库只读
UNLOCK TABLES; -- 释放
使用场景:全库逻辑备份。现在有了 mysqldump --single-transaction,基本不用这招了。面试里提一嘴"我知道有这么个东西,但 MVCC 时代有了更好的替代方案"就够了。
2. 表级锁——"这张表我包了"
LOCK TABLES user READ; -- 表级读锁(共享锁)
LOCK TABLES user WRITE; -- 表级写锁(排他锁)
UNLOCK TABLES;
表锁的优点是开销小、不会死锁(一次性拿锁,没有逐行争抢)。缺点是并发低——一个人锁了整张表,其他人全得排队。
除了显式的 LOCK TABLES,还有两类隐式的表级锁很常见:
元数据锁(MDL,Metadata Lock):MySQL 5.5 引入,不需要你手动加。CRUD 时自动加 MDL 读锁,改表结构(ALTER TABLE)时自动加 MDL 写锁。如果你在线上执行了一个大表 DDL 被阻塞,八成就是有人在跑慢查询占着 MDL 读锁不撒手。
自增锁(AUTO-INC Lock):跟自增主键(AUTO_INCREMENT)配合使用。插入数据时自动加,保证自增值不会重复。MySQL 8.0 后有了轻量级自增锁,性能好很多。
3. 行级锁——"精准制导"
InnoDB 才支持行锁,MyISAM 是不行的——这也是一般项目都选 InnoDB 的核心原因之一。
行锁由存储引擎层实现,开销比较大(每行都要维护锁信息),但并发度高。行锁下面还有细分,这是面试重灾区,我们稍后展开。
二、按读写属性分——共享锁 vs 排他锁
这俩概念贯穿 MySQL 所有锁类型,先搞明白它们:
| 类型 | 别称 | S/X 缩写 | 一句话 |
|---|---|---|---|
| 共享锁(Shared Lock) | 读锁 | S 锁 | 你能读,我也能读,但谁也不能写 |
| 排他锁(Exclusive Lock) | 写锁 / 互斥锁 | X 锁 | 我独占,别人读也不行、写也不行 |
兼容矩阵(这是面试里能画出来的加分项):
| S 锁 | X 锁 | |
|---|---|---|
| S 锁 | ✅ 兼容 | ❌ 冲突 |
| X 锁 | ❌ 冲突 | ❌ 冲突 |
-- 加共享锁(S 锁)
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE; -- MySQL 8.0 改名了:
SELECT * FROM user WHERE id = 1 FOR SHARE; -- 推荐写法
-- 加排他锁(X 锁)
SELECT * FROM user WHERE id = 1 FOR UPDATE;
一个很实用的面试小口诀:S 锁可以共存,X 锁六亲不认。
三、行级锁的细分类——面试高发区
这里是面试官最爱深挖的地方,也是 间隙锁文档 中详细讲过的内容。这里做个整体梳理:
行级锁
├── Record Lock(记录锁) → 锁住一条具体的索引记录
├── Gap Lock(间隙锁) → 锁住记录之间的空隙,防插入
├── Next-Key Lock(临键锁) → Record + Gap,前开后闭区间
└── Insert Intention Lock → 插入意向锁,特殊的间隙锁
Record Lock(记录锁)——最老实的那种
-- 锁住 id=10 这一行
SELECT * FROM user WHERE id = 10 FOR UPDATE;
锁住索引上的一条记录。如果表没有索引,InnoDB 会隐式创建聚簇索引来做行锁。
注意:行锁是加在索引上的,不是加在数据行上的。这句话面试说出来,直接上分。
Gap Lock(间隙锁)——防止幻读的功臣
锁住索引记录之间的空隙,防止别人在这些空隙里插入数据。
间隙锁只在 RR(可重复读) 隔离级别下生效。RC 级别下间隙锁直接退场。详情见 上一篇文章。
Next-Key Lock(临键锁)——InnoDB 的默认行锁
数据分布: 1 5 10 15
索引: ●——(1,5)——●——(5,10)——●——(10,15)——●——(15,+∞)
Next-Key Lock = 区间 + 右端点记录
(1,5] (5,10] (10,15] (15,+∞]
默认情况下,InnoDB 加的锁都是 Next-Key Lock。在某些情况下会退化:
- 唯一索引等值查询且记录存在 → 退化为 Record Lock
- 唯一索引等值查询且记录不存在 → 退化为 Gap Lock
Insert Intention Lock(插入意向锁)——隐形的排队哨兵
这是一种特殊的间隙锁。当你要往某个间隙插入数据时,会先在这个间隙上加一个"插入意向锁"。它跟间隙锁的规则是:
多个事务在同一个间隙加插入意向锁 → 互不冲突 插入意向锁遇到间隙锁 → 阻塞
说白了,插入意向锁的作用是让同间隙的插入操作能并发,但又保证在有人锁住整个间隙时插不进去。
四、按锁定方式分——隐式锁 vs 显式锁
| 类型 | 触发方式 | 示例 |
|---|---|---|
| 显式锁 | 你手动加的 | SELECT ... FOR UPDATE、LOCK TABLES |
| 隐式锁 | MySQL 自动加的 | DML 语句自动加行级 X 锁、MDL 锁、自增锁等 |
大多数时候你不需要操心隐式锁,MySQL 替你打理得很好。但了解它有个好处——排查线上锁问题的时候,你知道那些"看不见但确实存在的锁"在哪。
五、一张全景脑图
MySQL 锁体系
│
├── 按粒度分层
│ ├── 全局锁(FLUSH TABLES WITH READ LOCK)
│ ├── 表级锁
│ │ ├── 表锁(LOCK TABLES ... READ/WRITE)
│ │ ├── 元数据锁 MDL(自动,隐式)
│ │ └── 自增锁 AUTO-INC(自动,隐式)
│ └── 行级锁(InnoDB)
│ ├── Record Lock(记录锁)
│ ├── Gap Lock(间隙锁)
│ ├── Next-Key Lock(临键锁,默认)
│ └── Insert Intention Lock(插入意向锁)
│
└── 按读写属性分
├── 共享锁(S 锁,读锁)
└── 排他锁(X 锁,写锁)
六、常见面试追问
Q1:InnoDB 行锁是加在数据行上吗?
不是。行锁是加在索引上的。如果你的一条 UPDATE 语句的 WHERE 条件不走索引,那 InnoDB 没办法精准定位到具体行,就会锁住整张表的所有行(实际是锁住所有行的聚簇索引记录),效果等同于表锁。
这也是为什么不走索引的 DML 在 RR 级别下尤其危险。
Q2:MyISAM 和 InnoDB 在锁上的核心区别?
| MyISAM | InnoDB | |
|---|---|---|
| 行级锁 | ❌ 不支持 | ✅ 支持 |
| 事务 | ❌ 不支持 | ✅ 支持 |
| 默认锁 | 表锁 | 行锁(Next-Key Lock) |
| 读-读 | 不阻塞 | 不阻塞 |
| 读-写 | 写优先,读被阻塞 | 通过 MVCC 读不阻塞写 |
一句话:InnoDB 能活到今天是靠 MVCC + 行锁这套组合拳。
Q3:MySQL 怎么检测死锁?
InnoDB 内部维护了一个等待图(Wait-For Graph),当节点间形成环时即死锁。InnoDB 会主动检测并回滚其中一个事务。可以通过 SHOW ENGINE INNODB STATUS\G 查看最近的死锁信息。
七、一句话总结
MySQL 的锁,从上到下管全局、管表、管行;从读写属性分共享和排他;行锁再细分记录、间隙、临键、插入意向。记住这个三层结构,任何锁问题都能往里套。
更多推荐


所有评论(0)