一、什么是MVCC

  MVCC (Multiversion Concurrency Control) 中文全程叫多版本并发控制,是现代数据库(如MySql)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能
  MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读),是通过 "行级锁+MVCC"一起实现的,正常读的时候不加锁,写的时候加锁。而 MCVV 的实现依赖:隐藏字段、Read View、Undo log。
  另外MVCC只在 Read Committed 和 Repeatable Read两个隔离级别下工作,其他两个隔离级别和MVCC不兼容:
  • Read Uncommitted总是读取最新的记录行,不需要MVCC的支持;
  • Serializable 则会对所有读取的记录行都加锁,单靠MVCC无法完成。

二、MVCC实现的核心知识点

1、事务版本号
  每次事务开启前都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序。
  可以通过这样的命令来查看:select TRX_ID from INFORMATION_SCHEMA.INNODB_TRX;
 
2、隐藏字段(Innodb 为每行额外添加了3个字段,具体请参考官方文档):
DB_TRX_ID:大小为6个字节。指插入或更新该行的最后一个事务的事务标识符,也就是事务ID。 此外,删除在内部被视为更新,在该更新中,该行中的特殊位被设置为将其标记为已删除。
DB_ROLL_PTR:大小为7个字节。表示指向该行回滚段的指针。 回滚指针指向写入回滚段的撤消日志记录。 如果行已更新,则撤消日志记录将包含在更新行之前重建行内容所必需的信息。
DB_ROW_ID:大小为6个字节。包含一个行ID,该行ID随着插入新行而单调增加。 如果InnoDB自动生成聚集索引,则该索引包含行ID值。 否则,DB_ROW_ID列不会出现在任何索引中。
3、Undo log
Undo log是InnoDB MVCC事务特性的重要组成部分。Undo log 主要用于记录数据被修改之前的日志,在表信息修改之前先会把数据拷贝到undo log 里,当事务进行回滚时可以通过undo log 里的日志进行数据还原。具体就不详细介绍了,请看考这两篇文档:https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-logs.html http://mysql.taobao.org/monthly/2015/04/01/
4、read view
“InnoDB支持MVCC多版本,其中RC(Read Committed)和RR(Repeatable Read)隔离级别是利用consistent read view(一致读视图)方式支持的。所谓consistent read view就是在某一时刻给事务系统trx_sys打snapshot(快照),把当时trx_sys状态(包括活跃读写事务数组)记下来,之后的所有读操作根据其事务ID(即trx_id)与snapshot中的trx_sys的状态作比较,以此判断read view对于事务的可见性。
RR隔离级别(除了Gap锁之外)和RC隔离级别的差别是创建snapshot时机不同。 RR隔离级别是在事务开始时刻,确切地说是第一个读操作创建read view的;RC隔离级别是在语句开始时刻创建read view的(详见官方文档)。”
 
Read view中保存的trx_sys状态主要包括(以下字段解释来源于源码):
trx_ids: 为活跃事务id列表,即Read View初始化时当前未提交的事务列表。所以当进行RR读的时候,trx_ids中的事务对于本事务是不可见的(除了自身事务,自身事务对于表的修改对于自己当然是可见的)。
low_limit_id: 当前最大的事务id + 1,事务id >= low_limit_id,对于当前Read View都是不可见的。理解起来就是在创建Read View视图的时候,之后创建的事务对于该事务肯定是不可见的。
up_limit_id: 当前已经提交的事务id + 1,事务id < up_limit_id ,对于当前Read View都是可见的。 理解起来就是在创建Read View视图的时候,之前已经提交的事务对于该事务肯定是可见的。
creator_trx_id: 创建当前read view的事务版本号;
一旦一个Read View被创建,这三个参数将不再发生变化,理解这点很重要,其中low_limit_id 和 up_limit_id分别是 trx_Ids数组的上下界(注意:从单词上来区分的话很容易弄反)。
其他事务对当前事务的可见性判断如下:
  

三、案例分析

下面通过案例来分析MVCC怎么实现一致性读取的。前期数据准备:
  • 使用默认隔离级别RR;
  • 创建一个表: create table test(id int AUTO_INCREMENT, score int, primary key(id)) AUTO_INCREMENT = 0;
  • 假设当前事务id已经自增长到100;
步骤
事务1
事务2
事务3
1
begin;
   
2
 
begin;
 
3
insert into test(score) select 101;
此时事务ID为101
   
4
 
insert into test(score) select 102;
此时事务ID为102
 
5
select * from test;
+----+-------+
| id | score |
+----+-------+
| 1 | 101 |
+----+-------+
此时就会创建read view
up_limit_id = 101
low_limit_id = 103
trx_ids为(101,102)
而101自身可见,102在活跃事务列表中不可见
   
6
   
insert into test(score) select 103;
此时事务ID为103
7
   
insert into test(score) select 104;
此时事务ID为104
8
   
nsert into test(score) select 105;
此时事务ID为105
9
   
select * from test;
+----+-------+
| id | score |
+----+-------+
| 3 | 103 |
| 4 | 104 |
| 5 | 105 |
+----+-------+
此时的up_limit_id=101,low_limit_id=106,
trx_ids为(101, 102),
而101和102在trx_ds列表中不可见 
10
 
select * from test;
+----+-------+
| id | score |
+----+-------+
| 2 | 102 |
| 3 | 103 |
| 4 | 104 |
| 5 | 105 |
+----+-------+
此时就会创建read view:
up_limit_id=101,
low_limit_id=106,
trx_ids为(101, 102),
102自身可见,101在活跃事务列表中不可见
而103、104、105不在trx_ids列表中所有可见
 
11
select * from test;
+----+-------+
| id | score |
+----+-------+
| 1 | 101 |
| 3 | 103 |
| 4 | 104 |
| 5 | 105 |
+----+-------+
由于事务内read view不变
(与RC的区别就在这),
此时的up_limit_id=101,low_limit_id=103,
trx_ids为(101, 102),
101自身可见,102在活跃事务列表中不可见
而>=103的都不可见
   
 
 

四、总结

  1、MVCC主要靠Read view来实现一致性读,也就是快照读;底层是主要基于其中两个隐藏字段来实现(DB_TRX_ID、DB_ROLL_PTR)。这样可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。
  2、Read view其中几个重要组成属性(trx_ids、low_limit_id、up_limit_id、creator_trx_id),一旦一个Read View被创建,这三个参数将不再发生变化;
  3、MVCC只在 RC 和 RR两个隔离级别下工作, 它们的不同之处在于:
    RR:read view是在first touch read时创建的,也就是执行事务中的第一条SELECT语句的瞬间,后续所有的SELECT都是复用这个read view,所以能保证每次读取的一致性(可重复读的语义)
    RC:每次读取,都会创建一个新的read view。这样就能读取到其他事务已经COMMIT的内容。
  所以对于InnoDB来说,RR虽然比RC隔离级别高,但是开销反而相对少。
  补充:RU的实现就简单多了,不使用read view,也不需要管什么DB_TRX_ID和DB_ROLL_PTR,直接读取最新的record即可。

五、参考文献

对Innodb中MVCC的理解的更多相关文章

  1. 【mysql】关于innodb中MVCC的一些理解

    一.MVCC简介 MVCC (Multiversion Concurrency Control),即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代 ...

  2. 关于innodb中MVCC的一些理解

    一.MVCC简介 MVCC (Multiversion Concurrency Control),即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代 ...

  3. java面试一日一题:讲对mysql的MVCC的理解

    问题:请讲下对mysql中MVCC的理解 分析:这个问题要回答的是对MVCC的理解,以及MVCC解决了什么问题这几个方面入手. 回答要点: 主要从以下几点去考虑, 1.什么是MVCC? 2.MVCC用 ...

  4. MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解

    文章出处:https://www.souyunku.com/2018/07/30/mysql/?utm_source=tuicool&utm_medium=referral MySQL/Inn ...

  5. Innodb中的事务隔离级别和锁的关系

    前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力. ...

  6. Innodb中的事务隔离级别和锁的关系(转)

    原文:http://tech.meituan.com/innodb-lock.html 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库 ...

  7. MySQL InnoDB中的事务隔离级别和锁的关系

    前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力. ...

  8. 重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系

    重新学习MySQL数据库9:Innodb中的事务隔离级别和锁的关系 Innodb中的事务隔离级别和锁的关系 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁 ...

  9. Innodb 中的事务隔离级别和锁的关系

    转自:https://tech.meituan.com/innodb-lock.html 前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据 ...

随机推荐

  1. 最简单直接地理解Java软件设计原则之接口隔离原则

    理论性知识 定义 接口隔离原则, Interface Segregation Principle,(ISP). 一个类对应一个类的依赖应该建立在最小的接口上: 建立单一接口,不要建立庞大臃肿的接口: ...

  2. valgrind和Kcachegrind性能分析工具详解

    一.valgrind介绍 valgrind是运行在Linux上的一套基于仿真技术的程序调试和分析工具,用于构建动态分析工具的装备性框架.它包括一个工具集,每个工具执行某种类型的调试.分析或类似的任务, ...

  3. vulnhub靶场之AI-WEB1.0渗透记录

    在本机电脑上自行搭建了一个练手的靶场,下面是记录渗透过程 目录 一.确认靶机ip 二.端口&目录扫描 三.查看敏感目录 四.sql注入 五.get shell 六.系统提权 确认靶机ip ka ...

  4. php之魔术方法 __set(),__get(),__isset(),__unset()

    __set()与__get() 当一个类里面,属性被设置为私有属性时,这个属性是不能在外部被访问的.那么当我们又想在外部访问时该怎么办呢,我们可以用方法来实现.举例如下: 1 class Test 2 ...

  5. HashMap 和 Hashtable两者的区别以和解释

    HashMap 和 Hashtable 是 Java 开发程序员必须要掌握的,也是在各种 Java 面试场合中必须会问到的. 但你对这两者的区别了解有多少呢? 现在,栈长我给大家总结一下,或许有你不明 ...

  6. Maven三种打包方式jar war pom

    1.pom工程 用在父级工程或聚合工程中.用来做jar包的版本控制.必须指明这个聚合工程的打包方式为pom 2.war工程 将会打包成war,发布在服务器上的工程.如网站或服务.在SpringBoot ...

  7. HDOJ 1078

    标准的DAG上的DP,其实之前一直不大想得明白为什么dp[i][j]为什么一定是在(i,j)状态上的局部最优解了呢? 其实仔细想想和我一般所做的DP是一个道理,因为运用dfs的方法,因此可以确定的是, ...

  8. 【noi 2.7_7215】简单的整数划分问题(算法效率)

    题意:问正整数n的所有划分个数. 解法:f[i][j]表示划分 i 后的每个数不大于 j 的划分数.分情况讨论:划分中每个数都小于 j,相当于每个数不大于 j- 1, 故划分数为 f[i][j-1]  ...

  9. PowerShell随笔7 -- Try Catch

    PowerShell默认的顺序执行命令,即使中间某一句命令出错,也会继续向下执行. 但是,我们的业务有时并非如此,我们希望出现异常情况后进行捕获异常,进行记录日志等操作. 和其他编程语言一样,我们可以 ...

  10. CF1466-C. Canine poetry

    CF1466-C. Canine poetry 题意: 给出一个字符串,这个字符串里面可能会包含多个回文子字符串.现在你可以任意修改这个字符串中的任意一个字符任意次数,问你最少多少操作数之后这个字符串 ...