幻想读 通过多版本并发控制(MVCC)和间隙锁(Gap Lock)的组合也能防止幻读具体说下

总结:

间隙锁 (Gap Lock)

MySQL InnoDB 如何通过 MVCC 和间隙锁防止幻读

在 MySQL 的 InnoDB 存储引擎中,REPEATABLE READ 隔离级别通过 MVCC (多版本并发控制) 和间隙锁 (Gap Lock) 的组合机制确实能够防止幻读问题。下面详细解释这一机制的工作原理:

1. MVCC (多版本并发控制)

MVCC 是 InnoDB 实现非锁定读的关键技术:

  • 版本链:每行记录包含隐藏字段(DB_TRX_ID、DB_ROLL_PTR),形成版本链

  • ReadView:事务开启时创建一致性视图,决定能看到哪些版本的数据

  • 快照读:普通 SELECT 使用 MVCC,读取事务开始时的数据快照

MVCC 如何部分解决幻读

REPEATABLE READ 级别下:

  • 事务中的普通 SELECT 看到的是事务开始时的数据快照

  • 即使其他事务插入了新数据,当前事务也看不到这些"幻影行"

但仅靠 MVCC 不能完全防止幻读,因为:

  • 当前事务如果执行更新操作,会看到其他事务已提交的变更

  • 可能导致"看到"之前查询不存在的行

2. 间隙锁 (Gap Lock)

InnoDB 在 REPEATABLE READ 级别自动使用间隙锁来完全防止幻读:

  • 记录锁:锁定索引中的具体记录

  • 间隙锁:锁定索引记录之间的间隙

  • next-key 锁:记录锁 + 间隙锁的组合

间隙锁如何工作

当执行以下操作时会获取间隙锁:

sql

复制代码
SELECT * FROM table WHERE id > 100 FOR UPDATE;

锁定范围包括:

  1. id=100 的记录(如果存在)

  2. id > 100 的所有间隙

  3. 正无穷大的"上确界"

这样其他事务无法在锁定范围内插入新记录,从而防止幻读。

3. 实际防止幻读的机制组合

场景示例

事务A:

sql

复制代码
START TRANSACTION;
SELECT * FROM users WHERE age > 20;  -- 第一次查询(快照读)
-- 此时事务B插入 age=25 的新记录并提交
SELECT * FROM users WHERE age > 20;  -- 第二次查询(快照读,结果不变)
UPDATE users SET name='test' WHERE age > 20; -- 当前读,会看到事务B插入的记录
SELECT * FROM users WHERE age > 20;  -- 当前读,现在能看到新记录(幻读出现?)

InnoDB 的解决方案

  1. 第一次 SELECT:快照读,使用 MVCC 看到一致性视图

  2. UPDATE 语句:会尝试获取 age>20 条件的 next-key 锁

    • 锁定所有 age>20 的已有记录和间隙

    • 阻塞其他事务在 age>20 范围内的插入

  3. UPDATE 执行:采用"当前读"看到最新数据(包括事务B插入的记录)

  4. 后续 SELECT:在同一个事务中,所有读取都会基于最新更新后的视图

关键点:在 UPDATE 执行时获取的 next-key 锁会阻塞其他事务的插入操作,从而保证在事务提交前不会有新数据插入到查询范围内,从根本上防止了幻读。

4. 特殊情况说明

  1. 没有索引的列

    • 如果没有合适的索引,InnoDB 会锁全表

    • 性能极差但也能防止幻读

  2. 唯一索引

    • 对唯一索引的等值查询只使用记录锁

    • 范围查询仍使用 next-key 锁

  3. 半一致性读

    • 某些情况下 UPDATE 语句可能采用更宽松的锁定策略

    • 可通过设置关闭

5. 验证方法

可以通过以下实验验证:

会话A:

sql

复制代码
START TRANSACTION;
SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 获取next-key锁

会话B:

sql

复制代码
INSERT INTO users (age) VALUES (25); -- 会被阻塞

只有当会话A提交后,会话B的插入才能继续。

总结

InnoDB 在 REPEATABLE READ 级别防止幻读的完整机制是:

  1. 普通 SELECT 使用 MVCC 实现快照读

  2. 加锁的 SELECT (FOR UPDATE) 和写操作使用 next-key 锁

  3. next-key 锁(记录锁+间隙锁)阻塞其他事务在查询范围内的插入

  4. 保证事务期间查询范围的稳定性

这种组合既保证了读取性能(非阻塞读),又通过精细的锁定机制解决了幻读问题,是 InnoDB 设计的一大亮点。

相关推荐
焦虑的二狗3 小时前
Mac下载mysql
数据库·mysql·macos
weixin_456904273 小时前
控制台打开mysql服务报错解决办法
数据库·mysql
绅士范er4 小时前
【mysql 的安装及使用】
mysql
鲁子狄9 小时前
[笔记] 动态 SQL 查询技术解析:构建灵活高效的企业级数据访问层
java·spring boot·笔记·sql·mysql·mybatis
Dubhehug10 小时前
8.数据库索引
数据库·mysql·索引·索引分类·索引优缺点
冬夜戏雪10 小时前
阿里云ubuntu安装mysql docker容器(拉,运行,测试完整版)
mysql·ubuntu·阿里云
楼兰胡杨10 小时前
MySQL 更新字段的值为当前最大值加1
mysql
♡喜欢做梦10 小时前
【MySQL】索引
数据库·mysql
都叫我大帅哥10 小时前
📉 MySQL索引罢工事件簿:揭秘失效原因与优化起义方案
java·mysql