MVCC与锁联手:彻底搞懂MySQL如何解决幻读
关键词MySQL、InnoDB、MVCC、Next-Key Lock、幻读、间隙锁、并发控制、数据库原理大家好呀我是数据库小学妹我们之前学了两个重要的并发控制技术MVCC多版本并发控制让读不加锁锁机制让写不冲突。但是有个经典问题一直让很多新手头疼在可重复读RR隔离级别下MVCC不是已经避免了不可重复读吗为什么还会出现幻读传说中的“Next-Key Lock”到底是怎么和MVCC配合的今天我们就来揭开InnoDB 的终极秘密MVCC 和锁是如何配合解决“幻读”难题的。一、什么是幻读假设你运营一个电商系统订单表orders有status字段。现在有一个统计需求查询状态为“待支付”的所有订单并在这个事务里对这些订单做批量处理。幻读场景-- 事务A统计订单BEGIN;SELECT*FROMordersWHEREstatuspending;-- 查到2条-- 此时事务B插入了1条新的待支付订单-- 事务A再次执行同样的查询SELECT*FROMordersWHEREstatuspending;-- 查到3条COMMIT;同一个事务内两次查询结果集的行数不一致多了一条“幽灵”记录这就是幻读。 不可重复读 vs 幻读不可重复读同一行数据内容变了MVCC通过Read View解决幻读结果集的行数变了MVCC解决不了需要锁配合二、为什么MVCC挡不住幻读MVCC的核心机制是快照读普通SELECT它基于事务开始时的Read View来判断可见性。Read View一旦生成在整个事务中复用RR级别下所以已提交的新插入行的事务ID Read View的max_trx_id不会被当前事务看到。但问题在于MVCC只控制了“读”管不了“写”。如果事务A要修改或锁定那些“看不见”的新插入行就会触发当前读从而发现新行。MVCC能保证快照读没有幻读但无法阻止当前读SELECT ... FOR UPDATE/UPDATE/DELETE下的幻读。三、锁机制挺身而出Next-Key Lock登场为了解决幻读InnoDB引入了Next-Key Lock。它不是一个单独的锁而是行锁Record Lock 间隙锁Gap Lock的合体。Record Lock锁定某条具体记录比如锁住 id5 的行Gap Lock锁定索引记录之间的“间隙”防止其他事务在间隙里插入新记录Next-Key Lock锁定的是一个左开右闭的区间比如(5, 10]既锁住了记录又锁住了前面的空隙。实战Next-Key Lock如何防幻读回到订单表假设status列有索引值为pending的记录分布在索引上。当事务A执行SELECT*FROMordersWHEREstatuspendingFORUPDATE;InnoDB会做两件事对查询到的所有pending记录加上行锁Record Lock对pending值所在索引范围的前后间隙加上间隙锁禁止其他事务插入新的pending订单这样事务B就无法插入新的pending订单被间隙锁阻塞从而避免了幻读。 如果查询条件没有走索引InnoDB会升级为表级锁实际是锁全表的间隙并发性极差——所以防幻读也依赖索引设计。InnoDB 解决幻读的真相MVCC 负责解决“读写冲突”让你读快照Next-Key Lock 负责解决“写写冲突”和“幻读”让你插不进来。四、深度解析RR 级别下的“读”与“写”到底发生了什么为了让你更直观地理解我们用一个表格总结一下在 RR 级别下不同操作的底层逻辑操作类型使用的机制底层逻辑普通查询 SELECT *MVCC (快照读)不加锁直接读 Undo Log 里的历史版本所以读写不阻塞。锁定查询 SELECT … FOR UPDATENext-Key Lock (当前读)加锁这时候 MVCC 失效必须读最新的数据并且锁住记录和间隙。更新/删除UPDATE/DELETENext-Key Lock (当前读)同样是加锁读最新数据防止幻读和脏写。⚠️避坑时刻很多新手会发现在 RR 级别下有时候查不到幻读有时候又能查到。原因如果你用的是SELECT *快照读你永远看不到别人插入的新数据MVCC 在起作用。原因如果你用的是SELECT ... FOR UPDATE当前读你立刻就能看到新数据并且会被锁阻塞。五、实战验证如何看到间隙锁的效果开启两个会话事务A先执行BEGIN;SELECT*FROMordersWHEREidBETWEEN10AND20FORUPDATE;事务B尝试插入id 15INSERTINTOorders(id,status)VALUES(15,pending);你会看到事务B被阻塞直到事务A提交或回滚。这就是间隙锁在起作用。查看当前锁信息SHOWENGINEINNODBSTATUS\G输出中搜索LATEST DETECTED DEADLOCK或查看TRANSACTIONS段落可以看到gap lock字样。六、总结今天我们把数据库并发控制的最后一块拼图拼上了MVCC 是主角它负责让“读”变得飞快不用排队。Next-Key Lock 是保镖它负责在关键时刻写操作挡住“幻读”和“脏数据”。RR 隔离级别 是导演它安排 MVCC 和 锁 轮流上场既保证了数据一致又兼顾了性能六一句话总结MVCC 解决了“读旧数据”的问题锁解决了“写新数据”的问题。两者合体才是 InnoDB 高性能的真正秘密。 我是数据库小学妹一个用设计师思维学数据库的转行人。我们一起把复杂的技术变得简单有趣本文示例基于 MySQL 8.0 InnoDB。理解了这套机制你已经超越了 80% 的初级开发者。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2593488.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!