天天看点

面试必会 InnoDB的多版本并发控制(MVCC)一、开场白二、MVCC存在的意义三、MVCC如何实现读和写兼容四、遗留问题

一、开场白

MVCC(Multi-Version Concurrency Control)即多版本并发控制,通俗讲就是“通过多个版本的记录来实现并发控制”。并发控制的定义所述者众多,此不赘述。

实现并发控制的方法有:锁,MVCC。这篇文化讲的就是MVCC

二、MVCC存在的意义

刚才说了,实现并发控制方法有锁和MVCC。众所周知,如果存在两个干同样事情的东西,我们都要分析对比其优劣。那么MVCC和锁有何优劣对比呢?再说直白一些,既然锁可以实现并发控制,为什么还需要MVCC呢?

因为纯锁的方式只实现了读-读兼容,而读-写,写-读,写-写都是冲突的,这就会产生较多的锁阻塞,并发效率低。 而我们亲爱的MVCC,它实现了读-写,写-读的兼容,大大减少了锁阻塞,提高了并发效率!

三、MVCC如何实现读和写兼容

1、四个隐藏字段

列名 字节数 含义 作用
DB_TRX_ID 6 记录的版本号 Insert或Update行的最后一个事务的事务ID。(Delete也视为更新,并需要将其标记为已删除)
DB_ROLL_PTR 7 回滚指针 指向最近的历史数据
DB_ROW_ID 6 行隐藏主键 当表没有设置主键时,系统自动为其设置的隐藏主键

还有一个删除标志,当该行被执行Delete时,将此标志设为已删除状态,但是记录依然存在,要等事务提交后才会删除。

2、undo log

undo log即回滚日志,属于InnoDB的事务日志。虽然叫回滚日志,但是其作用却不仅仅限于回滚!

undo log记录了历史数据,这些历史数据的作用是:

(1)实现回滚,以保证事务的原子性

(2)实现MVCC,以实现事务的隔离性

本文只说第二点,即undo log在MVCC中发挥的作用

 3、版本链

每开启一个事务,事务会用当前的系统版本号作为自己的事务ID,并且系统版本号都会增加1。举个例子:

想象这样一个场景,很多人进入一个会场,会场入口有一个发号员,入场者根据自己拿到的号去对号入座。发号员的发号规则是:按照递增顺序发号!即来一个人,发1号,再来一个人,发2号,又来一个人,发3号...以此类推。这样的结果就是,每个人拿到的号都不一样,各自做自己的位置。(理解了这个例子只后,就忘掉它,因为这个例子对后面的理解没用,甚至会误导你)

每次对记录进行update操作时,都会把旧的记录标记为已删除,写入undo日志中,并且把新记录的回滚指针指向这条旧记录,新纪录DB_TRX_ID字段设为当前事务ID。每次修改都这样操作,形成的版本链是这样的。

面试必会 InnoDB的多版本并发控制(MVCC)一、开场白二、MVCC存在的意义三、MVCC如何实现读和写兼容四、遗留问题

图片来自 这篇文章

4、MVCC中的Insert和Delete操作

上面说了MVCC中的Update操作的做法,那Insert和Delete操作是如何做的呢?

Insert操作会产生一条新的记录,其DATA_TRX_ID字段为当前事务的ID,其DATA_ROLL_PTR字段为空。

Delete操作会将当前行标记为已删除,DATA_TRX_ID字段为当前事务的ID。

5、MVCC实现可重复读

MVCC通过快照读实现可重复读,即在事务中第一条Select语句开始时生成一个ReadView,之后该事务中所有的简单Select(没有for update和lock in share model)都会在此ReadView上进行,并且对于返回的记录,必须满足:

(1)如果没有被标记为删除,其DATA_TRX_ID字段值小于或等于当前事务ID的记录。当满足此条件的记录有多个时,选DATA_TRX_ID字段值最大的(即最近的历史值)

(2)如果已经标记为删除,其DATA_TRX_ID字段值大于当前事务ID的记录。

6、读-写兼容

有了上述这些控制,在进行数据库读写时,读操作不用阻塞写操作,写操作不用阻塞读操作,而且不会产生并发问题。这里的写操作包括:Insert,Update,Delete

如有错误之处,感谢指出

四、遗留问题

1、MVCC实现方式(悲观锁,乐观锁)

2、undo log的具体写入和保存流程