MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案

文章目录

  • 一、事务隔离级别
    • 1、事务隔离级别概述
    • 2、读一致性解决方案
      • (1)LBCC
      • (2)MVCC
  • 二、深入理解MVCC
    • 1、MVCC的效果
    • 2、MVCC原理分析
      • (1)实例分析
      • (2)可见性视图
    • 3、注意

一、事务隔离级别

1、事务隔离级别概述

MySQL事务隔离级别详解

MySQL InnoDB对事物隔离级别的支持:

事务隔离级别 脏读 不可重复读 幻读
未提交读(Read Uncommitted) 可能 可能 可能
已提交读(Read Committed) 不可能 可能 可能
可重复读(Repeatable Read) 不可能 不可能 对InnoDB不可能
串行化(Serializable) 不可能 不可能 不可能

InnoDB支持的四个隔离级别和SQL92定义的完全一致,隔离级别越高,事务的并发度就越低。唯一的区别就在于,InnoDB在RR的级别就解决了幻读的问题。

也就是说,不需要使用串行化的 隔离级别去解决所有问题,既保证了数据的一致性,又支持较高的并发度。这个也就是InnoDB默认使用RR作为事务隔离级别的原因。

2、读一致性解决方案

如果要解决读一致性的问题,保证一个事务中前后两次读取数据结果一致,实现事务隔离,应该怎么做?总体上来说,有两大类解决方案。

(1)LBCC

第一中,既然要保证前后两次读取数据一致,那么我读取数据的时候,锁定我要操作的数据,不允许其他的事务修改就行了。这种方案我们叫做基于锁的并发控制 Lock Based Concurrency Control(LBCC)。

如果仅仅是基于锁来实现事务隔离,一个事务读取的时候不允许其他时候修改,那就意味着不支持并发的读写操作,而我们大多数应用都是读多写少的,这样会极大地影响操作数据的效率。

(2)MVCC

所以我们还有另一种解决方案,如果要让一个事务前后两次读取的数据保持一致,那么我们可以在修改数据之前给它建立一个备份或者叫做快照,后面再来读取这个快照就行了。这种方案我们叫做多版本的并发控制Multi Version Concurrency Control(MVCC)

这也是今天我们讨论的重点。

二、深入理解MVCC

1、MVCC的效果

MVCC的原则:
1、一个事务能看到的数据版本:(1)第一次查询之前已经提交的事务的修改;(2)本事务的修改。
2、一个事务不能看见的数据版本:(1)在本事务第一次查询之后创建的事务(事务ID比我的事务ID大);(2)活跃的(未提交的)事务的修改。

基于这两个原则,我们可以总结出MVCC的效果:可以查到在我这个事务开始之前已经存在的数据,即使它在后面被修改或者删除了。而在我这个事务之后新增的数据,我是查不到的。

所以我们才把这个叫快照,不管别的事务做任何增删改查的操作,它只能看到第一次查询时看到的数据版本

那么问题来了:这个快照是怎么实现的呢?会不会占用额外的存储空间?

2、MVCC原理分析

InnoDB的事务都是有编号的,而且会不断递增。InnoDB为每行记录都实现了两个隐藏字段
DB_TRX_ID,6字节:事务ID,数据是在哪个事务插入或者修改为新数据的,就记录当前事务ID。
DB_ROLL_PTR,7字节:回滚指针(我们把它理解为删除版本号,数据被删除或记录为旧数据的时候,记录当前事务ID,没有修改或删除的时候为空)
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第1张图片

(1)实例分析

(1)第一个事务,初始化数据(检查初始数据):
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第2张图片
此时的数据,创建版本是当前事务ID(假设事务编号是1),删除版本为空:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第3张图片
(2)第二个事务,执行一次查询,查出两条原始数据,假设第二个事务的ID为2:
在这里插入图片描述
(3)第三个事务,插入一条数据:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第4张图片
此时的数据,多了一条tom,它的创建版本号是当前事务编号,假设为3:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第5张图片
(4)第二个事务执行第二次查询:
在这里插入图片描述
根据MVCC的查找规则:只能查找创建时间小于等于当前事务ID的数据,和删除时间大于当前事务ID的行(或未删除)
也就是不能查到在我的事务开始之后插入的数据,tom的创建ID大于2,所以还是只能查到两条数据。
(5)第四个事务,删除数据,删除了id = 2 huihui的这条记录:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第6张图片
此时的数据,huihui的删除版本被记录为当前事务ID,假设为4,其他数据不变:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第7张图片
(6)第二个事务中,执行第三次查询:
在这里插入图片描述
根据MVCC的查找规则:只能查找创建时间小于等于当前事务ID的数据,和删除时间大于当前事务ID的行(或未删除)
也就是,可以查出在我事务开始之后删除的数据,所以huihui依然可以查出来,所以还是这两行数据。

(7)第五个事务,执行更新操作,假设事务ID是5:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第8张图片
此时的数据,更新数据的时候,旧数据的删除版本被记录为当前事务ID为5(undo),产生了一条新数据,创建ID为当前事务ID 5:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第9张图片
(8)第二个事务执行第四次查询:
在这里插入图片描述
根据MVCC的查找规则:只能查找创建时间小于等于当前事务ID的数据,和删除时间大于当前事务ID的行(或未删除)
因为更新后的数据penyuyan创建版本大于2,代表是在事务之后增加的,查不出来。而旧数据qingshan的删除版本大于2,代表是在事务之后删除的,可以查出来。

通过该实例我们能看到,通过版本号的控制,无论其他事务是插入、修改、删除,第一个事务查询到的数据都没有变化。这就是MVCC的效果。当然,这是一个简化的模型。

(2)可见性视图

假设一条数据修改了3次,两次提交了一次未提交。每次修改之后都有开启一个事务去查询,那么事务2、4、6查到的数据是不一样的:
MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第10张图片
InnoDB中,一条数据的旧版本,是存放在哪里的呢?undo log。因为修改了多次,这些undo log会形成一个链条,叫做undo log链,现在undo log里面有liudehua、wuyanzu、penyuyan。

所以前面我们说的DB_ROLL_PTR,它其实就是指向undo log链的指针。

第二个问题,事务2、4、6最后再查一次,它们去undo log链找数据的时候,拿到的数据是不一样的,在这个undo log链里面,一个事务怎么判断哪个版本的数据是它应该读取的呢?

回想一下MVCC规则:
1、一个事务能看到的数据版本:(1)第一次查询之前已经提交的事务的修改;(2)本事务的修改。
2、一个事务不能看见的数据版本:(1)在本事务第一次查询之后创建的事务(事务ID比我的事务ID大);(2)活跃的(未提交的)事务的修改。

所以,我们必须要有一个数据结构,把本事务ID、活跃事务ID、当前系统最大事务ID存起来,这样才能实现判断。这个数据结构就叫Read View(可见性视图),每个事务都维护一个自己的Read View。

MVCC原理深度解析,InnoDB使用MVCC解决读一致性问题,MySQL读一致性解决方案_第11张图片
m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务ID列表。
min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的ID值。
creator_trx_id:表示生成该ReadView的事务的事务ID。

有了这个数据结构之后,事务判断可见性的规则是这样的:
0、从数据的最早版本开始判断(undo log)。
1、数据版本的trx_id = creator_trx_id,本事务修改,可以访问。
2、数据版本的trx_id < min_trx_id(未提交事务的最小ID),说明这个版本在生成ReadView已经提交,可以访问。
3、数据版本的trx_id > max_trx_id(下一个事务ID),这个版本是生成ReadView之后才开启的事务建立的,不能访问。
4、数据版本的trx_id 在min_trx_id和max_trx_id之间,看看是否在m_ids中,如果在,不可以;如果不在,可以。
5、如果当前版本不可见,就找undo log链中的下一个版本。

3、注意

注意:RR中ReadView是事务第一次查询的时候建立的。RC的ReadView是事务每次查询的时候建立的。

Oracle、Postgres等等其他数据库都有MVCC的实现。

在InnoDB中,MVCC和锁是协同使用的,这两种方案并不是互斥的。

你可能感兴趣的:(mysql,mysql,数据库)