我要投搞

标签云

收藏小站

爱尚经典语录、名言、句子、散文、日志、唯美图片

当前位置:小鱼玄机解释报 > 启动事务 >

MySQL 笔记整理(8a) --事务到底是隔离还是不隔离的?

归档日期:07-08       文本归类:启动事务      文章编辑:爱尚语录

  之前有提到过,如果是在可重复读隔离级别,事务T启动的时候会创建一个视图read-view,之后事务T执行期间,即使有其他事务修改了数据,事务T看到的仍然跟在启动时看到的一样,也就是说,一个在可重复读隔离级别下执行的事务,好像与世无争,不受外界影响。

  但是,在之前也行锁相关内容时又提到,一个事务要更新一行,如果刚好有另外一个事务拥有这一行的行锁,它又不能这么超然了,会被锁住,进入等待状态。问题是,既然进入了等待状态,那么等到事务自己获取到行锁要更新数据的时候,它读到的值又是什么呢?

  在上面这个例子中,事务C没有显示的使用begin/commit,表示这个update语句本身就是一个事务,语句完成时会自动提交。事务B在更新了行之后查询;事务A在一个只读事务中查询,并且时间顺序上是在事务B的查询之后。这时候,如果告诉你事务B查询到的K值是3,而事务A查到的k的值是1,你是不是感觉有点晕呢?

  一个是view,它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是create view...,而它的查询方法与表一样。

  另一个是InnoDB在实现MVCC时用到的一致性视图,即consistent read view,用于支持RC(Read Commited 读提交)和RR(Repeatable Read,可重复读)隔离级别的实现。

  在可重复读的隔离级别下,事务在启动的时候就“拍了个快照”,注意,这个快照是基于整个数据库的。这时,你可能觉得这不太现实,毕竟如果一个库有100G,那么我启动一个事务,MySQL就要拷贝100G的数据,这个过程得多慢啊。可是平常执行事务却很快。实际上,并不需要拷贝这100G的数据。我们先来看看这个快照是怎么实现的。

  InnoDB里面每个事务有一个唯一的事务ID,叫做transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。

  而且,每行数据也是有多个版本的,每次事务更新的时候,都会生成一个新的数据版本,并且把transaction id 赋值给这个数据版本的事务ID,记为row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说,数据表中的每一行记录,其实可能有多个版本(row),每个版本有自己的row trx_id。

  图中虚线个版本,当前最新版本是V4,k的值是22,它是被transaction id为25的事务更新的,因此它的row trx_id也是25.你可能会问,前面的文章不是说,语句更新会生成undo log(回滚日志)吗?那么,undo log在哪儿呢?实际上,图2中的三个虚线箭头,就是undo log;而V1,V2,V3并不是物理上真实存在的,而是每次需要的时候根据当前版本和undo log计算出来的。比如,需要V2的时候,就是通过V4依次执行U3,U2算出来。明白了多版本和row trx_id的概念后,我们再来想一下,InnoDB是怎么定义那个“100G”的快照的。按照可重复读的定义,一个事务启动的时候,能够看见所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。

  因此,一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本在我启动之前生成,就认;否则就不认。必须要找到它的上一个版本。当然,如果上一个版本也不可见,那就得继续往前找”。除此之外,如果这个事务自己更新的数据,它自己还是要认的。在实现上,InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃”指的就是,启动了但还没提交。数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为搞水位。这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。而数据版本的可见性规则,就是基于数据的row trx_id和这个一致性视图的对比结果得到的。这个视图数组把所有的 row trx_id分成了几种不同的情况。

  这样,对于当期事务的启动瞬间来说,一个数据版本的row trx_id,有以下几种可能:

  如果落在绿色部分,表示这个版本是已提交的事务或者是当期事务自己生成的,这个数据是可见的。

  如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的。

  如果落在黄色部分,那就包括两种情况。a. 若row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见。b.若 row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

  所以你现在知道了,InnoDB利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。接下来我们回顾一下图1中的三个事务,分析事务A的语句返回的结果,为什么是k=1.

本文链接:http://singtamil.com/qidongshiwu/476.html