MySQL高级篇——MVCC

一、前言

数据库会并发执行一些事务,多个事务之间可能会对相同的数据进行读写。如果什么都不做,那么可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。为了解决并发问题,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级别下只会读到第一次读到的数据,解决了不可重复读。并且感觉上应该也是解决了幻读,应为读不到新插入的数据了(在不可重复度级别下,新增修改删除的列都感知不到)。
如果是当前读,那么在RR隔离级别下,会加间隙锁来防止幻读。所以mysql在大多数情况下都解决了幻读,只有部分情况下还可能出现幻读。
但是如果一开始是快照读,第二次是当前读,还是会存在幻读。所以mvcc解决了快照读的幻读问题,对于当前读还是有可能存在幻读的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值