MySQL如何防止脏读和幻读_事务隔离解决方案解析?

我记得有一次,我在一个周末的下午,去了一家小型的书店。
这家书店不大,但书籍种类还挺多,我打算找一本关于历史的书。
我走到书架前,拿起一本看起来挺有意思的,正准备翻开看看,突然有个中年男子走了过来,他拿起一本书,翻了几页后又放下了。
我有点好奇,心想:“他是不是也看中这本书了呢?”然后,我继续翻看我的书。

过了一会儿,那个男子又回来了,他问我:“这本书是你刚才看的吗?”我有点惊讶,因为我并没有告诉他我在看什么书。
我回答说:“是的,我在看这本书。
”他笑了笑,说:“那我就不拿这本书了,免得你一会儿又得找。

这个小小的场景让我想到,在数据库的世界里,也存在着类似的情况。
比如,脏读和幻读,就是数据库中可能出现的两种不一致性问题。
脏读就像那个男子突然介入我的阅读一样,他看到了我未提交的“阅读状态”,而这个状态在他介入后可能就改变了。
而幻读,则像是书店的书架,我在同一时间两次查看同一区域,结果书架上的书因为其他人的操作而发生了变化。

这就是为什么在数据库设计中,我们需要设置合适的事务隔离级别,就像书店的规则一样,确保每个顾客都能有一个相对独立的阅读空间。
不过,这也只是我一个小小的联想,实际应用中还有很多更复杂的考量。
等等,还有个事,我突然想到,如果书店里的人流量太大,即使是独立的阅读空间,也可能因为拥挤而影响阅读体验。
在数据库里,高并发也可能导致性能问题,所以还需要有好的锁机制来控制并发。
嗯,这又是一个值得深思的话题。

美团面试官:MySQL可重复读如何解决幻读问题?

结论: MySQL可重复读隔离级别通过MVCC和next-keylock解决幻读。
MVCC提供快照读,保证事务一致性视图不变。
next-keylock结合间隙锁和记录锁,锁定查询范围和间隙,防止插入。
但特殊情况下仍可能发生幻读,需注意事务处理方式。

面试官:MySQL的幻读是怎么被解决的?

对对对,MySQL 这事儿吧,挺复杂的。

你看啊,隔离级别,可重复读。
这个级别,它要避免幻读,靠的是啥?MVCC,多版本并发控制。
还有 next-key 锁。

先说快照读。
就是你普通 SELECT 语句。
这个 MVCC 起作用。
你事务一开始,第一个查,就建了个 ReadView,一致性视图。
后面你再查,都看这个视图的快照,不是最新的实时数据。

效果咋样?别人事务后面插入数据了,或者删了数据了,甚至提交了,你事务再查,结果还是跟刚开始查的时候一样。
不会变。
比如,事务 A 查了一次,再查一次,结果一样,没幻读。

但是!要是你用当前读,比如 UPDATE、DELETE,或者 SELECT ... FOR UPDATE 这种。
光靠 MVCC,不行,还是会有幻读。

为啥?因为当前读,它要看到最新的数据。
如果当前读不加锁,别的进程就能在你查询期间,往里面插入数据。
等你真正查的时候,就看到新插入的数据了,结果就变了。
这就是幻读。

那咋办?Next-key 锁。

这个锁是啥?是记录锁 + 间隙锁。
组合起来。

记录锁,就是锁住你查的那个索引记录本身。
防止别人修改或删除。
间隙锁,就是锁住记录和记录之间的那个空隙。
防止别人往里面插新数据。

组合起来,next-key 锁,就同时锁了记录,也锁了间隙。
比如你执行 SELECT ... FOR UPDATE,锁定那个范围,别的进程就不能在这个范围里插入数据了。
这样,当前读就不会看到别人插入的数据,就避免了幻读。

不过有特殊情况。
比如你 UPDATE 语句,但是你那个表,没有索引。
那它没法用 next-key 锁,就会退化。
它会全表扫描,把所有行都加上记录锁,还对行跟行之间的间隙都加间隙锁。
这么一来,就相当于锁了整个表。
你想想,这并发性能,会差到什么程度。

还有,锁退化。
在某些场景下,next-key 锁也可能退化成只记录锁,或者只间隙锁。
比如你在唯一索引上做等值查询。

关键总结一下:快照读,靠 MVCC,没幻读,适合普通查。
当前读,靠 next-key 锁,解决幻读,适合改数据。
索引很重要,next-key 锁得靠索引。
你要是没索引,那查询就可能锁表,这线上肯定不行。
得注意。