MySQL 锁分类完全指南
锁是数据库并发控制的"交通警察"。在 MySQL InnoDB 中,锁的分类维度非常多,可以按粒度 、模式 、算法 、意图 等多种维度划分。理解锁的分类,是读懂死锁日志、优化高并发性能的必修课。
锁的本质就是对共享资源(数据行、表、间隙)的访问权限控制。为了精确描述锁,InnoDB 引入了多维度的分类体系:
一、按锁粒度(作用范围)分类
这是最直观的分类方式,决定了锁锁住多大的数据范围。
| 锁粒度 | 作用对象 | 特点 | 典型场景 |
|---|---|---|---|
| 表级锁(Table Lock) | 整张表 | 开销小、加锁快、并发度低 | DDL 操作(ALTER TABLE)、LOCK TABLES 显式锁定 |
| 行级锁(Row Lock) | 索引记录(单行或多行) | 开销大、加锁慢、并发度最高 | InnoDB 默认的 UPDATE/DELETE 操作 |
| 页级锁(Page Lock) | 数据页(16KB) | 介于表锁和行锁之间 | 仅 BDB 引擎支持,InnoDB 不支持 |
关键认知 :InnoDB 的行锁实际上是锁在索引记录(Index Record)上,而不是直接锁在数据行上。如果表没有索引,行锁会退化为全表扫描并加表锁。
元数据锁(MDL,Metadata Lock)
这是 MySQL Server 层维护的一种特殊的表级锁,不属于存储引擎层,但非常重要。它用于保护表结构在事务执行期间不被修改(DDL 操作)。
- 读锁(MDL_SHARED) :执行
SELECT、INSERT、UPDATE、DELETE时自动获取,允许多个事务并发读取表结构。 - 写锁(MDL_EXCLUSIVE) :执行
ALTER TABLE、DROP TABLE等 DDL 操作时获取,阻塞所有其他读写操作。
生产隐患:长事务持有 MDL 读锁,会阻塞后续的 DDL 操作;而阻塞的 DDL 又会排队阻塞后续所有对该表的查询,瞬间引发连接池耗尽(常表现为"Waiting for table metadata lock")。
二、按锁模式(读写类型)分类
这是基于操作类型对锁的最基本划分,决定锁是用于"读"还是"写"。
2.1 共享锁(S 锁 / Shared Lock)
- 含义 :允许持有锁的事务读取一行数据,禁止修改。
- 兼容性:多个事务可以同时持有同一行数据的共享锁。
- 加锁方式 :
SELECT ... LOCK IN SHARE MODE(MySQL 8.0 后推荐用FOR SHARE)。
2.2 排他锁(X 锁 / Exclusive Lock)
- 含义:允许持有锁的事务修改一行数据,其他事务既不能读也不能写。
- 兼容性:同一时刻,只有一个事务能持有排他锁。
- 加锁方式 :
SELECT ... FOR UPDATE、UPDATE、DELETE、INSERT自动加。
兼容矩阵:
| 锁模式 | 共享锁 (S) | 排他锁 (X) |
|---|---|---|
| 共享锁 (S) | ✅ 兼容 | ❌ 冲突 |
| 排他锁 (X) | ❌ 冲突 | ❌ 冲突 |
三、按锁意图(表级门卫)分类
"意图锁"是表级锁,用于表明事务打算 在表中加行锁。其作用是在加表锁时快速判断表中是否已有行锁,避免逐行检查,提升效率。
3.1 意向共享锁(IS 锁)
事务打算给表中某些行加 S 锁 。执行 SELECT ... LOCK IN SHARE MODE 前,先加 IS 锁。
3.2 意向排他锁(IX 锁)
事务打算给表中某些行加 X 锁 。执行 UPDATE/DELETE/SELECT ... FOR UPDATE 前,先加 IX 锁。
完整兼容矩阵(含意向锁):
| 锁模式 | X | IX | S | IS |
|---|---|---|---|---|
| X(排他) | ❌ | ❌ | ❌ | ❌ |
| IX(意向排他) | ❌ | ✅ | ❌ | ✅ |
| S(共享) | ❌ | ❌ | ✅ | ✅ |
| IS(意向共享) | ❌ | ✅ | ✅ | ✅ |
注意 :意向锁相互兼容(IS 和 IX 兼容),不会阻塞正常的行级读写,只阻塞表级锁(如
LOCK TABLES ... WRITE)。
四、行锁的具体算法(InnoDB 核心特色)
这一层分类是 InnoDB 最精妙的部分,决定了如何精确锁定行。
| 行锁算法 | 作用范围 | 使用的隔离级别 | 作用 |
|---|---|---|---|
| 记录锁(Record Lock) | 单个索引记录(精确匹配的行) | RC、RR | 锁定已存在的某一行 |
| 间隙锁(Gap Lock) | 索引记录之间的间隙(不包含记录本身) | 仅 RR(以及更高) | 阻止其他事务在间隙内插入,防止幻读 |
| Next-Key Lock | 记录锁 + 间隙锁(包含记录本身及前间隙) | 仅 RR(以及更高) | InnoDB 在 RR 级别解决幻读的杀手锏 |
4.1 记录锁(Record Lock)
锁定索引树上的叶子节点 (即具体的索引记录)。如果使用 id = 10 进行查询,且 id 是主键或唯一索引,只会在 id=10 这条记录上加记录锁。
4.2 间隙锁(Gap Lock)
锁定索引记录之间的"空隙"。例如:SELECT * FROM t WHERE id BETWEEN 5 AND 10 FOR UPDATE,如果表中没有 id=7 的记录,但存在 5 和 10,间隙锁会封锁 (5,10) 这个区间,阻止插入 id=7 或 id=8 等。
间隙锁的特点 :它是"纯排他"的,两个间隙锁永远是冲突的(即使是两个读事务,也不能在同一个间隙上加间隙锁)。
4.3 Next-Key Lock
这是 InnoDB RR 级别的"默认行锁模式"。它的锁范围是 (上一个记录值, 当前记录值] ,即间隙+该记录本身 。
例如,数据行有 1, 5, 10,对 id=5 加锁,实际上是锁住了 (1, 5] 区间,阻止在区间 (1,5] 内插入新值(如 2,3,4,5 均被锁)。
RC 与 RR 的锁算法差异:
- RC 级别 :仅记录锁,无间隙锁。因此不会阻塞其他事务插入新数据,并发性能更好,但可能出现幻读。
- RR 级别 :默认 Next-Key Lock,但当查询条件命中最优索引(如主键唯一等值匹配)时,Next-Key Lock 会退化为记录锁。
五、其他特殊锁
5.1 插入意向锁(Insert Intention Lock)
这是一种特殊的间隙锁 ,由 INSERT 操作触发。它不是用来阻塞插入的,而是用于协调不同插入操作之间的间隙冲突。
- 原理 :事务 A 在
(10, 20)间隙插入id=15,会在该间隙加"插入意向锁"(IX 的变种)。 - 冲突规则 :多个事务可以在同一个间隙插入不同位置 的数据(如插入 12 和 18),它们不会冲突;但如果插入位置重复,则会被排他锁阻塞。
5.2 自增锁(AUTO-INC Lock)
为了保证自增主键(AUTO_INCREMENT)的连续性和唯一性,InnoDB 提供了自增锁机制。
- 工作原理:当一个事务向有自增列的表插入数据时,InnoDB 会获取一个特殊的表级锁,直到插入完成。
- 参数控制 :
innodb_autoinc_lock_mode可设为 0(传统表锁)、1(连续,默认)、2(交错,最大并发,适合ROW复制模式)。
5.3 谓词锁(Predicate Lock)------ 空间索引专用
用于 SPATIAL INDEX(空间索引)的场景,目前使用较少。
六、基于策略的分类(应用视角)
从应用开发的角度,锁也分为悲观锁和乐观锁。这并非 MySQL 原生锁类型,而是应用层对锁机制的使用策略。
| 策略 | 实现方式 | 适用场景 |
|---|---|---|
| 悲观锁 | 直接使用 SELECT ... FOR UPDATE(X 锁) |
写冲突频繁,如库存扣减 |
| 乐观锁 | 使用 version 字段或时间戳(不加原生锁) |
读多写少,冲突率低 |
七、锁信息的查看与诊断(实战)
当发生死锁或锁等待时,学会查看锁信息至关重要。
7.1 当前锁持有与等待(MySQL 8.0 推荐)
sql
-- 查看当前持有的锁(替代旧版 INNODB_LOCKS)
SELECT * FROM performance_schema.data_locks\G;
-- 查看锁等待关系(谁在等谁)
SELECT * FROM performance_schema.data_lock_waits\G;
7.2 查看 InnoDB 整体状态(含死锁详情)
sql
SHOW ENGINE INNODB STATUS\G;
-- 重点查看 "TRANSACTIONS" 部分中的 "LATEST DETECTED DEADLOCK"
7.3 查看当前事务信息
sql
SELECT * FROM information_schema.innodb_trx\G;
八、核心总结与实战建议
| 分类维度 | 核心要点 |
|---|---|
| 粒度 | 行锁(InnoDB 默认) > 表锁(DDL/MDL) |
| 模式 | S 锁(读)与 X 锁(写)互斥,但 S 锁之间兼容 |
| 意图 | IS/IX 是表级门卫,用于快速判断表冲突,相互兼容 |
| 算法(行级) | RC 只有记录锁;RR 有 Next-Key(记录+间隙),但唯一索引等值匹配时退化为记录锁 |
| 特殊锁 | 插入意向锁(协调并发插入)、自增锁(保证自增连续性) |
| 应用策略 | 悲观锁(FOR UPDATE)适合写多;乐观锁(版本号)适合读多 |
生产环境黄金法则:
- 隔离级别选 RC 可以大幅减少间隙锁,从而降低死锁概率,适合高并发 OLTP 系统。
- 索引是关键,没索引的行锁会升级为表锁,瞬间摧毁并发能力。
- 长事务是万恶之源,它会让 MDL 锁和行锁长时间不释放,阻塞整个系统。
- 死锁不可怕,不重试才可怕 ------应用层必须捕获死锁异常(
1213)并自动重试。