MySQL高级篇——MVCC

MVCC

  • 一、前言
  • 二、简介
    • 1. 什么是MVCC
    • 2. 快照读和当前读
    • 3. 隐藏列
    • 4. 作用的隔离级别
  • 三、MVCC实现原理之ReadView
  • 四、能否解决幻读

一、前言

数据库会并发执行一些事务,多个事务之间可能会对相同的数据进行读写。如果什么都不做,那么可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。为了解决并发问题,mysql提供了隔离级别、锁、mvcc等。

在之前我们已经讲解过锁了,通过加锁是可以实现mysql不同的隔离级别并解决上述并发问题的。

读-读之间不会产生什么问题,写-写之间只有用锁才能解决并发问题。那么我们目前需要讨论的只有读-写写-读之间能否有除了加锁以外更高效的方式(应为数据被加了写锁,那么所有读的请求就都要等待。而如果先被加了读锁,写操作可能要等很久才能被执行)。mysql的innodb通过mvcc来实现并发读写

在读该文章时,您应该了解mysql事务、锁和undo日志


二、简介

1. 什么是MVCC

MVCC(Multiversion Concurrency Control),多版本并发控制。MVCC通过数据行的多个版本管理来实现数据库的并发控制。
MVCC没有正式标准,不同的DBMS中MVCC的实现方式可能是不同的,也不是普遍使用的。在mysql中目前只有innodb支持mvcc,其他存储引擎并不支持。

2. 快照读和当前读

  • 当前读 : 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。如共享锁select for share, 排他锁select for update,update,insert,delete等操作。
  • 快照读 : 不加锁的简单select都属于快照读,即非阻塞读。它是基于mvcc的,所以读取到的不一定是数据的最新版本,可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,在串行级别下的快照会退化成当前读。

3. 隐藏列

对于innodb存储引擎中的聚簇索引中的行格式,包含3个隐藏列(其中有一个是当没有主键时,自动生成的主键列)。

  • trx_id 每次一个事务对该记录进行改动时,会把事务id写到该隐藏列。
  • roll_pointer 每次对某条聚簇索引记录进行改动时,会把旧版本写道undo日志中,然后这个隐藏列相当于一个指针,可以找到它之前版本的信息。

4. 作用的隔离级别

对于READ UNCOMMITTED隔离级别,由于可以读到未提交事务修改过的记录,所以之间读取记录的最新版本就可以。
对于SERIALIZABLE 隔离界别,innodb采用加锁的方式来访问记录。
所以mvcc只要是针对于RCRR隔离级别,这两种隔离级别都必须保证读到的是已提交的事务。


三、MVCC实现原理之ReadView

MVCC的实现依赖于:隐藏字段、undo log 、ReadView
ReadView就是事务在使用mvcc进行快照读操作时产生的读视图。该视图记录了一些信息去保证在后续判断中该读到那个版本的数据。
ReadView中主要包含4个比较重要的内容:

  • creator_trx_id 创建该ReadView的事务id(只有在对表中记录改动时,才会为该事务分配id)
  • trx_ids 在生成ReadView时当前系统中活跃的读写事务id列表
  • up_limit_id 活跃的事务中最小事务id
  • low_limit_id 系统中最大的事务id+1

当生成了ReadView后,在通过mvcc访问某条记录时,按下边步骤来判断记录的那个版本是可见的:

  • 如果被访问版本的trx_id和ReadView的creator_trx_id相同,意味当前事务在访问自己修改过的记录,所以当前版本可以被事务访问。
  • 如果被访问版本的trx_id小于ReadView的up_limit_id,表明生成该版本的事务在当前事务之前就已经提交了,所以该版本可以看到。
  • 如果被访问版本的trx_id大于ReadView的low_limit_id,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被访问。
  • 如果被访问版本的trx_id在up_limit_idlow_limit_id之间,那么就要判断在不在trx_ids中。如果在,就说明该版本的事务还是活跃的不可被访问。如果不在,说明该版本的事务已经被提交,记录可以被访问。

当不能访问当前版本的数据时,就会顺着版本链找到历史版本进行判断。如果最后一个版本也不可见,那么查询结果就查不出该记录。


四、能否解决幻读

在RC隔离级别下,每次读取数据前都会生成一个ReadView。还未提交的事务属于活跃事务,所以在RC隔离级别在无法看到未提交的数据,解决了脏读。
在RR隔离级别下,只会在第一次执行查询语句的时候生成一个ReadView,之后就不会重复生成了。所以RR级别下只会读到第一次读到的数据,解决了不可重复读。并且感觉上应该也是解决了幻读,应为读不到新插入的数据了。
但是如果一开始是快照读,第二次是当前读,还是会存在幻读。所以mvcc解决了快照读的幻读问题,对于当前读还是有可能存在幻读的。


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