文章目录
版本链ReadView(一致性视图)生成 ReadView 的时机MVCC 小结版本链
对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含下面两个必要的隐藏列:
trx_id
:一个事务每次对某条聚簇索引记录进行改动时,都会把该事务的事务 id 赋值给trx_id
隐藏列;roll_pointer
:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo
日志中。这个隐藏列就相当于一个指针,可以通过它找到该记录修改前的信息。
比如,表account
中现在包含一条记录:
mysql> SELECT * FROM account WHERE id = 1;+----+--------+---------+| id | name | balance |+----+--------+---------+| 1 | kelvin |15 |+----+--------+---------+1 row in set (0.01 sec)
假设插入该记录的事务 id 为80,那么此刻该条记录的示意图如下图所示:
插入该记录的事物id为80
假设之后有两个事务 id 分别为100、200的事务对这条记录进行 UPDATE 操作:
UPDATE account SET name='vicky' WHERE id = 1; // 事务id为100UPDATE account SET name='peppa' WHERE id = 1; // 事务id为200
每对记录进行一次改动,都会记录一条undo
日志。每条undo
日志也都有一个roll_pointer
属性,通过这个属性可以将这些undo
日志串成一个链表,如下图所示:
页面中的记录和该记录对应的undo日志串成了一个版本链
在每次更新该记录后,都会将旧值放到一条undo
日志中(就算是该记录的一个旧版本)。
随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,这个链表称为版本链。
版本链的头节点就是当前记录的最新值。
我们会利用这个记录的版本链来控制并发事务访问相同记录时的行为,我们把这种机制称之为多版本并发控制(Multi-Version Concurrency Control, MVCC)。
ReadView(一致性视图)
对于使用READ COMMITTED
和REPEATABLE READ
隔离级别的事务来说,都必须保证读到已经提交的事务修改过的记录。
也就是说,假如另一个事务已经修改了记录但是尚未提交,则不能直接读取最新版本的记录。
核心问题是:需要判断版本链中的哪个版本是当前事务可见的。数据库设计了 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 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问;如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,则需要判断 trx_id 属性是是否在 m_ids 列表中。如果在,则说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,则说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。
生成 ReadView 的时机
Read COMMITTED
:每次读取数据前都生成一个 ReadViewREPEATABLE READ
:在第一次读取数据时生成一个 ReadViewMVCC 小结
所谓的 MVCC 指的就是在使用READ COMMITTED
、REPEATABLE READ
这两种隔离级别的事务执行普通的 SELECT操作时,访问记录的版本链的过程。这样可以使不同事务的 读-写、写-读操作并发执行,从而提升系统性能。
READ COMMITTED
、REPEATABLE READ
这两个隔离级别有一个很大的不同,就是生成 ReadView 的时机不同:READ COMMITTED
在每一次进行普通 SELECT 操作前都会生成一个 ReadView;而REPEATABLE READ
只在第一次进行普通 SELECT 操作前生成一个 ReadView,之后的查询操作都重复使用这个 ReadView。
只有我们进行普通的 SELECT 查询时,MVCC 才生效。
如果觉得《MVCC 多版本并发控制》对你有帮助,请点赞、收藏,并留下你的观点哦!