浅析mvcc原理

前言

RR解决脏读、不可重复读、幻读等问题,使用的是MVCC(Multi-Version Concurrency Control),即多版本的并发控制协议。在了解 MVCC 以前,咱们先来聊聊隐藏列、Undo log 和 Read View。html

隐藏列

InnoDB中每行数据除了咱们建立的字段外还有有隐藏列,其中隐藏列包含了本行数据的事务id、指向undo log的指针等。mysql

Undo log

MVCC 的实现是经过 Undo log 来完成的。当用户读取一行记录时,若该记录已经被其它事务占用,当前事务能够经过 Undo log 读取以前的行版本信息,由于没有事务须要对历史的数据进行修改操做,因此也不须要加锁,以此来实现非锁定读取。sql

Undo log(回滚日志)能够查看我以前整理的文章-----mysql日志文件总结数据库

Read View数组

什么是readview

事务(暂记事务1)在某一时刻给事务系统trx_sys打快照,把当时trx_sys状态(包括活跃读写事务数组)记下来,以后的全部读操做根据其事务id(即trx_id)与快照中的trx_sys的状态做比较,以此判断readview对于事务1的可见性。bash

Read View 中大体包含如下内容:

  1. trx_ids:数据库系统当前活跃事务 ID 集合;
  2. low_limit_id:活跃事务中最大的事务 ID +1;
  3. up_limt_id:活跃事务总最小的事务 ID;
  4. creator_trx_id:建立这个 Read View 的事务 ID。

好比某个事务,建立了 Read View,那么它的 creator_trx_id 就为这个事务的 ID,假如须要访问某一行,假设这一行记录的隐藏事务 ID 为 t_id,那么可能出现的状况以下:session

  1.     若是 t_id < up_limt_id,说明这行记录在这些活跃的事务建立以前就已经提交了,那么这一行记录对该事务是可见的。
  2.     若是 t_id >= low_limt_id,说明这行记录在这些活跃的事务开始以后建立的,那么这一行记录对该事物是不可见的。
  3.     若是 up_limit_id <= t_id < low_limit_id,说明这行记录多是在这些活跃的事务中建立的,若是 t_id 也同时在 trx_ids 中,则说明 t_id 还未提交,那么这一行记录对该事物是不可见的;若是 t_id 不在 trx_ids 中,则说明事务 t_id 已经提交了,那么这一行记录对该事物是可见的。

什么是MVCC

MVCC, 即多版本并发控制。MVCC 的实现,是经过保存数据在某个时间点的快照来实现的,也就是说,无论须要执行多长时间,每一个事务看到的数据都是一致的。根据事务开始的时间不一样,每一个事务对同一张表,同一时刻看到的数据多是不同的。并发

MVCC举例说明

# 首先开启两个临时客户端,将默认隔离级别改为RC
mysql> set session transaction isolation level READ COMMITTED;
Connection id:    7713
Current database: muke
Query OK, 0 rows affected (0.06 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set, 1 warning (0.00 sec)

# 客户端1
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
# 首先查看t21 b为4的数据
mysql> select * from t21 where b = 4;
+----+---+---+
| id | a | b |
+----+---+---+
|  4 | 4 | 4 |
+----+---+---+
1 rows in set (0.00 sec)

mysql> update t21 set b = 44 where b = 4;
Query OK, 1 rows affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select sleep(10);
+-----------+
| sleep(10) |
+-----------+
|         0 |
+-----------+
1 row in set (10.01 sec)
mysql> rollback;
Query OK, 0 rows affected (0.01 sec)


# 客户端2 
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t21 where b = 4;
+----+---+---+
| id | a | b |
+----+---+---+
|  4 | 4 | 4 |
+----+---+---+
1 rows in set (0.00 sec)
# 在客户端执行select sleep(10);在执行查询语句
mysql> select * from t21 where b = 4;
+----+---+---+
| id | a | b |
+----+---+---+
|  4 | 4 | 4 |
+----+---+---+
1 rows in set (0.00 sec)

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

从上面执行现象能够看出:客户端1的事务先于客户端2的事务执行,可是发如今客户端1休眠的时候,事务2很快就执行完毕并返回告终果。 若是RC模式下读操做须要获取S锁,可是由于当前已经有了X锁,两个锁是互斥的,理论上S锁须要等待X锁释放才会获取,可是并无发生这种状况,能够看出读操做并无使用S锁, 而是使用MVCC实现的。源码分析

在实验中,客户端2 查询的结果是 客户端1 修改以前的记录,也就是那个点的 Read View,根据上面将的 Read View 原理,被查询行的隐藏事务 ID 就在当前活跃事务 ID 集合中。所以,这一行记录对该事物(客户端2中的事务)是不可见的,能够知道 客户端2 查询的 b=4 记录实际就是来自 Undo log 中。咱们看到的现象就是同一条记录在系统中存在了多个版本,这就是多版本并发控制(MVCC)。性能

图例

扩展知识

  1. RR隔离级别(除了Gap锁(间隙锁)以外)和RC隔离级别的差异是建立snapshot时机不一样。 RR隔离级别是在事务开始时刻,确切地说是第一个读操做建立read view的;RC隔离级别是在语句开始时刻建立read view的。
  2. 建立/关闭read view须要持有mutex,会下降系统性能,5.7版本对此进行优化,在事务提交时session会cache只读事务的read view。
  3. 下次建立read view,判断若是是只读事务而且系统的读写事务状态没有发生变化,即trx_sys的max_trx_id没有向前推动,并且没有新的读写事务产生,就能够重用上次的read view。
  4. MVCC 最大的好处是读不加锁,读写不冲突,极大地增长了 MySQL 的并发性。经过 MVCC,保证了事务隔离性。
  5. MVCC 为何只在 RC 和 RR 两个隔离级别下工做?

        RU: 每次只取最新的值,不存在多个值的状况。 串行:每次查询会加读锁,当有更新时会阻塞住,只有等到查询完锁释放后,才会作更新操做,由此可知RU和串行的隔离级别下,只会存在一个值,不存在多个值的状况。MVCC是解决并发状况下,同时可能产生多个值,从多个值中查询出一个准确值的技术,因此RU,串行是用不到MVCC技术

 

 

学习连接

MySQL的MVCC在各类隔离级别中发挥的做用

MySQL · 源码分析 · InnoDB的read view,回滚段和purge过程简介

深刻学习MySQL事务:ACID特性的实现原理

mysql事务-----四种隔离级别

mysql日志文件总结

 

人们觉得他们的理性支配言语,恰恰有时反而支配理性。 ---培根

相关文章
相关标签/搜索