背景
MVCC
中,非常重要的一个实现机制则是依赖于
ReadView
,那么什么是
MVCC的 ReadView
,本篇我们就来一起探讨一下。
MySQL InnoDB MVCC机制
MVCC多版本控制
指的是一种提高并发的技术。
MVCC
实现的,MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。
MVCC
)。
MVCC
的实现,是通过保存数据在某一个时间点的快照来实现的。因此每一个事务无论执行多长时间看到的数据,都是一样的。
MVCC实现细节
MVCC的
核心实现主要基于两部分:多事务并发操作数据与一致性读实现。
多事务并发操作数据
Undo log
进行实现,
Undo log
可以用来做事务的回滚操作,保证事务的原子性。
DATA_TRX_ID:记录最近更新这条记录的事务ID(6字节) DATA_ROLL_PTR:指向该行回滚段的指针,通过指针找到之前版本,通过链表形式组织(7字节) DB_ROW_ID:行标识(隐藏单增ID),没有主键时主动生成(6字节)
Undo Log
链。
1、将待操作的行加排他锁。 2、将该行原本的值拷贝到 Undo Log
中,DB_TRX_ID
和DB_ROLL_PTR
保持不变。(形成历史版本)3、修改该行的值,更新该行的 DATA_TRX_ID
为当前操作事务的事务ID,将DATA_ROLL_PTR
指向第二步拷贝到Undo Log
链中的旧版本记录。(通过DB_ROLL_PTR
可以找到历史记录)4、记录 Redo Log
,包括Undo Log
中的修改。INSERT操作:产生新的记录,其 DATA_TRX_ID
为当前插入记录的事务ID。DELETE操作:软删除,将 DATA_TRX_ID
记录下删除该记录的事务ID,真正删除操作在事务提交时完成。
一致性读实现
READ UNCOMMITED
隔离级别:直接读取版本的最新记录。SERIALIZABLE
隔离级别:通过加锁互斥访问数据实现。READ COMMITED
和REPEATABLE READ
隔离级别:使用版本链实现。(ReadView,可读性视图)
RC
与
RR
隔离级别,实现一致性读都是通过
ReadView
,也就是今天的重点,什么是
ReadView
?
MVCC ReadView
ReadView
是事务开启时,当前所有活跃事务(还未提交的事务)的一个集合,
ReadView
数据结构决定了不同事务隔离级别下,数据的可见性。
Read view lists the trx ids of those transactions for which a consistent read should not see the modifications to the database.
ReadView
的数据结构如下示例:
up_limit_id:
The read should see all trx ids which are strictly smaller (<) than this value.
In other words, this is the low water mark".low_limit_id:
The read should not see any transaction with trx id >= this value.
In other words, this is the "high water mark".m_ids:
Set of RW transactions that was active when this snapshot was taken.
up_limit_id:最先开始的事务,该SQL启动时,当前事务链表中最小的事务id编号,也就是当前系统中创建最早但还未提交的事务 low_limit_id:最后开始的事务,该SQL启动时,当前事务链表中最大的事务id编号,也就是最近创建的除自身以外最大事务编号 m_ids:当前活跃事务ID列表,所有事务链表中事务的id集合
low_limit_id
与
up_limit_id
。
首先需要明确两个重点概念(敲黑板!!!):
1、下面所说的 db_trx_id
,是来自于数据行中的db_trx_id
字段,并非开启了一个事务分配的ID,分配的事务ID只有操作了数据行,才会更新数据行中的db_trx_id
字段2、 ReadView
是与SQL绑定的,而并不是事务,所以即使在同一个事务中,每次SQL启动时构造的ReadView
的up_trx_id
和low_trx_id
也都是不一样的
up_limit_id
表示“低水位”,即当时活跃事务列表的最小事务id(最早创建的事务),如果读取出来的数据行上的的
db_trx_id
小于
up_limit_id
,则说明这条记录的最后修改在
ReadView
创建之前,因此这条记录可以被看见。
if (trx_id < view->up_limit_id) {
return(TRUE);
}
low_limit_id
表示“高水位”,即当前活跃事务的最大id(最晚创建的事务),如果读取出来的数据行上的的
db_trx_id
大于
low_limit_id
,则说明这条记录的最后修改在
ReadView
创建之后,因此这条记录肯定不可以被看见。
if (trx_id > view->low_limit_id) {
return(FALSE);
}
db_trx_id
在
low_limit_id
和
up_limit_id
之间,则查找该数据上的
db_trx_id
是否在
ReadView
的
m_ids
列表中:
如果存在,则表示这条记录的最后修改是在 ReadView
创建之时,被另外一个活跃事务所修改,所以这条记录也不可以被看见。如果不存在,则表示这条记录的最后修改在 ReadView
创建之前,所以可以看到。
ReadView
的时机则各不相同,下面我们分别来看看一下
RR
与
RC
的
ReadView
。
REPEATABLE READ下的ReadView生成
SELECT
语句时,会将当前系统所有活跃事务拷贝到一个列表中生成
ReadView
。
SELECT
操作复用其之前生成的
ReadView
。
UPDATE,DELETE,INSERT
对一致性读
snapshot
无影响。
若事务A的第一个 SELECT
在事务B提交之前进行,则即使事务B修改记录后先于事务A进行提交,事务A后续的SELECT
操作也无法读到事务B修改后的数据。若事务A的第一个 SELECT
在事务B修改数据并提交事务之后,则事务A能读到事务B的修改。
RR
隔离级别,在第一次创建
ReadView
后,这个
ReadView
就会一直持续到事务结束,也就是说在事务执行过程中,数据的可见性不会变,所以在事务内部不会出现不一致的情况。
REPEATABLE COMMITED下的ReadView生成
SELECT
执行,都会重新将当前系统中的所有活跃事务拷贝到一个列表中生成
ReadView
。
RC
隔离级别,事务中的每个查询语句都单独构建一个
ReadView
,所以如果两个查询之间有事务提交了,两个查询读出来的结果就不一样。
RC的本质:每一条
SELECT
都可以看到其他已经提交的事务对数据的修改,只要事务提交,其结果都可见,与事务开始的先后顺序无关。
RR的本质:第一条
SELECT
生成
ReadView
前,已经提交的事务的修改可见。
RR
隔离级别的效率是比
RC
隔离级别的高。
结语
MVCC的
核心实现主要基于两部分:多事务并发操作数据与一致性读实现多事务并发操作数据核心基于 Undo Log
进行实现一致性读实现核心基于 ReadView
进行实现
MVCC
的实现,还是极为复杂的,本文仅谈了一点皮毛,如果您对更多的核心细节感兴趣,还是建议阅读官方文档或其他权威资料,感谢您的阅读。
ReadView Class Reference
https://dev.mysql.com/doc/dev/mysql-server/latest/classReadView.html#details
http://mysql.taobao.org/monthly/2017/12/01/
https://zhuanlan.zhihu.com/p/40208895
https://www.cnblogs.com/truestoriesavici01/p/13224749.html
文章转载自老宣说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。