Mysql学习笔记——Change Buffer及事务规则

大憨熊 提交于 2019-12-15 12:39:44

Change Buffer 简介

当需要更新一个数据页的时候,如果数据页在内存中,则直接更新。如果这个数据页不在内存中,在不影响数据一致性的情况下,InnoDB会将这些更新操作缓存在change buffer 中,这样就不需要从内存中读取数据了。

那么,什么时候执行这些缓存的更新操作呢?
在执行下次查询操作时,如果需要访问到这个数据页,会将其加载进内存,此时就可以进行更新操作,同时也设有定时更新的任务。

什么情况下会用到change buffer
对于唯一索引来说,所有的更新都需要判断是否违反唯一性约束,因此唯一索引是不能使用change buffer 的,针对普通索引的更新操作,并且涉及到的数据页不在内存中,此时就会用到change buffer。

使用场景
对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时change buffer的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。
反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记 录在change buffer,但之后由于马上要访问这个数据页,会立即触发merge过程。这样随机访问 IO的次数不会减少,反而增加了change buffer的维护代价。所以,对于这种业务模式来 说,change buffer反而起到了副作用。

MVCC 多版本控制

InnoDB里面每个事务有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向 InnoDB的事务系统申请的,是按申请顺序严格递增的。A每次事务更新数据的时候,都会生成一个新的数据版本,并且 把transaction id赋值给这个数据版本的事务ID,记为rowtrx_id。

InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活 跃”的所有事务ID。“活跃”指的就是,启动了但还没提交。
数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为高水 位。这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。而数据版本的可见性规则,就是基于数据的rowtrx_id和这个一致性视图的对比结果得到的。 这个视图数组把所有的rowtrx_id 分成了几种不同的情况。

在这里插入图片描述

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

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是 可见的;
  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
  3. 如果落在黄色部分,那就包括两种情况
    a. 若 rowtrx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    b. 若 rowtrx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这 个事务执行期间,其他事务的更新对它不可见。

在这里插入图片描述
事务B查到的k的值是3,而事务A查到的k的值是1

这里,我们不妨做如下假设:

  1. 事务A开始前,系统里面只有一个活跃事务ID是99;
  2. 事务A、B、C的版本号分别是100、101、102,且当前系统里只有这四个事务;
  3. 三个事务开始前,(1,1)这一行数据的rowtrx_id是90。 这样,事务A的视图数组就是[99,100], 事务B的视图数组是[99,100,101], 事务C的视图数组是 [99,100,101,102]。

为了简化分析,我先把其他干扰语句去掉,只画出跟事务A查询逻辑有关的操作:

在这里插入图片描述
101,102对于A来说都是不可见的,因为他们都是事务A启动之后执行的提交。

事务B的结果不符合一致性读的规则,原因如下:
更新数据都是先读后写的,而这个读,只能读当前的值,称为”当前读“。

除了update语句外,select语句如果加锁,也是 当前读。所以,如果把事务A的查询语句select *fromt where id=1修改一下,加上lock in share mode 或 for update,也都可以读到版本号是101的数据,返回的k的值是3。

在这里插入图片描述
事务C’的不同是,更新后并没有马上提交,在它提交前,事务B的更新语句先发起了。前面说过 了,虽然事务C’还没提交,但是(1,2)这个版本也已经生成了,并且是当前的最新版本。那么,事 务B的更新语句会怎么处理呢?

“两阶段锁协议”就要上场了。事务C’没提交,也就是说(1,2) 这个版本上的写锁还没释放。而事务B是当前读,必须要读最新版本,而且必须加锁,因此就被 锁住了,必须等到事务C’释放这个锁,才能继续它的当前读。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!