MySQL历险-MVCC相关知识

MySQL历险-MVCC相关知识

Scroll Down

2020唯美.jfif

今天想来记录下MVCC的相关知识,平时也看了不少相关文章。一开始看到这个词总是感觉很虚,看完文章好像懂了,让自己真正说出来的时候,总是条理不那么清晰。所以,为了让印象更深刻,决定写一篇小文章记录下知识点。

MVCC是什么

MVCC 是 Multi-Version Concurrency Control 的缩写,多版本并发控制。同一时刻记录可以在系统中存在多个版本,就是MVCC。

你一定很好奇,它到底有什么用,在了解具体作用前,我们先回归下事务的特性。

事务特性

事务有4大特性,被称为ACID。分别为原子性 Actomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability。

隔离性有在 MySQL 中有哪些隔离级别呢?

  1. 读未提交(read uncommitted):一个事务还没有提交,它所做的变更就能被别的事务所看到
  2. 读已提交(read committed):一个事务提交后,它所做的变更才会被其他事务所看到
  3. 可重复读(repeatable read):一个事务在执行过程中所看到的数据,总是跟这个事务在启动时候看到数据是一致的。本事务的未提交变更对其他事务也是不可见。
  4. 串行化:对同一行记录,读会加读锁写会加写锁。当出现读写冲突时候,后面访问的事务必须等前一个事务执行完成。

MVCC 只在读已提交和可重复读这两个隔离级别下工作。 MVCC解决的是在多并发事务的情况下,数据的可见性问题。

MVCC 的实现

InnoDB 给表中的每一行数据,加多了三个字段,分别为

  • DB_TRX_ID:最后一次修改本行的事务id
  • DB_ROLL_PTR:指向rollback segment中的undo log记录的指针
  • DB_ROW_ID:一个随着新行插入而单调递增的行ID。这个只在表没有指定主键,也没有唯一非空的列时,数据库帮我们生成的主键值。

InnoDB 中,每一个事务都有一个唯一的事务id,是严格递增的id,由事务在启动时向系统申请。 事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,同时也会把修改前的记录写入到 undo log。

MVCC 的实现依赖一致性读视图。在可重复读隔离级别下,获取读视图的时机是执行第一个select语句时候。在读已提交的隔离级别下,获取读取视图的时机是每一次执行select语句的时候,都去获取。一致性读视图可以看成是一个数组,数组低水位记录的是未提交并活跃的事务中最小的 DB_TRX_ID,数组高水位记录的是当前系统里面已经创建过的事务 ID 的最大值加 1 。

通过比较数据的事务id与读视图数组,就可以判断这个数据的当前值是不是对这个事务可见。

行数据的事务id小于低水位,那就意味着数据对事务可见。 行数据的事务id如果大于高水位,意味这数据对事务不可见。 如果落在低水位和高水位的区间范围内,那么还要再分两种情况:

  1. 不在数组里面,说明已经提交了的事务,这个数据对当前事务可见。
  2. 在数组里面,说明是读视图获取时的活跃事务,这个数据对当前事务不可见。

注意了这里的数据指当前版本数据,因为上面说了数据有很多个版本。

上面说得很拗口,不好记住。其实简单点说,就是一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:版本未提交,不可见;版本已提交,但是是在视图创建后提交的,不可见;版本已提交,而且是在视图创建前提交的,可见

总结一下,MVCC的实现是依赖事务创建时获取的一致性读视图,通过比较行数据的事务id,与视图的高低水位,来判断当前事务应该看到哪个版本的数据。

简单画了一个小图,可以辅助理解一下 mysql的mvcc

当前读和快照读

MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读), 是通过 "行排他锁+MVCC" 一起实现的。

假如在一个事务中,执行更新语句,那这行数据是需要加排他锁的,假如该事务还没提交,另一个事务需要更新改行的,那只能对不起了,你得稍等,等第一个事务释放了锁,你才可以继续执行。

快照读

平时写的select语句,不包括select...for update,select... lock in share mode

当前读

  1. select ... lock in share mode
  2. select ... for update
  3. insert
  4. update
  5. delete

打个比方,update 是先读后写,读的是当前值。

参考