知识卡片
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不仅仅锁定查询涉及的行,还会对索引中
的间隙进行锁定,以防止幻影行的插入。