一、事务隔离级别
- 引出:**事务的隔离性要求,**理论上在某个事务对某个数据进行访问时,其他事务应该进行排队,当该事务提交之后,其他事务才可以继续访问这个数据。我们既想保持事务的 隔离性 ,又想让服务器在处理访问同一数据的多个事务时性能尽量高些,舍一部分 隔离性 而取性能者也。
- 事务并发执行遇到的问题
(1)脏写:一个事务修改了另一个未提交事务修改过的数据
(2)脏读:一个事务读到了另一个未提交事务修改过的数据
(3)不可重复读:如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值
(4)幻读:一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先
的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。幻读重点强调了读取到了之前读取没有获取到的记录。 - SQL标准中的四种隔离级别
(1)READ UNCOMMITTED :未提交读。
(2)READ COMMITTED :已提交读。
(3)REPEATABLE READ :可重复读。
(4)SERIALIZABLE :可串行化。
各个隔离级别下,允许发生的事务并发下遇到的问题
注意:因为脏写这个问题太严重了,不论是哪种隔离级别,都不允许脏写的情况发生。 - MySQL中支持的四种隔离级别
注意:MySQL在REPEATABLE READ隔离级别下,是可以禁止幻读问题的发生的。与sql标准中规定有出入。MySQL 的默认隔离级别为 REPEATABLE READ(可手动修改)。
二、MVCC原理
- 版本链
前言:对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:
(1)trx_id :每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的 事务id 赋值给 trx_id 隐藏列。
(2)roll_pointer :每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo日志 中,然后这个隐藏
列就相当于一个指针,可以通过它来找到该记录修改前的信息(找到对应的undo日志)。
版本连介绍展示如下:
可以看到,第一张图中的两个事务的sql操作语句,形成了一个版本链(包括最新的记录和undo日志)。每个版本中还包含生成该版本时对应的 事务id。 - ReadView
ReadView 中主要包含4个比较重要的内容:
(1)m_ids :表示在生成 ReadView 时当前系统中活跃的读写事务的 事务id 列表。
(2)min_trx_id::m_ids 中的最小值。
(3)max_trx_id:表示生成 ReadView 时系统中应该分配给下一个事务的 id 值。
(4)creator_trx_id :表示生成该 ReadView 的事务的 事务id 。
ReadView的作用:需要判断一下版本链中的哪个版本是当前事务可见的。 - ReadView的例子介绍
(1)在 MySQL 中, READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成ReadView的时机不同。
首先看在 READ COMMITTED 隔离级别下的。
开启二个事务(id=100,200),在id=100的事务中修改名字的值,二个事务均未提交。然后通过查询语句,查询语句执行时候会生成一个ReadView,它的m_ids列表为[100, 200],min_trx_id 为 100 , max_trx_id 为 201 , creator_trx_id 为 0 。那么这次查询到的结果是版本号小于min_trx_id(100)的,所以查到的是 ‘刘备’。
之后提交一下id=100的事务,再次查询时候,会新生成一个ReadView, 它的m_ids列表为[200],min_trx_id 为 200 ,
max_trx_id 为 201 , creator_trx_id 为 0 。那么此时就需要版本号满足小于min_trx_id(200)的才能查询到,所以查到的是’张飞’。
总结一下就是:使用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的ReadView。
然后再看在 REPEATABLE READ 隔离级别下的。
这个与上一个的区别就是,只会在一个事务中第一次查询建立ReadView,那么对应到上一个修改名字的例子就是,在两个事务均未提交时候,执行查询语句时会生成一个ReadView, 它的m_ids列表为[100, 200],min_trx_id 为 100 , max_trx_id 为 201 , creator_trx_id 为 0 。查询到的是 ‘刘备’。
之后提交一下id=100的事务,再次查询时候,会使用 原来的ReadView,所以查询到的还是 ‘刘备’。这也和这种隔离级别(REPEATABLE READ)很符合(可重复读)。
4. MVCC小结
从上边的描述中我们可以看出来,所谓的 MVCC (Multi-Version Concurrency Control ,多版本并发控制)指的就
是在使用 READ COMMITTD 、 REPEATABLE READ 这两种隔离级别的事务在执行普通的 SEELCT 操作时访问记录的版
本链的过程,这样子可以使不同事务的 读-写 、 写-读 操作并发执行,从而提升系统性能。 READ COMMITTD 、
REPEATABLE READ 这两个隔离级别的一个很大不同就是:生成ReadView的时机不同,READ COMMITTD在每一
次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作
前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。