MySQL 可重复读隔离级别,完全解决幻读了吗?

MySQL的可重复读隔离级别其实也没能完全搞定幻读这事儿。
InnoDB默认的隔离级别就是可重复读,它倒是提出了两种办法来应对幻读,但说实话,并没有完全根除。
咱们来详细聊聊。

首先,啥叫幻读?简单来说,就是在同一个事务里,你用同样的SELECT语句,在不同的时间点跑出来的结果不一样。
比如,第一次查到了几条记录,第二次查的时候,突然多了几条(或者少了几条),这几条“突然出现”或“突然消失”的记录,就是所谓的“幻像”行。

可重复读是怎么解决幻读的呢?主要有两种方式:
1 . 快照读(就是普通的SELECT语句):靠的是MVCC(多版本并发控制)。
当你开始一个事务(执行BEGIN语句)时,系统会给你创建一个ReadView。
之后,你每次查询,都是用这个ReadView去查找事务开始时的数据版本。
所以,你在事务过程中,每次查询到的数据都是一样的,就算中途有其他事务插入了新记录,你也查询不到,这样就避免了幻读。

2 . 当前读(比如SELECT ... FOR UPDATE这样的语句):用的是next-keylock(记录锁+间隙锁)。
当你执行SELECT ... FOR UPDATE时,系统会给你加上next-keylock。
如果有其他事务想在next-keylock锁的范围内插入记录,那这个插入操作就会被阻塞,直到你的事务结束,这样就避免了幻读。

不过,尽管可重复读隔离级别用这两种方法在很大程度上避免了幻读,但在某些特殊情况下,幻读还是可能发生的。
具体来说:
1 . 快照读中的幻读:假设事务A要更新一条由事务B插入的记录。
事务A前后两次查询的记录条目可能会不一样,从而发生幻读。
比如,事务A第一次查询id=5 的记录没找到,然后事务B插了一条id=5 的记录并提交了。
接着,事务A更新id=5 的记录(虽然这时候你还看不到这条记录,但确实可以进行更新),然后再查询就能看到这条记录了,这就构成了幻读。

2 . 当前读中的幻读:如果事务开启后,先执行了快照读,再执行当前读,期间如果有其他事务插入了新记录,那么事务后续使用当前读进行查询时,也会发现两次查询的记录条目不一样,从而发生幻读。

要避免这类特殊场景下发生幻读,可以采取以下策略:尽量在开启事务之后,马上执行SELECT ... FOR UPDATE这类当前读的语句,因为它会对记录加next-keylock,这样就能避免其他事务插入新记录,从而防止幻读。

总的来说,InnoDB的可重复读隔离级别通过MVCC和next-keylock确实在很大程度上避免了幻读的发生,但并没有完全消除它。
所以,在使用可重复读隔离级别时,咱们还是得注意这些潜在的问题,并采取相应的策略来避免幻读现象的发生。

MySQL 可重复读如何“避免”幻读?

嘿,咱们聊聊MySQL的隔离级别,特别是那个能避免幻读的可重复读(RR)级别。
怎么做到的呢?主要靠以下几个大招:
首先是MVCC机制,这货就像每个数据行后面都跟着两个小跟班,管理着数据的多个版本。
这样一来,在RR级别下,事务就能通过读取相同版本号的数据来保证重复读,也就是说,同一个事务里,不管你读多少次同一数据,结果都一样。

再来是快照读,这就像是事务开始时拍了一张照片,之后的读操作都是基于这张照片,减少了加锁的麻烦,同时保证了数据的一致性。

NextKey锁机制也不简单,它在外键关联的地方,用区间锁来阻止其他事务在特定区间插入新数据,这就避免了幻读。

还有当前读模式,这主要是为了更新数据,它会锁定数据,确保数据完整性和一致性,从而避免幻读。

还有那个SELECT FOR UPDATE语句,这就像是说“我要修改这份数据”,所以要加排他锁,防止别人同时改。
虽然这会带来一些性能上的影响,但能保证数据的一致性。

总的来说,虽然RR级别不能完全杜绝所有幻读,但在实际应用中,它通过这些机制有效地减少了幻读的发生,让数据库在高并发下也能保持数据的一致性。