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 源码的锁模块,至少搞清楚锁结构的定义,争取下次不留遗憾。

相关推荐
fundroid1 小时前
Rust 为什么不适合开发 GUI
开发语言·后端·rust
可爱的霸王龙8 小时前
SpringBoot整合JWT
java·后端·jwt
爱的叹息8 小时前
Spring容器从启动到关闭的注解使用顺序及说明
java·后端·spring
蜡笔小祎在线学习9 小时前
小林coding-12道Spring面试题
java·后端·spring
知否技术9 小时前
Node登陆认证实战!10分钟手把手教会你!
后端·node.js
movee9 小时前
十分钟从零开始开发一个自己的MCP server(二)
后端·llm·mcp
movee9 小时前
十分钟从零开始开发一个自己的MCP server(一)
后端·llm·mcp
Adellle10 小时前
Java进阶
java·后端·面试
G探险者10 小时前
项目日志是否应该启用文件压缩?
运维·后端
Asthenia041210 小时前
Spring Boot 的自动装配原理:@EnableAutoConfiguration/AutoConfigurationImportSelector/条件
后端