MySQL进阶第九章

九.MVCC和隔离级别

MVCC,全称是Multi-Version Concurrency Control(多版本并发控制),MVCC在MySQL InnoDB中的实现主要是为了提高数据库的并发性能,用更好的方式去处理【读-写冲突】,做到即使有【读写冲突】时,也能做到不加锁,非阻塞并发读,学习mvcc之前需要知道一些概念。

1.Read View(读视图)

首先要了解MySQL InnoDB下的当前读快照读,学习过undo log就会知道事务修改数据后会形成一个版本链,

  • 当前读:像select lock in share mode(锁)、 select for update、 update、insert、delete(排他锁)这些操作都是【当前读】,他读取的是记录的【最新版本】,读取时还要保证其他【并发事务】不能修改当前记录,会对读取的记录进行加锁。
  • 快照读:像不加锁的select操作就是快照读,即不加锁的【非阻塞读】;快照读的前提是【隔离级别不是串行级别】,串行级别下的快照读会【退化成当前读】,顾名思义,快照读读取的是【快照】,他是通过readView实现的。
 1.实现原理

Read View是事务进行快照读的时候产生的读视图,该事务执行快照读的那一刻就会生成当前数据库系统的一个快照。

注意:【快照】不是说将数据库复制一份,【Read View】的主要作用是做【可见性判断】, 快照的实现逻辑是通过undo log的【版本链】,配合一些【参数】,比如事务id,来确定当前事务可以读取的版本。

2.readView的结构

举一个列子,当前有事务id为12、13、14、16、20的五个事务,他们在同时修改一条数据,此时,事务13发生读取行为,在【事务13】读取之前【事务14】已经提交,当前场景下,将产生一个readview如下:

一个readView就是一个【结构体】,你甚至可以理解成为java里的实例(readview)和属性,包含属性如下:

  • m_ids:生成该readview时,当前系统中【活跃的事务】id列表。对于当前案例,因为14已经提交,就不活跃了,所以该变量的值为[12,13,16,20]。
  • min_trx_id:当前系统【活跃事务】中最小的【事务id】,他也是m_ids的最小值,当前案例的值就是12。
  • max_trx_id:当前系统中计划分配给下一个事务的id,他可能是m_ids的最大值+1,也可能比他大。当前案例值假设为22。
  • creator_trx_id:生成这个readView的事务id,当前案例的值为12。

2.快照读原理解析

在一个事务读取数据时,会根据当前数据形成一个readview,读取时会按照以下逻辑进行读取:

  • 如果被访问数据的事务trx_id和readView中的creator_trx_id值相同,意味着自己在读取自己修改过的数据,那么这个数据就可以被访问。
  • 如果被访问数据的事务trx_id小于readView中的min_trx_id值,说明生成这个版本的事务已经提交,这个版本的数据是干净的可以使用的,那么这个数据也是可以被访问的。
  • 如果被访问数据的事务trx_id大于readView中max_trx_id值,说明这个版本的数据的事务是在生成readView之后开启的,这样的数据不能被访问,会产生脏读。
  • 如果被访问数据的事务trx_id在min_trx_id值和max_trx_id值的范围内,那么就需要去判断该事务的trx_id是否在m_ids中,目的是判断生成该数据的事务是否已经提交,如果那么这个版本的数据是不应该被读取的,应该读取低版本的数据。如果不在,那么说明事务已经提交,该数据可用。

3.解决脏读和不可重复读

对于RU隔离级别的事务来说,由于可以读取到未提交的事务,所有直接读取【最新的记录】(当前读)就可以,对于serializable的事务来说,必须使用加锁的方式来访问。

1.解决脏读

(1).没有undo+mvcc

一个事务读取了一个数据之后,立马给这个数据加写锁,不允许其他事务进行修改,这是加锁解决脏读。

(2).使用undo+mvcc

所有事务对数据的修改,记录成版本链,使用readView进行版本选择,每个事务只能读取满足条件的数据,这个事务就不需要加锁,而且很好的解决了读写操作的并发执行。

2.解决不可重复读

RC和RR两个隔离级别解决不可重复读是通过生成readview时间不同

(1).RC隔离级别:同一个事务中每次读取数据时都生成一个新的ReadView,两次读取时,如果中间有其他事务进行提交,可能会生成两个不同的ReadView,两次读取的数据不一样,这就是不可重复读。

(2).RR隔离级别:同一个事务只在第一次读取数据时生成一个ReadView,以后这个事务都使用这个ReadView,那么同一个事务中多次读取的数据都是一致的,就解决了不可重复读。

3.解决幻读

他是通过间隙锁实现的,一旦锁定某一个范围的数据,就会对这个范围的数据加锁,间隙锁保证我们不能在这个范围内插入新的数据。

你可能感兴趣的:(mysql,数据库)