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. 开发一个最简单的iOS App

    开发一个最简单的iOS App 大家好,我是孜孜不倦学习的Zhangbeihai. 上月底我组织了[组队学习]TensorFlow 入门课程(中文) ,截至目前有300多同学加入.主要就是 Tenso ...

  2. nuxt.js实现页面刷新功能

    key 属性Key:String 或者Function key属性赋值到<router-view>,这对于在动态页面和不同路径中进行转换很有用.不同的key会使页面组件重新渲染. 设置ke ...

  3. DFS深度优先搜索例题分析

    洛谷P1596 Lake Counting S 题面翻译 由于近期的降雨,雨水汇集在农民约翰的田地不同的地方.我们用一个 \(N\times M(1\times N\times 100, 1\leq ...

  4. Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

    普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态.而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手 ...

  5. 结合商业项目深入理解Go知识点

    这篇文章比较硬核,爆肝5千字,把之前整理的知识点都串起来了.建议先收藏,慢慢看. 前言 上一篇文章 #[Go WEB进阶实战]开源的电商前后台API系统 很受大家欢迎,有好多小伙伴私信我问题:&quo ...

  6. Java学习笔记:2022年1月9日(其二)

    Java学习笔记:2022年1月9日(其二) 摘要:这篇笔记主要记录了1月9日学习的第四章的类的基础知识,以及访问器以及访问器于多线程的意义. 目录 Java学习笔记:2022年1月9日(其二) 1. ...

  7. 【3.x合批亲测】使用这个优化方案,iPhone6也能飞起来,直接拉满60帧!

    大家好,我是晓衡! 上周我花了3天的时间,体验测试了一款 Creator 3.x 性能优化工具:98K动态分层合批. 它能将 DrawCall 超过 1000+ 次的 2D 界面,实现运行时节点分层排 ...

  8. 【MySQL】MySQL8安装

    1. MySQL8安装 安装环境 操作系统:CentOS7 MySQL版本:8.0.28 安装方式:二进制Generic 软件路径:/app/database 数据路径:/data/3306 日志路径 ...

  9. ajax补充知识点、多对多外键的三种创建方式、django内置序列化组件、批量操作数据、分页器推导思路与自定义分页器的使用、form组件

    今日内容 ajax补充说明 主要是针对回调函数args接收到的响应数据 1.后端request.is_ajax() 用于判断当前请求是否由ajax发出 2.后端返回的三板斧都会被args接收不在影响整 ...

  10. Unreal学习笔记2-绘制简单三角形

    目录 1. 概述 2. 详论 2.1. 代码实现 2.2. 解析:Component 2.3. 解析:材质 2.4. 解析:包围盒 2.5. 解析:Section 3. 其他 4. 参考 1. 概述 ...