tidb mvcc 读取数据流程的想法

最近一直在研究tidb mvcc读取数据的流程,之前一直在想1个问题,为什么mvcc读的时候会有读写冲突呢?
在这个帖子里面https://asktug.com/t/topic/813036,我举了1个例子,


其实在我举的这个例子里面,“那么B事务就应该等待A事务提交完成,再去读id=1这一行的最新数据(commit.tso是10:59:50这一刻的状态,而不是直接读commit.tso是10:58:00 这个时刻的状态)”的原因

猜想1:我认为是防止幻读,假设现在的隔离级别是快照隔离级别(可重复读),我B事务中执行了2次select 查询(按照快照隔离级别的原则, 应该只获取同1个start.tso),假设MVCC不存在读写冲突,那么有可能出现下列情况,B事务执行第1次查询,查询到的值是commit.tso=10:58:00这一时刻的数据,B事务执行第二次查询,查询到的值是commit.tso=10:59:50 这一时刻的数据(假设10:59:00这个A事务已经完全提交了),那么有可能出现第二次查询的值与第一次查询的值不一样产生了幻读(或者叫不可重复读),违反了快照隔离级别(可重复读)设计原则,我个人认为这是mvcc下,还有读写冲突的1个原因之一吧。
https://pingcap.com/zh/blog/percolator-and-txn

2、猜想2,基于这个猜想,我还有1个猜想就是,如果隔离级别是读已提交呢,读已提交本身就存在幻读的现象,所以这个时候的mvcc的读就可用不存在读写冲突了,那么是不是可以说明在某些情况下,比如A事务产生了1个update大事务,B事务要去读这个A事务当中涉及到的行,如果是快照隔离级别模式下,B事务需要等待查询(等待A事务提交完成),如果是读已提交隔离级别模式下,B事务就不需要等待查询了,那么在这样的情况下,是不是说明读已提交的隔离级别性能比快照隔离级别性能更好?

不知道我上面的2个猜想是否正确,还请各位老师指点?

2 个赞

举个不太恰当的例子:A和B两个货架各50件货物,现在想把A货架上的10件货物搬到B货架上去

  • T1时刻:进行初始校验,确认A货架和B货架一共有100件货物
  • T2时刻:A货架上卸下10件货物,确认被物流机器人扛走,这时UPDATE库,给A的货物数减去10
  • T3时刻:物流机器人到达B货架,并上架这10件货物完成,这时UPDATE库,给B的货物数加上10
  • T4时刻:进行最终校验,确认A货架和B货架一共100件货物,整个过程结束

上述业务逻辑有严格的时间先后要求,否则会造成最终校验不通过。那么T4时刻必须要能够看到T2和T3时刻的变更,才能确保这个系统逻辑正确。说白了,就是一致性

老师,你好
我感觉这个例子突出的是事务的ACID中的D,我们想讨论下,在不同隔离级别下读取数据的差异

和持久性没关系。RR和RC在MVCC上的最大区别在于,RR中的快照是事务创建时(TiDB)或者事务执行第一条SQL时(MySQL)创建的,整个事务中的所有SQL都使用这一个快照,而RC中每条SQL语句都会创建单独的快照。无论是RC还是RR,一旦创建了快照,那么基于这个快照的读,要保证ACID,也是会存在读写冲突的,这个和隔离级别无关。我写的这个例子是想说,T4时刻创建的快照(无论是RR还是RC),它必须要能看见T2和T3的变更(有可能发生T3与T4两个事务读写冲突),否则就违背了线性一致性。当然如果业务对一致性要求没有这么高,比如只需要保证因果一致性或者最终一致性,那么就另当别论
https://docs.pingcap.com/zh/tidb/stable/transaction-overview#因果一致性事务

该主题在最后一个回复创建后60天后自动关闭。不再允许新的回复。