一、什么是MVCC
MVCC是通过数据行的多个版本管理来实现数据库的并发控制。MVCC也就相当于是如何实现在相应的隔离级别下,更好的实现并发。
二、快照读与当前写
MVCC在InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突
。
2.1、快照读
快照读,读取的是快照数据。不加锁的简单SELECT
都属于快照读。
SELECT * FROM player WHERE ...
快照读是基于提高并发性能的考虑,实现是基于MVCC,在很多情况下避免了加锁操作,降低了开销。
2.2、当前读
当前读读取的是记录的最新版本。加锁的场景下对数据进行增删查改都会进行当前读。
三、MVCC实现原理之ReadView
ReadView的实现依赖于:隐藏字段、Undo Log、Read View
3.1、什么是ReadView
ReadView就是事务在使用MVCC机制进行快照读操作时产生的读视图。InnoDB为每个事务构造一个数组用来记录并维护系统当前活跃事务的ID。(活跃指的是启动了但没有提交)
3.2、ReadView结构
ReadView中包含四个重要的内容:
creator_trx_id
:创建这个Read View的事务IDtrx_ids
:生成ReadView时当前系统中活跃的读写事务的事务id列表up_limit_id
:活跃的事务中最小的事务IDlow_limit_id
:系统中最大的事务ID(区别于最大的活跃事务ID)
例子:
trx_ids
为trx2、trx3、trx5、trx8的集合,此时low_limit_id
为trx8+1,up_limit_id
为trx2
3.3、设计思路
在四个隔离级别中,读未提交和串行化这两个隔离级别不需要MVCC。原因在于:
- 读未提交,可以读取未提交事务修改过的数据。所以可以直接读取最新的数据,不需要去读历史版本的数据。
- 串行化,InnoDB规定使用加锁的方式来访问记录,这直接解决了幻读的问题,所以不需要使用MVCC。
而使用读已提交和不可重复读的隔离级别下,都必须保证读到已经提交了的事务修改过的数据,所以不能直接读取最新版本的记录。所以在ReadView中的主要任务就是判断版本链中哪个版本是当前事务可见的。
3.4、MVCC操作流程
在查询一条记录的时候,流程如下:
- 1、首先获取事务自己的版本号。
- 2、获取ReadView
- 3、进行查询,然后与ReadView中的事务版本号进行对比。
- 4、如果不符合ReadView规则,则从Undo Log中获取历史快照。
- 5、最后返回符合规则的数据
如果版本链的最后一个版本也不符合规则,则查询结果不包含该数据。
3.5、ReadView规则
- 如果
被访问版本的trx_idx = ReadView的creator_trx_id
:当前事务在访问它自己修改过的数据,所以该版本可以被当前事务访问。 - 如果
被访问版本的trx_idx < ReadView的up_limit_id
:生成该版本的事务已经在当前事务之前提交了,所以该版本可以被当前事务访问。 - 如果
被访问版本的trx_idx >= ReadView的low_limit_id
:生成该版本的事务已经在当前事务生成Readview后才开启,所以该版本不可以被当前事务访问。 - 如果
ReadView的up_limit_id <= 被访问版本的trx_idx <= ReadView的low_limit_id
:判断trx_id是否在trx_ids链表中:- 如果在,表示生成该版本的事务仍然活跃,该版本不可访问
- 如果不在,表示生成该版本的事务已经提交,该版本可以访问。