MYSQL RR 解决“脏读+不可重复读“和“幻读“的本质区别
RR 解决脏读 不可重复读 和 RR 解决幻读MVCC 解决快照读。这两个是否矛盾一、先说结论完全不矛盾问题解决机制解决哪类读脏读 不可重复读MVCCReadView 复用单行读幻读MVCC快照读范围读幻读当前读Next-Key Lock范围读 锁关键洞察MVCC 一个机制既解决了不可重复读又解决了快照读的幻读两种场景都是读快照RR 下 ReadView 复用 → 看不到其他事务的新数据不可重复读和幻读本质相同都是读不到其他事务新数据——单行级别就是不可重复读范围级别就是幻读二、3 大问题本质不可重复读单行被修改-- 老哥的报表例子 BEGIN; SELECT balance FROM account WHERE id 1; -- 第 1 次读balance1000 -- 其他事务UPDATE account SET balance 900 WHERE id 1; COMMIT; SELECT balance FROM account WHERE id 1; -- 第 2 次读balance900 -- ⚠️ 同一事务同一行结果不同 → 不可重复读幻读范围被插入-- 老哥的批量报表例子 BEGIN; SELECT * FROM account WHERE balance 1000; -- 第 1 次查5 条 -- 其他事务INSERT INTO account (balance) VALUES (2000); COMMIT; SELECT * FROM account WHERE balance 1000; -- 第 2 次查6 条 -- ⚠️ 同一事务同一范围行数不同 → 幻读本质对比维度不可重复读幻读关注点单行被修改范围被插入/删除结果变化值变了行数变了底层机制都是 MVCC ReadView 复用都是 MVCC ReadView 复用区别同一行的不同版本范围中多了/少了行**所以——MVCC 解决不可重复读自然就解决了快照读的幻读。因为它们都是看不到新数据。三、MVCC 一个机制两个效果RR 隔离级别下 - 事务开始第一次 SELECT 时创建 ReadView - 整个事务期间复用这个 ReadView - 看不到 ReadView 之后才提交的数据 ↓ 看不到被修改的数据不可重复读解决 ↓ 看不到被插入的数据幻读解决一句话总结MVCC 看不到 ReadView 之后的新数据自然就既解决不可重复读修改又解决幻读插入。一个机制两个效果。四、为什么会有矛盾的感觉感觉矛盾可能是因为4 大隔离级别的标准定义隔离级别解决没解决RU无脏读 / 不可重复读 / 幻读RC脏读不可重复读 / 幻读RR标准脏读 /不可重复读幻读SE全部无标准 SQL 定义里 RR 是不解决幻读的但MySQL InnoDB 通过 MVCC Next-Key Lock 突破了标准定义几乎解决了幻读。所以老哥看到的两个说法1.RR 解决脏读 不可重复读标准 SQL 定义2.RR 解决幻读MySQL InnoDB 实际实现它们都对只是描述的角度不同角度 1按标准 SQL 定义RR 不解决幻读角度 2按 MySQL InnoDB 实现RR通过 MVCC Next-Key Lock 几乎解决幻读五、RR 解决幻读的 2 大机制MVCC 解决快照读 Next-Key Lock 解决当前读机制 1MVCC 解决快照读的幻读普通 SELECT-- RR 隔离级别 普通 SELECT BEGIN; -- 创建 ReadView假设 m_ids[2,3,4,5], min2, max6 SELECT * FROM account WHERE balance 1000; -- 看到 5 条 -- 期间事务 6 INSERT 并提交一条 -- 事务 6 的 trx_id6 max6不在 ReadView 范围内 SELECT * FROM account WHERE balance 1000; -- 仍看到 5 条ReadView 复用 COMMIT;关键ReadView 看不到 trx_id max_trx_id 的事务提交的数据所以新插入的行看不到幻读解决。机制 2Next-Key Lock 解决当前读的幻读SELECT FOR UPDATE-- RR 隔离级别 当前读 BEGIN; SELECT * FROM account WHERE balance 1000 FOR UPDATE; -- 加 Next-Key Lock -- 锁定范围balance 1000 涉及的索引区间 -- 期间其他事务尝试 INSERT balance 1000 INSERT INTO account (balance) VALUES (2000); -- ⚠️ 阻塞 SELECT * FROM account WHERE balance 1000; -- 仍看到 5 条 COMMIT;关键Next-Key Lock 记录锁 间隙锁锁定了可能插入的位置防止新数据插入。六、面试话术不矛盾。RR 解决脏读 不可重复读是标准 SQL 定义——按 SQL 标准 RR 不解决幻读。RR 解决幻读是MySQL InnoDB 实际实现——InnoDB 用MVCC 解决快照读幻读ReadView 复用用Next-Key Lock 解决当前读幻读记录锁间隙锁。核心洞察不可重复读和幻读本质相同——都是看不到其他事务的新数据。单行级别叫不可重复读范围级别叫幻读。MVCC 一个机制同时解决。七、项目实战对照RR 默认Transactional // RR public void generateReport(Report report) { // 1. 单行查不可重复读解决 Report existing reportMapper.selectById(report.getId()); // 整个事务期间existing 不会被其他事务的修改影响 // 2. 范围查幻读解决 ListReport pending reportMapper.selectByStatus(pending); // 整个事务期间pending 不会被其他事务的插入影响 // 3. 当前读Next-Key Lock 解决幻读 ListReport all reportMapper.selectByStatusForUpdate(pending); // 加锁其他事务不能 INSERT statuspending 的报表 }RC 查询Transactional(isolation Isolation.READ_COMMITTED) // RC public ListMaskedData queryLatestMasked() { // 1. 单行查能重复读 → 老数据— mpvs 不在意 // 2. 范围查能幻读 → 新数据— mpvs 在意要看最新 return dataMaskMapper.selectAll(); // 看到最新 }用 RR不可重复读 幻读都不能有同一报表要一致。用 RC能看到最新有些任务要看最新数据。八、记忆口诀不可重复读和幻读本质都是看不到新数据单行级别 不可重复读范围级别 幻读MVCC 一个机制同时解决两个标准 SQL RR 不解决幻读MySQL InnoDB 解决了快照读靠 MVCC当前读靠 Next-Key Lock