mvcc机制中的快照读和当前读

什么是MVCC?

MCVV(Multiversion Concurrency Control),多版本并发控制是InnoDB引擎处理读写冲突的手段,目的是用来提高数据库并发场景下的吞吐性能。
不同的事务在并发过程中,SELECT操作可以不加锁,而是通过MVCC机制来指定读取版本,通过一些手段来保证读取的数据符合事务隔离级别,从而解决并发场景下的读写冲突

版本链

又称事务链,每次修改数据的时候,都会记录一条undoLog日志,日志中记录数据每一次的变化,并且通过数据中的两个隐藏列,trx_id(事务id)用标记每一条数据对应的事务id,roll_pointer(指针)用来串联版本链的指针。
mvcc机制中的快照读和当前读_第1张图片

ReadView

阅读视图,可以理解为某一次读取的时候,根据不同的事务生成的数据快照,并且除了表中的数据,还有一下这些固定字段。
m_ids:表示生成ReadView时,当前系统正在活跃的读写事务的事务Id列表。
min_trx_id:表示生成ReadView时,当前系统中活跃的读写事务的最小事务Id。
max_trx_id:表示生成ReadView时,当前时间戳InnoDB将在下一次分配的事务id。
creator_trx_id:当前事务id。

原理

我们都知道,数据库的事务有四个隔离级别:读未提交(Read Uncommitted,简称RU)、读已提交(Read Committed,简称RC)、可重复读(Repeatable Read,简称RR)、串行化(Serializable)。
只有RC和RR才跟MVCC机制有关,因为RU会直接返回记录上的最新值,而Serializable是每一次操作都要加锁。

RC和RR的事务隔离就是通过版本链来控制的,核心逻辑就是判断版本链中的那个版本是当前事务可见可处理的

举个例子,当前有个数据的初始值是 name=“刘德华”

下面是事务时间表,有这样5个事务

时间 tx_100 tx_200 tx_300 tx_400 tx_500
T1 begin;
T2 begin; begin;
T3 update user set name = “古天乐” where id = 1
T4 select * from user where id = 1 begin;
T5 commit; update user set name = “刘青云” where id = 1
T6 select * from user where id = 1 select * from user where id = 1 begin;
T7 commit;
T8 select * from user where id = 1 select * from user where id = 1

当在T4时间点的时候,版本链如下:

id name trx_id roll_pointer
1 刘德华 0 null
1 古天乐 100 roll_pointer(指向上一个版本)
RC隔离级别

当在读已提交(Read Committed)隔离级别的时候,每一次读取都会生成一个新的ReadView。

当处于T4时间点,由于tx_100事务未提交,tx_200事务未操作,事务 tx_300 查询出的数据就是 name=“刘德华”。

id name trx_id roll_pointer
1 刘德华 0 null
1 古天乐 100 roll_pointer(指向上一个版本)

当处于T6时间点,tx_100事务已提交,tx_200事务未提交,那么tx_300事务查询出来的数据就是name=“古天乐”。tx_400事务也是一样。

id name trx_id roll_pointer
1 刘德华 0 null
1 古天乐 100 roll_pointer(指向上一个版本)
1 刘青云 200 roll_pointer(指向上一个版本)

当处于T8时间点,tx_100、tx_200事务都已提交,所以tx_300、tx_500事务查询到的数据都是name=“刘青云”。

id name trx_id roll_pointer
1 刘德华 0 null
1 古天乐 100 roll_pointer(指向上一个版本)
1 刘青云 200 roll_pointer(指向上一个版本)

有没有发现一个问题,在tx_300事务中,虽然是同一个事务下,但是每次查询的数据都不一样,这就是出现了不可重复读的现象。

RR隔离级别

当处于可重复读(Repeatable Read)隔离级别的时候,与RC最大的不同就是同一个读取事务,只有一个ReadView。

事务tx_300查询了3次,第一次查询的时候是在T4时间点,tx_100事务还未提交数据,所以生成了一个快照ReadView1,name=“刘德华”。

T6时间点事务tx_300第二次查询的时候不会生成新的快照,依旧获取的是ReadView1,name=“刘德华”。 而此时查询的事务tx_400,则会生成快照ReadView2,name=“古天乐”。

T8时间点的时候,事务tx_300第三次查询依旧不会生成新的快照,获取的数据还是ReadView1,name=“刘德华”。 此时查询事务tx_500,则会生成新的快照ReadView3,name=“刘青云”。

MVCC机制,就是通过版本链来控制每一个事务读取的版本,从而实现事务的隔离级别。关键是要理解,如何通过控制ReadView的生成,来实现不同的事务隔离级别。

当前读

读取的是最新版本,并且对数据加锁,阻塞其他操作修改记录。
比如select… update,update,delete,insert这类操作都是当前读

快照读

只有select单纯的读取操作,会在读取的时候生成ReadView快照数据。
并且RC隔离级别下,会在每次select操作的时候都生成一个ReadView快照。
而在RR隔离级别下,同一个事务中只会在第一次select操作的时候生成ReadView快照,避免同一个事务多次读取的数据不一样。

你可能感兴趣的:(面试,数据库,java,MVCC,快照读,当前读)