5.4.1 锁分类

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) :执行 SELECTINSERTUPDATEDELETE 时自动获取,允许多个事务并发读取表结构。
  • 写锁(MDL_EXCLUSIVE) :执行 ALTER TABLEDROP 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 UPDATEUPDATEDELETEINSERT 自动加。

兼容矩阵

锁模式 共享锁 (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=7id=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)适合写多;乐观锁(版本号)适合读多

生产环境黄金法则

  1. 隔离级别选 RC 可以大幅减少间隙锁,从而降低死锁概率,适合高并发 OLTP 系统。
  2. 索引是关键,没索引的行锁会升级为表锁,瞬间摧毁并发能力。
  3. 长事务是万恶之源,它会让 MDL 锁和行锁长时间不释放,阻塞整个系统。
  4. 死锁不可怕,不重试才可怕 ------应用层必须捕获死锁异常(1213)并自动重试。
相关推荐
Dxy12393102167 小时前
Python 如何使用 MySQL 的事务
python·mysql
ZJH__GO9 小时前
网络编程v4pro--实现聊天室文件传输功能
java·服务器·网络·计算机网络
程序员黑豆9 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
命运之光9 小时前
【C语言完整代码】就诊信息管理系统
java·c语言·开发语言
marvelyu10 小时前
每天10分钟学会OceanBase系列(Day 20):跨机房容灾实战——构建多数据中心高可用架构
java·大数据·数据库
ZCBUS实时计算11 小时前
金融证券实时数仓建设实践:轻量化实时计算平台落地,实现交易数据端到端秒级处理
大数据·数据库·数据仓库·金融·flink·dba·etl
zd20057211 小时前
海洋微生物数据库
数据库·宏基因组
海上小飞龙11 小时前
Redis 分布式锁原理:从 SET NX EX 到 Redisson 看门狗
数据库·redis·分布式
AI人工智能+电脑小能手11 小时前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦
BUG指挥官11 小时前
Sa-Token和Spring Security对比
java·后端·spring