知识卡片

InnoDB用"间隙锁"而非"快照检测"来防止幻读

普通读书笔记卡

内容

“可重复读”隔离级别下,同一个事务两次读同一批记录得到的结果要 保持一致——但这不只包括已存在记录的值不能被改,还包括不能凭空 “多出”或”少了”符合条件的记录(幻读)。要防止别的事务在自己 查询的范围内插入新记录,直觉的做法是给已经查到的那几行记录 加锁,但这样完全挡不住”插入一条全新记录”这种操作,因为新记录 根本不在”已查到的行”里,锁不到它。InnoDB的解法是间隙锁 (next-key locking):不只锁住查询命中的那些具体行,还把这些 行之间、以及查询范围边界处”没有被任何记录占据的间隙”本身也 锁起来——相当于把一个连续的键值区间整体封锁,而不是零散地锁住 几个孤立的点。这样一来,任何试图在这个区间内插入新记录的操作 都会被间隙锁挡住,因为”插入”这个动作本身要先在目标位置获得 间隙锁的许可,从根本上防止了幻影行的出现。这条路径和”用MVCC 快照来判断可见性、事后检测冲突再终止事务”(本session此前深入 讨论过的PostgreSQL/Greenplum可序列化快照隔离思路)是两种性质 完全不同的防幻读方案:一个是提前锁住”未来可能被插入的空间”, 从源头上不让冲突发生;另一个是允许写入先发生,靠版本快照和 读写依赖检测事后发现冲突、再终止其中一方。两条路径都能解决 同一个问题,但一个是”预防”思路(代价是更多的锁争用和潜在死锁), 一个是”事后纠正”思路(代价是可能出现串行化异常误报和事务回滚 重试),没有哪一个绝对更优,取决于系统更愿意为哪种代价买单。

参考来源

- 位置:《高性能MySQL:第3版》第1章"MySQL架构与历史"1.5.1节 "InnoDB存储引擎"(源文件: _epub-src/OEBPS/Text/part0008.xhtml) - 结论依据:原文明确"InnoDB采用MVCC来支持高并发,并且实现了 四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读), 并且通过间隙锁(next-key locking)策略防止幻读的出现。间隙锁 使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行 锁定,以防止幻影行的插入",直接说明间隙锁通过锁定索引间隙 (而非仅锁定已有行)来阻止新记录插入的机制。 - 原始内容:并且通过间隙锁(next-key locking)策略防止幻读的 出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中 的间隙进行锁定,以防止幻影行的插入。