Skip to main content

18、RR解决了幻读?

Y-aong...About 4 minmysqlrr幻读

18、RR解决了幻读?

Repeatable Read 解决了幻读问题吗

可重复读(repeatable read)定义: 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。

不过理论上会出现幻读,简单的说幻读指的的当用户读取某一范围的数据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据时会发现有新的幻影行。

注意在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此, 幻读在“当前读”下才会出现(查询语句添加 for update,表示当前读);

在 MVCC 并发控制中,读操作可以分为两类: 快照读(Snapshot Read)与当前读 (Current Read)。

  • 快照读
    快照读是指读取数据时不是读取最新版本的数据,而是基于历史版本读取的一个快照信息(mysql 读取 undo log 历史版本) ,快照读可以使普通的 SELECT 读取数据时不用对表数据进行加锁,从而解决了因为对数据库表的加锁而导致的两个如下问题
    1. 解决了因加锁导致的修改数据时无法对数据读取问题.
    2. 解决了因加锁导致读取数据时无法对数据进行修改的问题.
  • 当前读
    当前读是读取的数据库最新的数据,当前读和快照读不同,因为要读取最新的数据而且要保证事务的隔离性,所以当前读是需要对数据进行加锁的(插入/更新/删除操作,属于当前读,需要加锁 , select for update 为当前读)

表结构

idkeyvalue
000
111

假设 select * from where value=1 for update,只在这一行加锁(注意这只是假设),其它行不加锁,那么就会出现如下场景:

image.png
image.png

Session A 的三次查询 Q1-Q3 都是 select * from where value=1 for update,查询的 value=1 的所有 row。

  • T1:Q1 只返回一行(1,1,1);
  • T2:session B 更新 id=0 的 value 为 1,此时表 t 中 value=1 的数据有两行
  • T3:Q2 返回两行(0,0,1),(1,1,1)
  • T4:session C 插入一行(6,6,1),此时表 t 中 value=1 的数据有三行
  • T5:Q3 返回三行(0,0,1),(1,1,1),(6,6,1)
  • T6:session A 事物 commit。

其中 Q3 读到 value=1 这一样的现象,就称之为幻读,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行

先对“幻读”做出如下解释:

  • 要讨论「可重复读」隔离级别的幻读现象,是要建立在「当前读」的情况下,而不是快照读,因为在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。

Next-key Lock 锁

产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,Innodb 引擎为了解决「可重复读」隔离级别使用「当前读」而造成的幻读问题,就引出了 next-key 锁,就是记录锁和间隙锁的组合。

  • RecordLock 锁:锁定单个行记录的锁。(记录锁,RC、RR 隔离级别都支持)
  • GapLock 锁:间隙锁,锁定索引记录间隙(不包括记录本身),确保索引记录的间隙不变。(范围锁,RR 隔离级别支持)
  • Next-key Lock 锁:记录锁和间隙锁组合,同时锁住数据,并且锁住数据前后范围。(记录锁+范围锁,RR 隔离级别支持)
image.png
image.png

总结

  • RR 隔离级别下间隙锁才有效,RC 隔离级别下没有间隙锁;
  • RR 隔离级别下为了解决“幻读”问题:“快照读”依靠 MVCC 控制,“当前读”通过间隙锁解决;
  • 间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间;
  • 间隙锁的引入,可能会导致同样语句锁住更大的范围,影响并发度。
Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8