详细说一下MVCC
MVCC 是 Multiversion Concurrency Control 的缩写,翻译过来就是多版本并发控制,是一种用于解决读写冲突的无锁并发控制的方案。它会为每个事务分配一个事务 ID,并且为每一次修改记录一个版本,版本和事务 ID 相关联,每条记录都有属于自己的版本链。读操作只读该事务开启之前数据库的快照。这样读操作不会阻塞写操作,写操作在不阻塞读操作的同时,也避免了脏读和不可重复读的问题,提高了数据库并发读写的能力。
MVCC的实现原理?
它主要依靠的是数据库中记录的三个隐藏字段,Undo Log 以及 Read View 实现的。
三个隐藏字段分别是:
-
db_row_id:隐藏主键,如果数据库中没有主键索引和非空的唯一索引,则利用这个字段创建聚簇索引。
-
db_trx_id:对这条记录做了最新修改的事务 ID。
-
db_roll_ptr:回滚指针,指向的是 Undo Log 中上一个版本的快照地址。
因为对于记录的每次修改都会在 Undo Log 记录一个版本的快照,所以这三个隐藏字段也会跟随着记录到保存在 Undo Log 中。
那么有了 Undo Log 和这三个隐藏字段,我们应该读取哪一个版本的快照呢,这就用到 Read View了。
Read View
Read View 主要是用来决定读取 Undo Log 中哪个版本的快照。
它中有几个重要的属性:
-
trx_ids,系统当前未提交的事务 ID 的列表。
-
low_limit_id,应该分配给下一个事务的id 值。
-
up_limit_id,未提交的事务中最小的事务 ID。
-
creator_trx_id,创建这个 Read View 的事务 ID。
每开启一个事务,我们就会从数据库中获取一个事务 ID,这个 ID 是自增的,事务 ID 大的能看见事务 ID 小的事务的数据变更记录。
因为每条记录都有一个 db_trx_id 字段,我们用它和 Read View 作比较。
-
如果 db_trx_id < up_limit_id,则表明开启该事务之前这条记录已经修改完了,那么这条记录对当前事务是可见的。
-
如果 db_trx_id > low_limit_id,则表明事务开启期间有事务对这条记录进行了修改并提交,那么这条记录对该事务是不可见的。
-
up_limit_id < db_trx_id < low_limit_id,这种情况下就需要遍历 trx_ids 了。
-
如果 db_trx_id 存在于 trx_ids,则表明该事务开启过程中,未提交的事务已经提交了,那么这条记录对该事务是不可见的。
-
如果 db_trx_id 存在于 trx_ids,则表明该事务开启之前,这条记录已经修改了,那么这条记录对该事务是可见的。
-
当读取数据库中的某一条记录时,如果该记录对该事务可见,那么直接返回就可以了,反之则取出Undo Log 中上一个版本的记录和 Read View 进行比较,依次类推,直到能返回为止。
注意:MVCC + 间隙锁在一定程度上能够解决幻读,但是如果在事务开启过程中发生当前读,也是会出现幻读现象的。