mvcc介绍

MVCC是数据库提供并发访问控制的一种技术。其核心理念是数据快照,不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。虽然是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。mysql的InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。与基于锁的并发控制 Lock-Based Concurrency Control (LBCC)相比,MVCC最大的好处:读不加锁,读写不冲突。

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的事务ID,一个保存了行的回滚指针。每开始一个新的事务,都会自动递增产生一个新的事务id。

MVCC只在REPEATABLE READ和READ COMMITIED两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容 ,因为READ UNCOMMITIED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE则会对所有读取的行都加锁。

MVCC 在mysql 中的实现依赖的是undo log与read view。

undo log

为了更好的支持并发,InnoDB的多版本一致性读是采用了基于回滚段的的方式。另外,对于更新和删除操作,InnoDB并不是真正的删除原来的记录,而是设置记录的delete mark为1。因此为了解决数据Page和Undo Log膨胀的问题,需要引入purge机制进行回收。Undo log保存了记录修改前的镜像。在InnoDB存储引擎中,undo log分为:insert undo log 与 update undo log。

insert undo log是指在insert操作中产生的undo log。由于insert操作的记录,只是对本事务可见,其他事务不可见,所以undo log可以在事务提交后直接删除,而不需要purge。
update undo log是指在delete和update操作中产生的undo log。该undo log会被后续用于MVCC当中,因此不能提交的时候删除。提交后会放入undo log的链表,等待purge线程进行最后的删除。

如下图所示(第一次修改):
当事务2使用UPDATE语句修改该行数据时,会首先使用排他锁锁定该行,将该行当前的值复制到undo log中,然后再真正地修改当前行的值,最后填写事务ID,使用回滚指针指向undo log中修改前的行。

 ReadView

对于使用 READ UNCOMMITTED 隔离级别的事务来说,直接读取记录的最新版本就好了。对于使用SERIALIZABLE 隔离级别的事务来说,使用加锁的方式来访问记录。对于使用 READ COMMITTED 和REPEATABLE READ 隔离级别的事务来说,就需要用到我们上边所说的 版本链 了。核心问题就是需要判断一下版本链中的哪个版本是当前事务可见的。所以设计 InnoDB 的设计者提出了一个ReadView的概念,这个 ReadView 中主要包含当前系统中还有哪些活跃的读写事务,把它们的事务id放到一个列表中,我们把这个列表命名为m_ids,并确定三个变量的值:

m_up_limit_id:m_ids事务列表中的最小事务id,如果当前列表为空那么就等于m_low_limit_id。事务id的下限。

m_low_limit_id:系统中将要产生的下一个事务id的值。事务id的上限。

m_creator_trx_id:当前事务id,m_ids中不包含当前事务id

这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本(版本链中的版本)是否可见:
1)如果被访问版本的 trx_id 属性值小于 m_up_limit_id ,表明生成该版本的事务在生成 ReadView前已经提交,所以该版本可以被当前事务访问。
2)如果被访问版本的 trx_id 属性值等于 m_creator_trx_id 既当前事务id,可以被访问。
3)如果被访问版本的 trx_id 属性值大于等于 m_low_limit_id ,在生成 ReadView 后才生成,所以该版本不可以被当前事务访问。
4)如果被访问版本的 trx_id 属性值在 m_up_limit_id 和 m_low_limit_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中。
如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;
如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问

在 MySQL 中, READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成ReadView 的时机不同,如下。
1.READ COMMITTED

每次读取数据前都生成一个ReadView

数据库中的记录是id为1,c为刘备,该行记录的版本是80
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
select c from t where id =1时
在执行SELECT语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是 [100]
然后从版本链中挑选可见的记录,最新版本的列 c 的内容是 '张飞' ,该版本的trx_id值为100,
在 m_ids 列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
下一个版本的列 c 的内容是 '关羽' ,该版本的 trx_id 值也为 100 ,也在m_ids 列表内,所以也
不符合要求,继续跳到下一个版本。
下一个版本的列c的内容是'刘备',该版本的trx_id值为80,小于m_ids列表中最小的事务id 100,
所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'刘备'的记录。

# Transaction 200
BEGIN;
UPDATE t SET c = '赵云' WHERE id = 1;
UPDATE t SET c = '诸葛亮' WHERE id = 1;

执行如下操作:
select c from t where id =1时
在执行SELECT语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是 [100,200],查找的结果仍然为刘备
此时可以看出,Transaction 100与200没有提交时,查询的结果仍然是刘备的,这就达到了RC隔离级别下的mvcc控制的效果

Transaction 100 事务提交
select c from t where id =1时
在执行SELECT语句时会重新生成一个 ReadView,ReadView 的 m_ids 列表的内容是 [200],查找的结果为张飞。事务1提交了,才查询到了最新的记录。

2.READ REPEATABLE

在事务开始后第一次读取数据时生成一个ReadVie
数据库中的记录是id为1,c为刘备,该行记录的版本是80
# Transaction 100
BEGIN;
UPDATE t SET c = '关羽' WHERE id = 1;
UPDATE t SET c = '张飞' WHERE id = 1;
select c from t where id =1时
在执行SELECT语句时会先生成一个 ReadView,ReadView 的m_ids列表的内容是 [100]
然后从版本链中挑选可见的记录,最新版本的列c的内容是'张飞',该版本的trx_id值为100,
在m_ids列表内,所以不符合可见性要求,根据 roll_pointer 跳到下一个版本。
下一个版本的列c的内容是'关羽' ,该版本的trx_id值也为100,也在m_ids 列表内,所以也
不符合要求,继续跳到下一个版本。
下一个版本的列c的内容是'刘备',该版本的trx_id值为80,小于m_ids列表中最小的事务id 100,
所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'刘备'的记录。

# Transaction 200
BEGIN;
UPDATE t SET c = '赵云' WHERE id = 1;
UPDATE t SET c = '诸葛亮' WHERE id = 1;

执行如下操作:
select c from t where id =1时
在执行SELECT语句时会第一次产生的ReadView 的 m_ids列表的内容就是[100],查找的结果仍然为刘备

Transaction 100 事务提交,
select c from t where id =1时
之前的ReadView的m_ids 列表的内容仍然是[100],查找的结果仍然为刘备,
这就可以看出,Transaction 100第一次查询时,在READ REPEATABLE隔离级别下,产生的ReadView的m_ids列表的内容是[100],尽管
之后Transaction 100 事务提交了,但是ReadView的m_ids列表没变,所以查询的结果仍然为刘备,达到了可重复读的效果。



 

每次读取数据前都生成一个ReadView

(三) Mysql 之MVCC的更多相关文章

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

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

  2. mysql的mvcc(多版本并发控制)

    mysql的mvcc(多版本并发控制) 我们知道,mysql的innodb采用的是行锁,而且采用了多版本并发控制来提高读操作的性能. 什么是多版本并发控制呢 ?其实就是在每一行记录的后面增加两个隐藏列 ...

  3. {MySQL数据库初识}一 数据库概述 二 MySQL介绍 三 MySQL的下载安装、简单应用及目录介绍 四 root用户密码设置及忘记密码的解决方案 五 修改字符集编码 六 初识sql语句

    MySQL数据库初识 MySQL数据库 本节目录 一 数据库概述 二 MySQL介绍 三 MySQL的下载安装.简单应用及目录介绍 四 root用户密码设置及忘记密码的解决方案 五 修改字符集编码 六 ...

  4. 【数据库】悲观锁与乐观锁与MySQL的MVCC实现简述

    悲观锁 悲观锁,就是一种悲观心态的锁,每次访问数据时都会锁定数据: 乐观锁 乐观锁,就是一种乐观心态的锁,每次访问数据时并不锁定数据,期待数据并没作修改,如果数据没被修改则作具体的业务 应用程序上使用 ...

  5. MySQL InnoDB MVCC

    MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...

  6. Mysql中MVCC的使用及原理详解

      准备 测试环境:Mysql 5.7.20-log 数据库默认隔离级别:RR(Repeatable Read,可重复读),MVCC主要适用于Mysql的RC,RR隔离级别 创建一张存储引擎为test ...

  7. 【MySQL】面试官:谈谈你对Mysql的MVCC的理解?

    MVCC(Mutil-Version Concurrency Control),就是多版本并发控制.MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问. 在Mysql的In ...

  8. mysql 之mvcc多版本控制

    MVCC是multiversion concurrency control的缩写,提供MySQL事物隔离级别下无锁读,例如一个事物在执行update等修改数据的sql,并未提交时其他事物进行数据读取是 ...

  9. MySQL InnoDB MVCC深度分析

    关于MySQL的InnoDB的MVCC原理,很多朋友都能说个大概: 每行记录都含有两个隐藏列,分别是记录的创建时间与删除时间 每次开启事务都会产生一个全局自增ID 在RR隔离级别下 INSERT -& ...

  10. 《Mysql - 事务 MVCC》

    一:前言 - 前面通过 <Mysql 事务 - 隔离> 的学习,知道了事务的实现,是根据 获取一致性视图 来实现的. 二:那么,什么时候会获取到一致性视图呢? - 例如:有三个事务,启动的 ...

随机推荐

  1. SQLMap入门——获取数据库中的表名

    查询完数据库后,查询指定数据库中所有的表名 python sqlmap.py -u http://localhost/sqli-labs-master/Less-1/?id=1 -D xssplatf ...

  2. PL/SQL Developer使用中文条件查询时无数据的解决方法

    1.在PL/SQL Developer中执行sql命令:select userenv('language') from dual; 显示结果为:AMERICAN_AMERICA.ZHS16GBK: 2 ...

  3. 【转载】EXCEL VBA 同时选择多个工作表的方法

    如果要用VBA同时选择多个工作表,可以利用数组或设置"Select"方法的参数为"False"来扩展所选择的内容,如下面的一些代码:   1.用工作表名称:   ...

  4. [R语言] R语言PCA分析教程 Principal Component Methods in R

    R语言PCA分析教程 Principal Component Methods in R(代码下载) 主成分分析Principal Component Methods(PCA)允许我们总结和可视化包含由 ...

  5. 使用插件式开发称重仪表驱动,RS232串口对接各类地磅秤数据实现ERP管理

    在ERP系统中,采集一线的生产数据是重要工作之一,而称重计量是企业的核心资产数据,人工计重费时费力,还容易出错,重量数据是否正确,直接影响企业的采购或销售额.基于此,由系统对接电子秤实现自动抓取数据是 ...

  6. java代码的基本组成

    我们可以通过上一篇博客写的内容来分析java代码的组成 java代码的组成我们可以大致分成4个部分 一.标识符 除了关键字(有颜色的,可以看到上方图片)以外,自己们写的单词(黑色部分的),如MyJav ...

  7. 09.什么是synchronized的重量级锁?

    大家好,我是王有志.关注王有志,一起聊技术,聊游戏,聊在外漂泊的生活. 今天我们继续学习synchronized的升级过程,目前只剩下最后一步了:轻量级锁->重量级锁. 通过今天的内容,希望能帮 ...

  8. A. Greatest Convex【Codeforces Round #842 (Div. 2)】

    A. Greatest Convex You are given an integer \(k\). Find the largest integer \(x\), where \(1≤x<k\ ...

  9. Python 跨模块使用全局变量(自定义类型)

    gol.py def _init():#初始化 global _global_dict _global_dict = {} def set_value(key,value): "" ...

  10. day16-声明式事务-02

    声明式事务-02 3.事务的传播机制 事务的传播机制说明: 当有多个事务处理并存时,如何控制? 比如用户去购买两次商品(使用不同的方法),每个方法都是一个事务,那么如何控制呢? 也就是说,某个方法本身 ...