版本链
对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(row_id并不是必要的,我们创建的表中有主键或者非NULL唯一键时都不会包含row_id列):
1)trx_id:每次对某条聚簇索引记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。
2)roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
比方说我们的表t现在只包含一条记录:
mysql> SELECT * FROM t;
+----+--------+
| id | c      |
+----+--------
+|  1 | 刘备   |
+----+--------+
1 row in set (0.01 sec)
假设插入该记录的事务id为80,那么此刻该条记录的示意图如下所示:
假设之后两个id分别为100、200的事务对这条记录进行UPDATE操作,操作流程如下:

小贴士: 能不能在两个事务中交叉更新同一条记录呢?哈哈,这是不可以滴,第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。本篇文章不是讨论锁的,有关锁的更多细节我们之后再说。

 
每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer属性(INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来,串成一个链表,所以现在的情况就像下图一样:
对该记录每次更新后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id,这个信息很重要,我们稍后就会用到。
ReadView
对于使用READ UNCOMMITTED隔离级别的事务来说,直接读取记录的最新版本就好了,对于使用SERIALIZABLE隔离级别的事务来说,使用加锁的方式来访问记录。对于使用READ COMMITTED和REPEATABLE READ隔离级别的事务来说,就需要用到我们上边所说的版本链了,核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的。所以设计InnoDB的大叔提出了一个ReadView的概念,这个ReadView中主要包含当前系统中还有哪些活跃的读写事务,把它们的事务id放到一个列表中,我们把这个列表命名为为m_ids。这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
1)如果被访问版本的trx_id属性值小于m_ids列表中最小的事务id,表明生成该版本的事务在生成ReadView前已经提交,所以该版本可以被当前事务访问  
2)如果被访问版本的trx_id属性值大于m_ids列表中最大的事务id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。
3)如果被访问版本的trx_id属性值在m_ids列表中最大的事务id和最小事务id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
4)如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本,如果最后一个版本也不可见的话,那么就意味着该条记录对该事务不可见,查询结果就不包含该记录。
在MySQL中,READ COMMITTED和REPEATABLE READ隔离级别的的一个非常大的区别就是它们生成ReadView的时机不同,我们来看一下。
READ COMMITTED --- 每次读取数据前都生成一个ReadView
比方说现在系统里有两个id分别为100、200的事务在执行:

小贴士: 事务执行过程中,只有在第一次真正修改记录时(比如使用INSERT、DELETE、UPDATE语句),才会被分配一个单独的事务id,这个事务id是递增的。

 
此刻,表t中id为1的记录得到的版本链表如下所示:
假设现在有一个使用READ COMMITTED隔离级别的事务开始执行:

 
这个SELECT1的执行过程如下:
1)在执行SELECT语句时会先生成一个ReadView,ReadView的m_ids列表的内容就是[100, 200]。
2)然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列c的内容是'张飞',该版本的trx_id值为100,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
3)下一个版本的列c的内容是'关羽',该版本的trx_id值也为100,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
4)下一个版本的列c的内容是'刘备',该版本的trx_id值为80,小于m_ids列表中最小的事务id100,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'刘备'的记录。

 
之后,我们把事务id为100的事务提交一下,就像这样:

 
然后再到事务id为200的事务中更新一下表t中id为1的记录:

此刻,表t中id为1的记录的版本链就长这样:
然后再到刚才使用READ COMMITTED隔离级别的事务中继续查找这个id为1的记录,如下:
这个SELECT2的执行过程如下:
1)在执行SELECT语句时会先生成一个ReadView,ReadView的m_ids列表的内容就是[200](事务id为100的那个事务已经提交了,所以生成快照时就没有它了)。
2)然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列c的内容是'诸葛亮',该版本的trx_id值为200,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
3)下一个版本的列c的内容是'赵云',该版本的trx_id值为200,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
4)下一个版本的列c的内容是'张飞',该版本的trx_id值为100,比m_ids列表中最小的事务id200还要小,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'张飞'的记录。

 
以此类推,如果之后事务id为200的记录也提交了,再此在使用READ COMMITTED隔离级别的事务中查询表t中id值为1的记录时,得到的结果就是'诸葛亮'了,具体流程我们就不分析了。总结一下就是:使用READ COMMITTED隔离级别的事务在每次查询开始时都会生成一个独立的ReadView。
REPEATABLE READ ---在第一次读取数据时生成一个ReadView
对于使用REPEATABLE READ隔离级别的事务来说,只会在第一次执行查询语句时生成一个ReadView,之后的查询就不会重复生成了。我们还是用例子看一下是什么效果。
比方说现在系统里有两个id分别为100、200的事务在执行:
此刻,表t中id为1的记录得到的版本链表如下所示:
假设现在有一个使用REPEATABLE READ隔离级别的事务开始执行:

 
这个SELECT1的执行过程如下:
1)在执行SELECT语句时会先生成一个ReadView,ReadView的m_ids列表的内容就是[100, 200]。
2)然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列c的内容是'张飞',该版本的trx_id值为100,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
3)下一个版本的列c的内容是'关羽',该版本的trx_id值也为100,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
4)下一个版本的列c的内容是'刘备',该版本的trx_id值为80,小于m_ids列表中最小的事务id100,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'刘备'的记录。
之后,我们把事务id为100的事务提交一下,就像这样:

 
然后再到事务id为200的事务中更新一下表t中id为1的记录:
此刻,表t中id为1的记录的版本链就长这样:
然后再到刚才使用REPEATABLE READ隔离级别的事务中继续查找这个id为1的记录,如下:
这个SELECT2的执行过程如下:
1)因为之前已经生成过ReadView了,所以此时直接复用之前的ReadView,之前的ReadView中的m_ids列表就是[100, 200]。
2)然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列c的内容是'诸葛亮',该版本的trx_id值为200,在m_ids列表内,所以不符合可见性要求,根据roll_pointer跳到下一个版本。
3)下一个版本的列c的内容是'赵云',该版本的trx_id值为200,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
4)下一个版本的列c的内容是'张飞',该版本的trx_id值为100,而m_ids列表中是包含值为100的事务id的,所以该版本也不符合要求,同理下一个列c的内容是'关羽'的版本也不符合要求。继续跳到下一个版本。
5)下一个版本的列c的内容是'刘备',该版本的trx_id值为80,80小于m_ids列表中最小的事务id100,所以这个版本是符合要求的,最后返回给用户的版本就是这条列c为'刘备'的记录。

 
也就是说两次SELECT查询得到的结果是重复的,记录的列c值都是'刘备',这就是可重复读的含义。如果我们之后再把事务id为200的记录提交了,之后再到刚才使用REPEATABLE READ隔离级别的事务中继续查找这个id为1的记录,得到的结果还是'刘备',具体执行过程大家可以自己分析一下。
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就好了。
---------------------
作者:wust_zwl
来源:CSDN
原文:https://blog.csdn.net/qq_38538733/article/details/88902979
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://www.cnblogs.com/xibuhaohao/p/11065532.html

MySQL 事务 MVCC 版本链相关推荐

  1. 京东面试官问我:“聊聊MySql事务,MVCC?”

    大家好,今天来分享一个京东面试真题,也是这是我前阵子听我工位旁边高T(高,实在是高)面试候选人的时候问的一个问题,他问,你能说说 mysql的事务吗?MVCC有了解吗? 话不多说,直接开干 事务定义及 ...

  2. Mysql事务-MVCC

    多版本并发控制(MVCC) 1 MVCC 同一条记录在系统中存在多个版本,这就是数据库的多版本并发控制(MVCC,Multiversion Concurrency Control),MVCC 的实现依 ...

  3. 阿里二面:了解 MySQL 事务底层原理吗

    你好,我是坤哥,今天是国庆最后一天,不知大家是否玩得尽兴,我基本在家带娃了,累得半死,顺带肝了一篇文,来自读者曾经在阿里的面试题,希望对大家有帮助,另外也欢迎大家加我微信「geekoftaste」,一 ...

  4. 链mysql_mysql 版本链机制 readView

    版本链机制 , readView img innodb表下存在两个隐藏列: trx_id : 存放事务id roll_pointer : 存放一个指向上一事务版本的指针 版本链的作用: 用一个单链表的 ...

  5. Mysql事务隔离级别及MVCC(多版本并发控制)

    一.MySQL事务隔离级别 先注明一点:以下讨论都是在多事务并发的情境下讨论的 事务的特性(InnoDB引擎才有事务): ACID 原子性:一个事务不可再分割,要么都执行要么都不执行 一致性:一个事务 ...

  6. MySQL高级理论-MVCC提交查询相关(版本链)

    首先要介绍几个概念: 1.    MVCC(Multi-Version Concurren):多版本并发控制,是MySQL的事务型存储引擎如InnoDB. 2.    trx_id与roll_poin ...

  7. Mysql的MVCC知多少(隐藏字段,undolog版本链和ReadView)

    文章目录 前言 一.什么是MVCC 二.快照读与当前读 三.4种隔离级别与MVCC 四.MVCC实现 4.1 隐藏字段和undolog版本链 4.2 ReadView 五.说明 前言 其实自己之前对M ...

  8. mysql实现可重复读(解决幻读)的原理(MVCC机制的版本链和读视图)

    ​​​​​​​ 版本链 对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(row_id并不是必要的,我们创建的表中有主键或者非NULL唯一键时都不会包含row_id列): ...

  9. MYSQL专题-MVCC多版本并发控制

    MVCC,全称Multi-Version Concurrency Control,即多版本并发控制.MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内 ...

  10. 腾讯面试:MySQL事务与MVCC如何实现的隔离级别?

    有情怀,有干货,微信搜索[三太子敖丙]关注这个不一样的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. ...

最新文章

  1. 第二批重磅嘉宾已就位,邀你共探AI行业新机遇 | MEET2022智能未来大会
  2. 学python需要什么-想要学人工智能需要学些什么python的知识
  3. 安卓客户端与服务器交互Json数据
  4. [Leedcode][JAVA][第1014题][最佳观光组合][数组][暴力优化]
  5. 数据结构 - 多路搜索树(2-3树、b树、b+树、b*树)
  6. 本地储存数据_你的爬虫数据储存在哪?MongoDB入门篇
  7. Java基础学习总结(59)——30 个java编程技巧
  8. 怎么做数据可视化大屏?从设计到上线,一般用这3类工具
  9. java 开源im_开源IM项目-InChat登录接口设计与实现(基于Netty)
  10. Struts2基础教程
  11. FIDO android客户端认证
  12. swap file .swp already exists
  13. win10linux双系统引导设置,win10 + Ubuntu20.04 LTS双系统引导界面美化
  14. 电商网络推广是干什么,电商网络营销做什么
  15. 孙鑫老师VC笔记(转)
  16. Apache Storm 官方文档 —— Trident API 概述
  17. matlab atem(),非特定人的英文
  18. 谷歌pay服务端文档_米大师服务端说明
  19. OPENGL学习(四)GLUT三维图像绘制
  20. 为什么终结符只有综合属性?

热门文章

  1. 案例分享:Windows Phone上的移动浏览体验
  2. 社区智慧+机器智能=奇虎经验的知识机器人
  3. 微信小程序文本溢出的处理方法
  4. android 懒加载用法,Android ViewPager Fragment使用懒加载提升性能详解
  5. 天气预测频繁2项集_寻攻略 | 天气不似预期?我从来没在怕的
  6. 4.Entity Framework Core 5.0 添加数据
  7. golang gin解决跨域:编写一个全局中间件
  8. Kibana 自定义索引模式 Index patterns
  9. PHP数组常用方法(优化版)
  10. Bootstrap图片中加播放按钮