全网最详细MVCC讲解,一篇看懂( 五 )


T6
小红
T8
小白
下面我们来看看,RR 级别下的表现是如何的 。
RR 下的 Read View(RR 的版本链和 RC 的版本链是一致的,区别在于 Read View)
T4时刻
T4 时刻的情况 , 和 R C的情况是一致的:
字段

m_ids
[100,200]
m_creator_trx_id
300
m_low_limit_id
400
m_up_limit_id
100
根据可见性原则,最终T4时刻事务C 查询到数据为  name = "小明" ,和 RC 的T4时刻是一致的 。
T6时刻
RR 级别会复用 Read View,所以T6时刻也是:
字段

m_ids
[100,200]
m_creator_trx_id
300
m_low_limit_id
400
m_up_limit_id
100
根据可见性原则 , T6时刻我们发现事务C查询到的数据还是  name = "小明"  。
继续看T8时刻 。
T8时刻
T8时刻继续复用先前的 Read View 。
根据可见性原则,T8时刻事务C查询到的数据依旧是   name = "小明"  。
小结我们将事务C在 RC 和 RR 级别下看到的数据 , 放到一块来对比下:
时刻
RC
RR
T4
小明
小明
T6
小红
小明
T8
小白
小明
可以看出二者由于生成 Read View 的时机不同 , 导致在各个时刻看到的数据会存在差异 。
回过头来看 RC 和 RR 隔离级别的定义,会有种恍然大悟的感觉:

  • 读已提交(Read Committed):事务只能读取到已经提交的数据 。
  • 可重复读(Repeatable Read):事务在整个事务期间保持一致的快照视图,不受其他事务的影响 。
总之在 RC 隔离级别下,每个快照读都会生成并获取最新的 Read View;而在 RR 隔离级别下 , 则是只在第一个快照读创建Read View,之后的快照读获取的都是同一个Read View
RR 级别下能否防止幻读严谨的说,RR 级别下只能防止部分幻读
首先,幻读通常指的是在同一个事务中,第二次查询发现了新增加的行,而第一次查询并没有返回这些新增加的行 。
通过前面的例子,我们也看到了,在 RR 隔离级别下,由于一致性视图的存在,如果其他事务插入了新的行,在同一个事务中进行多次查询,这些新增的行将会被包含在事务的一致性视图中,确实可以避免部分幻读场景 。
这里注意一下:MVCC解决的只是 RR 级别下快照读的幻读问题,而当前读的幻读问题则是通过临键锁来解决的 。也就是说 RR 级别下是通过 MVCC+临键锁 来解决大部分幻读问题的 。
为什么说是部分解决?看下面这个例子:
 
事务A
事务B
T1
begin
 
T2
 
begin
T3
 
select * from user
T4
insert into user(id, name) values(2, "小张')
 
T5
 
select * from user for update
T6
commit
 
T7
 
commit
假设数据初始状态如下:
全网最详细MVCC讲解,一篇看懂

文章插图
图片
T3时刻看到的数据只有一条 name = "小明",而T5时刻,由于 select * from user for update 使用的是当前读,读取的是最新的数据版本,T5时刻查询出来的数据是两条,name 分别为 "小明" 和 "小张" 。
理解了上面的例子之后,再看下面这个例子:
 
事务A
事务B
T1
begin
 
T2
 
begin


推荐阅读