MVCC:多版本并发控制。在MySQL中用以可重复读和读已提交隔离级别的实现,解决了脏读和不可重复度的问题。
MVCC提供了两种读操作:
快照读:在第一次访问数据时,给当前数据拍一张照,之后读取的内容都是快照上的内容,而不是最新更改过的数据。当我们在使用像 select 等不需要加锁的操作时,使用的就是快照读。
即当前读读取的数据不是最新版本而是旧版本的。
当前读:使用select * for update(排他锁),insert,delete,update,select lock in share mode(共享锁)等使用的就是当前读,读取的都是最新数据。
MVCC的实现原理
当前读就是很普通的读操作,所以实现原理重点在于快照读是如何实现的。
MVCC依赖于三个字段(TRX_ID事务ID,ROLL_PTR回滚指针,ROW_ID隐藏主键字段 ), undo log ,Read View 进行实现。
三个隐藏字段:
每一行数据都含有三个隐藏字段:
TRX_ID事务ID:记录最近修改当条数据的事务ID。
ROLL_PTR回滚指针:指向当前记录上个版本的指针。
ROW_ID隐藏主键字段:隐藏的主键字段,当我们没有给数据指定主键时,他就是主键。
这三个字段配合 undo log 实现的MVCC的快照读。
undo log
undo log:回滚日志,事务日志之一。在insert,update,delete时产生的,方便进行事务的回滚。
使用上述三个字段生成了一份数据链表,如图所示:

当进行insert操作的时候,产生的undolog只在事务回滚的时候需要,并且在事务提交之后可以被立刻丢弃;当进行 update和delete操作的时候,产生的undolog不仅仅在事务回滚的时候需要,在快照读的时候也需要,所以不能随便删除,只有在快照读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
Read View
第一次select时为当前数据拍张照,之后的select获取到的数据都是这张照片上的数据。
Read View是事务进行快照读操作的时候生产的读视图,在该事务执行快照读的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。
其实Read View的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最新的数据,也有可能读取的是当前行记录的 undo log 中某个版本的数据。
Read View遵循的可见性算法主要是将要被修改的数据的最新记录中的DB_TRX_ID(当前事务id)取出来,与系统当前其他活跃事务的id去对比,如果DB_TRX_ID跟Read Vew的属性做了比较,不符合可见性。那么就通过 DB_ROLL_PTR回滚指针去取出 undo log 中的 DB_TRX_ID做比较,即遍历链表中的DB_TRX_ID,直到找到满足条件的 DB_TRX_ID。这个DB_TR_ID所在的旧记录就是当前事务能看到的数据。
解决的问题
解决脏读问题:每一次select都生成Read View,保证数据都是被commit的。
解决不可重复读问题:只有在第一次select 时才会生成Read View,虽然之后其他事务修改了数据,但是我们依然看到的是第一次生成的Read View中的数据。
解决了部分幻读问题:因为每一个事务是可以看到被自己修改了的数据的。
MVCC可以无锁的解决事务并发冲突,提高了数据库的并发读写效率。