MVCC
2021年12月24日
MVCC
MVCC 即多版本并发控制,是在并发事务场景下用于支持 RC、RR 隔离级别的实现
四种隔离级别:
- 读未提交
- 读已提交(Read Committed)
- 可重复读(Repeatable Read)
- 串行
事务隔离是怎么实现的?
在 mysql 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作(undo log),都可以得到前一个状态的值。
假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。
这里可以看到一条记录存在多个版本,着就是 MVCC 的 MV 的由来。
mysql 中存在两个“视图”的概念:
- 一个是 view。它是用一个查询语句定义的虚拟表
- 另一个是 Innodb 中实现 MVCC 时用到的一致性读视图(consistent read view),用于支持 RC、RR 隔离级别的实现
一致性视图的创建时机有两种:
- 在 begin/start transaction 后的第一条语句开始时创建
- 在 执行 start transaction with consistent snapshot 时创建
这个 一致性视图 是什么东西?它其实就是一个“快照”,如果在可重复读隔离级别下的话,是对整个库的快照。它是怎么实现的呢?(即 MVCC 是怎么实现 RC、RR 隔离级别的呢?)
分四部分讲:
- 事务 ID
- 行记录隐藏列
- undo log
- ReadView
事务 ID
innodb 里面每个事务有一个唯一的事务 ID,它是事务开始时向 innodb 的事务系统申请的,是按申请顺序严格递增的。
行记录的隐藏列
- row_id: 隐藏的行 ID ,用来生成默认的聚集索引。如果创建数据表时没指定聚集索引,这时 innodb 就会用这个隐藏 ID 来创建聚集索引。采用聚集索引的方式可以提升数据的查找效率
- trx_id: 即最后一个对数据插入或者更新的事务 ID,每一次事务对索引对应的记录进行改动时,都会把该事务的 ID 赋值给 trx_id
undo log
这里 U1、U2、U3 都是 undo log,如果最新版本的记录 V4 要回到 V3 只要通过 U3 就可以了,同理回到 V2 只要通过 U3、U2 就可以了。
ReadView
ReadView 中主要包含4个比较重要的内容:
- m_ids:表示在生成 ReadView 时当前系统中活跃(值事务启动了但还没提交)的读写事务的事务 id 列表
- min_trx_id:表示在生成 ReadView 时当前系统中活跃的读写事务中最小的事务 id,也就是 m_ids 中的最小值
- max_trx_id:表示生成 ReadView 时系统中应该分配给下一个事务的 id 值
- creator_trx_id:表示生成该 ReadView 的事务的事务 id
ReadView 是如何工作的?
有了这些信息,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
- 果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问
- 如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问
- 如果被访问版本的 trx_id 属性值大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问
- 如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下 trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问
innodb 利用了 “所有数据都有多个版本” 的特性,实现了 “秒级创建快照” 的能力。