MySQL-MVCC
大黄 Lv4

MVCC

以下针对的是InnoDB引擎

什么是可重复读

在可重复读的事务隔离级别下,当我们开启一个事务时,其他事务后续的改动,当前事务查询到的数据还是事务刚刚启动时候内容。这个实际上就是MySQL的MVCC做的事情。

1
2
3
4
5
6
7
8
9
10
11
-- 事务1
begin; -- 执行顺序1
select * from my_test where id = 1; -- 执行顺序2
select * from my_test where id = 1; -- 执行顺序4
update my_test set b = 'c' where id = 1; -- (更新数据是先读后写,是当前读,不基于快照读) 执行顺序5
COMMIT;

-- 事务2
update my_test set b = 'e' where id = 1; -- 执行顺序3

-- 按照上述的执行顺序,在第四步操作时,查询到的数据还是原来的数据,而不是事务2修改的内容

那么,MySQL的MVCC是怎么做到这一切的呢?

实现MVCC依赖到了MySQL的一致性视图 consistent read view (支持RC 读提交和RR 可重复读)

关于这个一致性视图,它并不是我们之前使用到的查询时候建立的虚拟表(view),没有一个具体的物理结构,作用就是定义了事务执行时能看到什么样的数据。

然后,对于MySQL各个隔离级别使用这个一致性视图的情况如下:

  • 读未提交:直接返回记录上的最新值,不存在视图的概念
  • 读提交:一致性视图是在每个SQL语句开始执行的时候创建的
  • 可重复读:事务启动的时候创建的,整个事务存在期间都是用同一个视图
  • 串行化:直接加锁来避免并行访问
1
2
-- 这里补充一个查询事务隔离级别的命令
show variables like 'transaction_isolation';

可重复读历史数据版本实现原理

首先明确一点:关于这个历史数据,并不是在事务开启时对历史数据进行一个快照版本备份(数据量大,成本太高),而是通过事务id和undolog来实现的

关于事务id

InnoDB里面每一个事务都有一个唯一的事务id(transaction id),它是事务开启的时候向InnoDB的事务系统申请的,根据申请顺序严格递增的。

然后对于每次更新操作,数据库是这样记录的

V1,V2,V3实际上是不存在,而是根据V4和undolog逆推实时计算出来的

MYSQL-MVCC行记录.png

那么,关于可重复读,MySQL是如何知道要逆推到哪个版本就可以结束了呢?

InnoDB为每个事务构建了一个数组,用来保存事务启动瞬间,当前正在活跃(启动但还没有提交)的所有事务id。

关于当前事务可以看到的数据版本,有以下几种可能

  • 如果trx_id落在绿色部分,说明是已经提交的事务或者是当前事务自己生成的,对当前事务是可见的
  • 如果落在红色部分,说明是当前事务以后的事务生成的,不可见
  • 如果落在黄色部分
    • trx_id在数组中,说明这个版本的数据由未提交的事务生成,不可见
    • trx_id不在数组中,说明这个版本的数据是已经提交的事务生成,可见

MySQL-MVCC高低水位.png

  • Post title:MySQL-MVCC
  • Post author:大黄
  • Create time:2023-01-06 16:53:55
  • Post link:https://huangbangjing.cn/2023/01/06/MySQL-MVCC/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.