InnoDB之MVCC

掘金社区:MVCC与事务隔离级别
微信文章:InnoDB MVCC机制
官网:InnoDB多版本

1.概述

InnoDB是多版本存储引擎

  • InnoDB是一个多版本的存储引擎:它保留有关已更改行的旧版本信息,这些信息保存在表空间的回滚段中;这些信息可以用来回滚事务,也可以用来建立行的早期版本以实现一致性读。

三个隐藏字段
InnoDB将三个字段添加到数据库中的每一行(也就是聚簇索引的叶子),这三个字段是:

  1. DB_TRX_ID:表示最后最后更新该行的事务ID;
  2. DB_ROLL_PTR:回滚指针,指向该行上一个版本的回滚日志地址(undo log);
  3. DB_ROW_ID:可选,如果InnoDB使用隐藏自增主键,则该行存在;
  4. 此外,还有一个特殊的标记位来记录该行是否被删除。

undo log
回滚日志可以分为两类,分别是插入撤销日志(insert undo log)和更新撤销日志(update undo log):

  • 插入撤销日志:插入撤消日志仅在事务回滚时才需要,并且在事务提交后可以立即将其丢弃;
  • 更新撤销日志:用于一致性读取、构建数据行的早期版本、实现MVCC机制;
  • 暂时这么理解,先不必要纠结undo log的类型;在MVCC机制中,对于未提交的事务的更新(INSERT、UPDATE、DELETE)操作,也会被记录在undo log(或者聚集索引树)中,这样一来,本事务就能够从undo log中读取到自己的更新操作,但是其他事务并不会读取到(原因见下面的ReadView算法)。

MVCC机制中的删除

  1. 在MVCC中删除操作被视为一种特殊的更新,这种更新会为该行(在聚簇索引中的数据行)添加删除标志,并构造类似下面的undo log:
    在这里插入图片描述
  2. 如果删除操作会导致某个二级索引树的变化,那么也会在二级索引树上将对应的索引记录添加删除标志;
  3. 当使用purge对update undo log进行清理时,才会物理删除对应的行及其索引记录

MVCC机制的更新

  1. 当我们要更新某一行时,会在聚簇索引上对记录执行原地更新,其旧版本的记录可以通过DB_ROLL_PTR回滚指针找到;
    在这里插入图片描述
  2. 如果该行的更新操作会导致某个二级索引树的变化,那么会在二级索引树上将旧的索引记录添加删除标志,然后再插入新的索引记录;

MVCC机制的查找

  • 如果是通过聚簇索引来查找数据,那么会在版本链上寻找合适的版本;
  • 通过二级索引来查找数据记录时,如果二级索引页被新的(newer)事务更新或者索引记录有删除标志,InnoDB会到聚簇索引中查找数据记录(回表)。在聚簇索引中,InnoDB会根据当前事务的ID,通过DB_ROLL_PTR回滚指针找到正确的版本。

2.InnoDB行格式

在这里插入图片描述
举例而言:

create table t1(c1 int primary key, c2 int, c3 char(10), index i_c3(c3));
insert into t1 values(1,1,’a’);

在这里插入图片描述

3.undo log

  • 每个撤销日志都与某个事务相关联;
  • 利用撤销日志找到数据记录的早期版本,形成一个版本链;
    在这里插入图片描述

4.ReadView

  • 个人认为,快照读就是通过ReadView来实现的;
  • 主要用处是判断版本链中的哪个版本是当前事务可见的;
  • ReadView中主要包含当前系统中有哪些活跃的事务,把它们的id放到一个名为m_ids的列表中,这样就可以通过m_ids来判断版本链中的某个版本是否可见,判断的过程如下:
    在这里插入图片描述
  • 如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本,如果最后一个版本也不可见的话,那么就意味着该条记录对该事务不可见。
  • RR隔离级别下:在第一个普通select时就建立ReadView(快照),之后的普通select读都基于此ReadView;
  • RC隔离级别下:每个普通select都建立自己的ReadView(新快照)。

5.Purge

Purge操作控制undo日志的回收和物理删除具有删除标志的记录。
在这里插入图片描述
1)假设当前所有ReadView都在t1(实际上,t1时刻不可能有readview,因为此时没有事务在运行)之后,那么此时可以回收T1的undo日志,并将涉及到的有删除标记的记录物理删除;
2)同理,假设当前所有ReadView都在t2之后,那么此时可以回收T1、T3的undo日志,并将涉及到的有删除标记的记录物理删除;
3)同理,假设当前所有ReadView都在t3之后,那么此时可以回收T1、T2、T3、T5的undo日志,并将涉及到的有删除标记的记录物理删除。

6.MVCC——实例1

需要注意:DB_TRX_ID的取值,对于seesion1,TRX_ID = 2:
在这里插入图片描述
在聚簇索引中,索引树的结构是(不要在意节点的多少,只是为了说明聚簇索引的叶子结构):
在这里插入图片描述
对于session1的最后一次查询select * from t1,我们会扫描如上所示的聚簇索引树,由于session1中事务的TRX_ID 为 2,
1)此时最新版本(0, 1, 5, 1, ptr, 1, c)的事务id为5,因此不可见;
2)所以要使用roll_ptr构造出上一个版本(0, 1, 3, 1, ptr, 1, b),仍然不可见;
3)继续通过roll_ptr构造出再上一个版本(0, 1, 1, 1, ptr, 1, a),此时该版本可见,因此返回该版本:(1, 1, a)

二级索引树i_c3中包含如下节点:
在这里插入图片描述
此时如果session1再执行一个查询语句select * from t1 force index(i_c3) where c3 >= 'a',那么会强制在二级索引树上进行查找,查找过程如下:
1)对于索引记录(1,a,1),由于当前索引页被新的(newer)事务修改过(也可以说因为它的delete mark为1),因此需要到聚簇索引中查找c1= 1的记录的可见版本,会回溯到(1,1,’a’),此时经过判断,回溯到的版本(1,1,’a’)中的i_c3列值为’a’,与索引记录(1,a,1)的i_c3列值一致,因此可以返回;
2)对于索引记录(1,b,1),由于当前索引页被新的(newer)事务修改过(也可以说因为它的delete mark为1),因此需要到聚簇索引中查找c2 = 1的记录的可见版本,会回溯到(1,1,’a’),此时经过判断,回溯到的版本(1,1,’a’)中的i_c3列值为’a’,与索引记录(1,b,1)的i_c3列值不一致,因此不可以返回;
3)对于索引记录(1,c,1),由于当前索引页被新的(newer)事务修改过,因此需要到聚簇索引中查找c1 = 1的记录的可见版本,会回溯到(1,1,’a’),此时经过判断,回溯到的版本(1,1,’a’)中的i_c3列值为’a’,与索引记录(1,c,1)的i_c3列值不一致,因此不可以返回。

一些疑问和理解

1.在MVCC机制中,对未提交的事务的更新(update、insert、delete)操作可能会记录在聚簇索引的叶子中,而当前读会返回最新的版本,这个最新的版本是否会包含未提交的事务的数据呢?

  • 无论在RR还是RC级别下,如果某个事务T1的更新操作未提交,那么T1将会持有对应行的锁直到事务结束或回滚;这意味着,在T1结束之前,如果其它事务T2的也希望更新那些行,那么T2将会被阻塞。
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读