橘子学MYSQL之MVCC(上)

一、前情提要

准备一个数据表,忽视我这个表的字段命名或者长度类型选择等问题。
里面有一个用户数据,致敬梅西,35岁生日快乐,希望你能世界杯夺冠。里奥,你配的上世间所有的赞美。
在这里插入图片描述

DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
  `id` int(32) NOT NULL AUTO_INCREMENT COMMENT '主键,自增',
  `name` varchar(255) NOT NULL COMMENT '名字',
  `age` int(32) NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';

-- ----------------------------
-- Records of person
-- ----------------------------
INSERT INTO `person` VALUES ('1', '梅西', '35');

# 查看当前事务的事务号的语句是:
select TRX_ID from INFORMATION_SCHEMA.INNODB_TRX where TRX_MYSQL_THREAD_ID = CONNECTION_ID();

# 查看事务的隔离级别
show variables like '%tx_isolation%';

# 修改事务的隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ-COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE-READ;

二、前置知识

1、隐藏字段

大家都知道的,在innodb引擎下面,每一个数据行记录都有几个隐藏列。
1、row_id:行标识,你如果不显式的声明主键,或者表里面也没什么唯一索引这种,那就innodb自己给你创建这个row_id作为主键,innodb是一定给你有一个聚簇索引的,你不建,那就innodb给你来。
2、trx_id:事务id,每个事务的表示,事务里面每次对数据行做处理改动,都会把这个事务号赋值给数据行的这个字段。表示是谁改动的。
3、roll_pointer:是一个指针,在版本链里面做指向的。后面看图就好了。

先来明确几个概念:
trx_id这个东西在一个事务中只有当你做更新,删除,插入操作的时候才会生成这个事务id。普通的只读操作就是0,但是呢我开启了一个事务,看一下:
在这里插入图片描述
这里出现了信仰危机,难不成书上是错的?经过查资料,看到这么个说法。姑且先信任它,要论证只能去看源码,本文着重在MVCC原理,不做这个处理。
这个数字是每次查询的时候由系统临时计算出来的。它的算法是:把当前事务的 trx 变量的指针地址转成整数,再加上 248。使用这个算法,就可以保证以下两点:
因为同一个只读事务在执行期间,它的指针地址是不会变的,所以不论是在 innodb_trx 还是在 innodb_locks 表里,同一个只读事务查出来的 trx_id 就会是一样的。
如果有并行的多个只读事务,每个事务的 trx 变量的指针地址肯定不同。这样,不同的并发只读事务,查出来的 trx_id 就是不同的。OK,不深究了,看到这里就够了,我们已经有了前置知识了,可以正式开始mvcc原理的分析了。

2、版本链

版本链是MVCC的核心,实际上mvcc作用的对象就是这个版本链。
版本链是由一系列的undo log组成的,这里默认你知道undo log这个东西了。举个例子。
1、刚开始数据库里面有一个数据,在你插入数据之后,那么此时数据是这样的。
在这里插入图片描述
ok,那个指针就是指向undo数据用的。我们假设这个插入的事务号是95,那么在这个事务中操作完这个数据后,就会给那个字段赋值为95.
2、此时两个事务并发过来,一个事务号是96,一个是97。
时间序列图如下:
在这里插入图片描述
每次你对一个数据做操作,都会记录一个undo log(select不会),每个undo log也会有一个roll_pointer指针往下指,这里注意一下,插入操作不会有这个指针,因为Insert操作没有更早的版本,你让他指向谁。
真真有这个东西第一次是第一个数据上面有的。
所以经过上面两个事务的操作之后,现在的Undo 日志记录变成了这样。
在这里插入图片描述
每次更新数据,都会产生一个新的undo记录,然后指向上一次的记录,经过多次更新之后,产生一个版本链的东西。而且每次更新操作产生的undo记录上的trx_id字段都被赋值为操作事务的事务id。表示是哪个事务更新的。此时的这个链条就是版本链,而mvcc就是多版本控制,其基于的东西就是这个版本链。

3、ReadView(一致性视图)

有了版本链,我们只完成了mvcc的第一步。要完成真正的mvcc控制,还需要一致性视图的介入。

注意:一执行视图,只会在RR RC这两个隔离级别出现。
RU级别你都能读到别的事务没提交的东西,那你每次拿的都是最新的,甚至都是没提交的。所以不需要控制,串行化级别不说了,都尼玛串行化了,你说锤子。
在我们有了版本链这时候就面临一个新问题,就是这个版本链上那么多视图,那我们需要读到哪个呢?这个就需要一致性视图ReadView了,我实在不喜欢敲单词,后面的都以一致性视图代替。

一致性视图包含如下几个概念:
1、m_ids:在生成一致性视图的时候,当前mysql服务中活跃的读写事务的事务号trx_id的集合列表。活跃的就是未提交的。
2、min_trx_id:在生成一致性视图的时候,当且活跃读写事务集合里面最小的事务号,也就是m_ids里面最小的trx_id。
3、max_trx_id:在生成一致性视图的时候,mysql服务要分配给下一个启动的事务的id,因为这个事务id是顺序递增的,所以比如现在活跃的有97,98。那这个max_trx_id就是99。
4、creator_trx_id:生成这个一致性视图的事务的trx_id。就是哪个事务开启就是谁。
接下来就依赖于这个一致性视图的四个概念以及版本链来实现记录的读取可见。

当你开启事务此时有一个事务id我们记作trx_id_myself,要读取的数据的版本链中的事务号记作trx_id。
规则1:如果trx_id_myself等于了trx_id,那么说明你这个undo log版本就是当前事务生成的,所以你是能看到的,自己能看到自己的更新这很基操。

规则2:如果读到的trx_id小于了一致性视图中的min_trx_id,说明你在读到的这个版本是在当前活跃事务之前就开启了,而且他不在活跃里面说明提交了,所以可以读到。

规则3:如果读到的trx_id大于等于了max_trx_id,说明你读到的这个版本,在当前活跃事务后面才开启,比你还后面,所以你读不到。

规则4:如果你读到的trx_id在min_trx_id和max_trx_id之间,这时候需要你判断你这个trx_id是不是在m_ids中,也就是你现在是不是活跃着,可能你已经提交了,如果还在那么说明你没提交,那么读不到,如果不在了,那么说明你提交了,那你就读不到。

规则5:如果你在版本链上读取到一个版本此时不可见那就顺着版本链继续往下走,顺着指针往下走。直到最后一个版本如果还没读到,那这一行数据就不会被读到,不会加入到返回的结果集中。

规则6:一致性视图虽然在RC RR两个级别中存在,但是他们在不同级别下面生成的时机不一样,因为这个不一样导致了RC可能出现不可重复读的问题,而RR就不会有这个问题。

OK,上篇就到这里,上篇着重是理论表述,理论都是晦涩的,下篇我们将开启两个事务,在并发状态下在不同的隔离级别下看一下mvcc对于读取数据的表现具体如何。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值