专栏首页DBA随笔MySQL之MVCC初探(1)

MySQL之MVCC初探(1)

//

MySQL之MVCC初探(1)

//

MVCC初探---结合案例

昨天的文章中,我们说了MVCC的基本概念,然后讲了记录额外的两个字段,今天我们通过例子来说明一下MVCC在实际应用中的表现。我们首先创建一张表,然后插入一条数据:

mysql:yeyztest 21:48:49>>show create table swordsman\G
*************************** 1. row ***************************
       Table: swordsman
Create Table: CREATE TABLE `swordsman` (
  `id` int() NOT NULL AUTO_INCREMENT,
  `name` varchar() DEFAULT NULL,
  `kongfu` varchar() DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8
 row in set (0.00 sec)

mysql:yeyztest ::>>select * from swordsman;
+----+-----------+-----------------+
| id | name      | kongfu          |
+----+-----------+-----------------+
|  1 | 张无忌    | 乾坤大挪移      |
+----+-----------+-----------------+
1 row in set (0.00 sec)

这里我们插入一条数据,这条数据有三个字段,分别id,名称和武功,我们看一下这条记录是如何存储的:

可以看到,除了已有的三个字段外,后面还有两个字段,分别是trx_id和roll_pointer,这两个字段就是我们昨天说的隐藏列,其中trx_id保存的是记录的创建版本号,roll_pointer里面是一个指针,它指向了一个insert的undo日志,这个日志中保存的是之前的版本号。这里需要注意,这个undo日志只在事务执行的过程中存在,一旦这个insert事务提交完毕了,这条insert undo日志也就不存在了,系统会自动回收。

在现在这个情况下,我们假设对这条记录做一个改动,先看看改动的先后顺序:

这里我们看到两个事务对这个记录进行了更改,其中第一个事务的事务id是20,第二个事务的事务id是30,此时这个记录就变成了:

mysql:yeyztest 22:12:07>>select * from swordsman;
+----+--------+-----------------+
| id | name   | kongfu          |
+----+--------+-----------------+
|  1 | 杨逍   | 乾坤大挪移      |
+----+--------+-----------------+
1 row in set (0.00 sec)

这很好理解,因为第二个事务的提交时间比第一个晚,所以最终的name的值变成了"杨逍",这个时候,这条记录其实已经有很多版本了。画出来就是:

其实也就是之前的trx_id为10和20的版本也都存在于这个版本链里面了。如果我们对这条记录再次进行更新,这个版本链将会越来越多,这个版本链表的头结点就是当前最新的记录值。这样随着版本越来越多,一个重要的问题就出现了:在并发事务的时候,如何判断到底当前版本链中的哪一个版本对当前事务是可见的。

这个也是多版本并发控制的核心,我们知道事务有四个隔离级别,其中Read uncommitted级别是可以读取到其他未提交事务修改的记录,最高级别的serializable事务来说,innodb中是通过加锁的方式来访问记录的,暂时不考虑这种情况这样一来,我们只需要考虑在RC隔离级别和RR隔离级别下的情况了

这里我们提出一个读视图的概念,也可以称之为ReadView,在这个概念中,包含4个比较重要的内容,分别是:

1、m_ids:表示在生成readview时当前系统中活跃的读写事务的事务id列表 2、min_trx_id:m_ids中的最小值 3、max_trx_id:表示生成readview时系统中应该分配给下一个事务的id值 4、creator_trx_id:表示生成该ReadView的事务的事务id,默认值为0

这里需要补充重要的一点:事务执行过程中,只有在第一次真正修改记录时(比如使用INSERT、DELETE、UPDATE语句),才会被分配一个单独的事务id。

有了这四个概念和readview的概念,我们可以根据下面的方法来判断记录的某个版本是否可见:

第一条:如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。 第二条:如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。 第三条:如果被访问版本的trx_id属性值大于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。 第四条:如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

这样说可能比较拗口,我们看下例子吧,假设现在有一个RC隔离级别的事务开始执行,事务id为20:

然后再开启一个事务id为30的事务,对别的表执行一个update操作,目的是为了生成版本号。

此时我们先把版本链画在这里:

这里需要注意的是蓝色部分的trx_id之所以为20,是因为在trx 20里面更新了两次name的值,而在trx 30中还没有对name的值进行更新。

此时我们在开启第3个事务,事务id为40,并在事务中执行select * from swordsman where id=1;

此时我们思考一下,这个事务读取到的值应该是多少?

分析过程如下:

1、这个select的语句会生成一个ReadView,其中m_ids的列表里面有事务id为20和30的两条记录,也就是m_ids[20,30],min_trx_id=20,max_trx_id=31(30的下一个事务id是31),creator_trx_id=0(默认值)

2、从版本链中,我们套用之前的四条规则,可以发现最新值是阳顶天,版本值是20,在m_ids里面,所以不符合规则当中的第四条。接着往下找

3、下一个版本的name列是杨逍,trx id是20,在m_ids里面,所以不符合第四条要求,继续跳到下一个版本

4、下一个版本的name值是"张无忌",trx id的值是10,符合可见性规则的第2条,10<20,说明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

所以这个select的值就是trx id为10的那条记录,也就是: +----+-----------+-----------------+ | id | name | kongfu | +----+-----------+-----------------+ | 1 | 张无忌 | 乾坤大挪移 | +----+-----------+-----------------+

为了避免混乱,我们今天先写这么多,改天再谢谢RC隔离级别和RR隔离级别的区别,今天的实验是在RC隔离级别下的。

本文分享自微信公众号 - DBA随笔(gh_acc2bbc0d447),作者:AsiaYe

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • insert唯一键冲突的加锁情况分析

    今天分享的内容是MySQL里面insert语句在发生冲突的时候加锁情况,废话就不多说了,直接从例子开始吧。

    AsiaYe
  • MySQL从库server-id相同会发生什么情况?

    今天中午,尝试着将线上rds的一套主从复制架构重新给搭建成一主两从的架构,在搭建的过程中,遇到了一些有意思的问题,记录一下:

    AsiaYe
  • pt-osc工具的一个细节

    在MySQL中,如果我们需要对大表进行变更,往往使用gh-ost或者pt-osc工具,我平日里使用pt-osc比较多,来说说这个工具使用过程中的一个细节吧。关于...

    AsiaYe
  • 【眼见为实】自己动手实践理解 READ COMMITTED && MVCC

    【眼见为实】自己动手实践理解READ UNCOMMITED && SERIALIZABLE

    撸码那些事
  • MySQL事务 Krains 2020-08-09

    若没有事务的支持,会导致数据不一致的问题,比如转账操作将会面临问题:小明给小红各有1000元,小明要给小红转账100元,首先先从小明账户里扣除100元,在给小红...

    Krains
  • MVCC是如何实现的?

    mvcc即多版本并发控制,通过读取指定版本的历史记录,并通过一些手段保证读取的记录值符合事务所处的隔离级别,在不加锁的情况下解决读写冲突

    Java识堂
  • MySQL是如何实现可重复读的?

    可重复读是指:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。

    超超不会飞
  • 一篇文章带你掌握mysql的一致性视图(MVCC)

    提到事务,你肯定会想到ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性),我们就...

    luozhiyun
  • MySQL-长事务详解

    『入门MySQL』系列文章已经完结,今后我的文章还是会以MySQL为主,主要记录下近期工作及学习遇到的场景或者自己的感悟想法,可能后续的文章不是那么连贯,但还是...

    MySQL技术
  • 无限级子商户的查询优化方法

    A 有 2 个直接下级B、C,    B有2个直接下级D、E,    C有2个直接下级F、G

    宣言言言

扫码关注云+社区

领取腾讯云代金券