MySQL:意向锁与兼容性/MySQL中的锁加在什么上?/innodb中锁的底层是怎么实现的?

这次面试被问到了"说一下意向锁",以及后续的"MySQL 里的锁是加在什么上的,底层是如何做的"。复盘时发现,我对意向锁的理解停留在表面,尤其是 S、X、IS、IX 的兼容关系部分,回答得太笼统,缺乏深度。以下是重新整理的思路,力求把问题讲透。

意向锁的本质与作用

意向锁(Intention Lock)是 MySQL InnoDB 引擎中一种表级锁 ,它的核心作用是提高锁冲突检测的效率 ,在行锁和表锁共存的场景下充当"桥梁"。InnoDB 支持细粒度的行锁,但也允许表级锁(比如显式的 LOCK TABLES 或某些 DDL 操作)。如果一个事务想对整个表加锁,逐行检查是否有锁冲突显然不现实,意向锁就解决了这个问题。

意向锁有两种:

  • 意向共享锁(IS Lock):表示事务打算在某些行上加共享锁(S Lock)。
  • 意向排他锁(IX Lock):表示事务打算在某些行上加排他锁(X Lock)。

它的逻辑是:当事务在行上加锁前,先在表上加一个对应的意向锁,作为"意向声明"。这样,其他事务在尝试加表级锁时,只需检查表上的锁状态,就能快速判断是否会冲突。

S、X、IS、IX 的兼容关系(全面解析)

面试时我提到兼容性矩阵,但只列了部分情况,显得不够严谨。实际上,S(共享锁)、X(排他锁)、IS(意向共享锁)、IX(意向排他锁)之间的兼容关系可以用一个完整的矩阵来表示:

S X IS IX
S
X
IS
IX
逐项解析:
  1. S 和 S 兼容:多个事务可以同时持有表级共享锁(S Lock),因为共享锁允许并发读。
  2. S 和 X 不兼容:表级共享锁和排他锁冲突,因为 X 锁要求独占资源。
  3. S 和 IS 兼容:IS 表示"打算加行级 S 锁",不会影响表级 S 锁,因为两者都是读操作的意向。
  4. S 和 IX 不兼容:IX 表示"打算加行级 X 锁",而表级 S 锁不允许任何写操作,所以冲突。
  5. X 和任何锁都不兼容:表级 X 锁是最高权限的锁,无论是 S、X、IS 还是 IX,都会被阻塞。
  6. IS 和 IS 兼容:多个事务都可以声明"打算加行级 S 锁",互不干扰。
  7. IS 和 IX 兼容:IS 和 IX 是表级意向锁,本身不直接锁资源,只表示意向,所以兼容。但如果具体到行级,S 和 X 会冲突。
  8. IX 和 IX 兼容:同样,IX 只是表级意向,多个事务可以同时声明"打算加行级 X 锁"。
为什么 IS 和 IX 兼容?

这点我之前没讲清楚。IS 和 IX 的兼容性只在表级成立,因为意向锁本身不锁具体数据,只是"声明"。真正的冲突发生在行级锁上。比如:

  • 事务 A 加了 IX,想对行 R1 加 X 锁。
  • 事务 B 加了 IS,想对行 R2 加 S 锁。 表级上看,IX 和 IS 兼容,事务可以继续。但如果 R1 和 R2 是同一行,行级的 X 和 S 会冲突,导致阻塞。

举个实际例子

假设有个表 users,字段有 idname

  1. 事务 A 执行 SELECT ... FOR SHARE(加行级 S 锁),InnoDB 会在表上加 IS 锁。
  2. 事务 B 执行 UPDATE ... WHERE id = 5(加行级 X 锁),表上加 IX 锁。
  3. 事务 C 想执行 LOCK TABLES users WRITE(表级 X 锁)。

此时,事务 C 检查表上的锁,发现有 IS 和 IX,表明表内有行被锁,直接阻塞,不需要逐行检查。这就是意向锁的高效之处。

MySQL 里的锁是加在什么上的?

锁的加锁对象因类型而异:

  • 表锁:直接加在表上,比如意向锁(IS、IX)或显式表锁。
  • 行锁 :加在索引记录上。InnoDB 的行锁依赖索引实现:
    • 有索引时,锁加在对应的索引项上(主键、唯一索引或二级索引)。
    • 无索引时,退化到锁整个表,或者用隐藏的聚簇索引(row id)。
  • 间隙锁和 Next-Key Lock:锁住索引间的范围,防止幻读。

比如 UPDATE users SET name = 'Tom' WHERE id = 5,锁会加在 id = 5 的索引记录上。如果 id 没索引,InnoDB 会扫描全表,锁可能退化为表锁。

底层是如何做的?

这部分我之前回答得很弱,现在补充一些细节:

  1. 锁数据结构 :InnoDB 在内存中维护一个锁管理器,每个锁对象包含:
    • 锁类型(S、X、IS、IX 等)。
    • 锁资源(表或索引记录的指针)。
    • 事务 ID。
  2. 加锁过程
    • 行锁通过 B+ 树定位索引记录,在内存中为该记录创建锁结构。
    • 表锁(包括意向锁)记录在表的元数据中。
  3. 冲突检测:锁管理器用哈希表或链表跟踪所有锁,当新锁请求到来时,检查兼容性矩阵。
  4. 等待与死锁
    • 如果锁冲突,请求者进入等待队列。
    • InnoDB 用等待图(wait-for graph)检测死锁,比如事务 A 等 B,B 等 A,会回滚一个事务。
  5. 存储开销:锁信息存在内存中,事务提交后释放。如果内存不足,可能触发 OOM。

底层具体代码我没研究过,但大致猜想是用 C 实现的锁链表或哈希表,结合 B+ 树的索引定位机制。

反思与改进

这次复盘让我发现,S、X、IS、IX 的兼容关系不只是背矩阵,还得理解背后的设计逻辑。意向锁看似简单,但结合行锁和表锁的协作场景,复杂度一下就上来了。下次面试如果再遇到,我会先讲定义,再用矩阵和例子把兼容性讲透,最后简单提一下底层实现。接下来打算看一下 InnoDB 源码的锁模块,至少搞清楚锁结构的定义,争取下次不留遗憾。

相关推荐
石榴树下4 分钟前
00. 马里奥的 OAuth 2 和 OIDC 历险记
后端
uhakadotcom4 分钟前
开源:subdomainpy快速高效的 Python 子域名检测工具
前端·后端·面试
似水流年流不尽思念21 分钟前
容器化技术了解吗?主要解决什么问题?原理是什么?
后端
Java水解22 分钟前
Java中的四种引用类型详解:强引用、软引用、弱引用和虚引用
java·后端
i听风逝夜23 分钟前
看好了,第二遍,SpringBoot单体应用真正的零停机无缝更新代码
后端
柏油1 小时前
可视化 MySQL binlog 监听方案
数据库·后端·mysql
舒一笑2 小时前
Started TttttApplication in 0.257 seconds (没有 Web 依赖导致 JVM 正常退出)
jvm·spring boot·后端
M1A12 小时前
Java Enum 类:优雅的常量定义与管理方式(深度解析)
后端
AAA修煤气灶刘哥3 小时前
别再懵了!Spring、Spring Boot、Spring MVC 的区别,一篇讲透
后端·面试
柏油3 小时前
MySQL 字符集 utf8 与 utf8mb4
数据库·后端·mysql