谈谈 InnoDB 中的表级锁、页级锁、行级锁?

#中间件#面试八股

谈谈 InnoDB 中的表级锁、页级锁、行级锁?

结论:锁粒度是「并发度与开销」的权衡。表级锁开销小、冲突高;行级锁开销大、并发高;页级锁介于两者之间,但 InnoDB 根本没有页锁——它是 BDB 引擎的历史产物。InnoDB 的完整答案是:行级锁为主,配合意向锁实现表级与行级的共存,另有 MDL 与 AUTO-INC 锁处理元数据与自增。

一、三种粒度

粒度代表引擎加锁开销冲突概率并发度典型场景
表级锁MyISAM、MEMORY小高低全表统计、DDL
页级锁BDB(已废弃)中中中历史引擎,InnoDB 不使用
行级锁InnoDB大低高OLTP 点更新

InnoDB 里「表级锁」依然存在,但它主要服务于「让表锁与行锁能共存」——这就是意向锁。

二、InnoDB 锁的全景

InnoDB 锁
├── 行级锁(锁索引记录,不是锁行本身)
│   ├── 记录锁 Record Lock       :锁住某条索引记录
│   ├── 间隙锁 Gap Lock          :锁住两条记录之间的开区间
│   ├── 临键锁 Next-Key Lock     :记录锁 + 前面的间隙(左开右闭)
│   └── 插入意向锁 Insert Intention:插入前的间隙锁,等待时不阻塞其他插入意向
├── 表级锁
│   ├── 意向共享锁 IS / 意向排他锁 IX :声明「我要在某行上加锁」
│   ├── AUTO-INC 锁              :自增值分配
│   └── MDL 元数据锁              :保护表结构,DML 取共享、DDL 取排他
└── 其他
    ├── 谓词锁(空间索引)
    └── 页级锁:InnoDB 无

三、next-key lock 的加锁规则

在可重复读下,UPDATE/DELETE/SELECT ... FOR UPDATE 走非唯一索引或范围条件时,加的是临键锁,用来阻止区间内的插入(防幻读)。

-- 假设 id 有 1、5、10 三条记录
BEGIN;
SELECT * FROM t WHERE id BETWEEN 3 AND 8 FOR UPDATE;

-- 加锁范围(左开右闭):
--   (-∞, 1] 之外的 (1, 5] 与 (5, 10] 被锁住
--   插入 id=4 / id=6 都会被阻塞

查看实际加锁情况:

SELECT engine_transaction_id, object_name, index_name,
       lock_type, lock_mode, lock_data
FROM performance_schema.data_locks
ORDER BY engine_transaction_id;

lock_mode 里出现 X,GAP 是间隙锁,X,REC_NOT_GAP 是纯记录锁,X 通常就是临键锁。

四、不同隔离级别下的差异

隔离级别间隙锁行锁范围说明
读未提交无最小几乎不加读锁
读已提交无仅命中行锁范围小、死锁少
可重复读有命中行 + 间隙默认级别,防幻读
串行化有读也加共享锁并发最低
-- 切到 RC 观察:同样的范围更新,间隙锁消失
SET SESSION TRANSACTION ISOLATION LEVEL READ-COMMITTED;

五、几类容易混淆的「表锁」

  • 意向锁:由 InnoDB 自动加,SELECT ... FOR UPDATE 前先给表加 IX。它的作用是让「表级请求」不必逐行检查是否有人持行锁,直接看意向锁即可。
  • AUTO-INC 锁:innodb_autoinc_lock_mode 控制,0 传统表锁、1 连续、2 交错(8.0 默认)。批量插入时模式 0/1 会阻塞并发插入。
  • MDL:DDL 与 DML 之间的协调者。长事务持有 MDL 共享锁时,ALTER TABLE 会等待,并把它后面的所有请求一起堵住——这也是「加索引锁表」的真实原因。

常见追问

  • InnoDB 有页级锁吗? 没有。页级锁是 BDB 引擎的概念,InnoDB 只有行级与表级(含意向锁、MDL、AUTO-INC)。
  • 行锁锁的是什么? 锁的是索引记录。没有索引时,扫描到的每一行都会被锁,表现为「锁表」。
  • 意向锁会阻塞谁? 只与表级锁互斥:LOCK TABLES ... WRITE 需要等待所有意向锁释放。
  • 间隙锁一定降低并发吗? 会,插入被阻塞、死锁概率上升;所以高并发写入常把隔离级别降为 RC。
  • SELECT 会加锁吗? 普通快照读不加锁;FOR UPDATE/FOR SHARE 与串行化下的读会加锁。
  • 如何减少锁冲突? 走索引缩小范围、缩短事务、固定加锁顺序、必要时降级到 RC。

小结

回答这题的关键是「纠偏 + 补全」:先指出 InnoDB 没有页锁,再说明它用行级锁保证并发、用意向锁让表级与行级共存、用 MDL 保护结构、用 AUTO-INC 锁管理自增。最后补一句「行锁锁的是索引记录,没索引就退化成锁表」,基本就到位了。

笔记加载中…