InnoDB之MVCC精讲

MVCC精讲

定义: MVCC,也叫多版本并发控制,顾名思义,是通过维护数据的多个版本来实现的,是一个”为了提升并发性能“而提出来的一个概念,它允许读操作和写操作并发执行,而不会相互阻塞。由于没有正式的规范,所有每个存储引擎和数据库系统实现的方式都是有差异的。

下面主要介绍InnoDB版MVCC。

MVCC数据结构

MVCC的实现主要依赖于以下数据结构

  • 版本链(Version Chain)

    当一行数据被修改时,它不会直接覆盖原始数据,而是创建一个新的数据版本,并将新版本与旧版本通过指针链接起来,形成一个版本链。这样,不同的事务就可以读取到不同版本的数据,从而实现了并发控制

  • ReadView

    ReadView是MVCC中一个非常重要的数据结构,它表示了一个事务在某一时刻能够看到的数据版本范围。当一个事务开始执行时,它会根据当前活跃的事务列表生成一个ReadView,用于确定哪些版本的数据对该事务是可见的。

  • Undo日志

    Undo日志用于保存数据的旧版本信息。当事务进行更新或删除操作时,旧的数据版本会被保存在Undo日志中。这样,如果需要回滚事务或读取旧版本的数据,就可以通过Undo日志来恢复或获取。

  • 活跃事务列表

    活跃事务列表记录了当前系统中所有未提交的事务。这个列表用于生成ReadView,以确定哪些事务的版本对当前事务是可见的。当新的事务开始时,它会被添加到这个列表中;当事务提交或回滚时,它会被从这个列表中移除

  • 行级锁

    虽然MVCC本身不直接依赖于行级锁来实现并发控制,但在某些情况下,行级锁可以与MVCC结合使用,以提供更高级别的并发性能。行级锁可以确保在修改数据时,只有被锁定的行会被阻塞,而其他行仍然可以被其他事务并发访问


在聚簇索引中,每条记录都有额外的三个隐藏字段,分别是db_row_iddb_trx_iddb_roll_pointer

  • db_row_id:当定义了主键时,db_row_id 就是主键id,没有指定时,db_row_id 就是找到的第一个唯一的非空索引列的id,如果唯一的非空索引也没有,那么会自动生成一个主键
  • db_trx_id:事务的id
  • db_roll_pointer:指向undo.log中历史版本数据的指针

在这里插入图片描述

下面重点来看read view 的数据结构

read view 结构

ReadView是一个数据结构,它包含了生成该视图时活跃的事务信息。ReadView 用于确定哪些行版本对当前事务是可见的

read view 中包含了以下四个关键组成部分:creator_trx_idtrx_idsup_limit_id low_limit_id,它们各自的作用如下

creator_trx_id

  • 这是创建当前read view的事务的版本号。它标识了read view的“所有者”或创建者,确保该事务能够看到自己所做的修改,这个应该很好理解。

trx_ids

  • 这是一个集合,也就是上面说的 活跃事务列表,它包含了当前系统中所有活跃(即未提交)的事务的ID。这些事务ID代表了当前正在执行且尚未完成的事务。这些事务的ID是在 read view 创建时活跃的,因此它们所做的修改对于当前事务来说是不可见的。

up_limit_id

  • 这是“系统正处于活跃事务最小版本号”。换句话说,它标识了在read view 创建时,系统中最早开始的一个活跃事务的版本号,也就是从 trx_ids 中选择出来的最小事务ID。这有助于确定哪些旧版本的数据对当前事务是可见的。

low_limit_id

  • 通常被设置为creator_trx_id + 1,或者是创建当前 read view 时系统当前分配的最大事务ID加1。由于事务版本号在事务开始时分配,并且随着新事务的创建而递增,low_limit_id 确保了任何在read view创建之后开始的事务的修改对当前事务都是不可见的

这四个组成部分共同工作,使得InnoDB能够确定一个事务在执行查询时能够看到哪些数据版本。通过比较这些值和当前正在执行的事务的版本号,InnoDB能够确保每个事务都看到一个一致的数据快照,即使其他事务同时在进行修改。这种机制是实现MVCC的核心,它允许高并发的数据库操作,同时保证了数据的一致性和隔离性


可见性规则

访问某条记录的时如何判断该记录是否对当前事务可见呢?看看下面的规则

  • 如果被访问记录的事务id = creator_trx_id,说明当前事务在访问自己修改过记录,是可见的

  • 如果被访问记录的事务id < creator_trx_id,并且不在trx_ids 中,或者说小于 up_limit_id,说明在当前事务创建read view 之前该记录就已经被提交,因此对当前的事务是可见的

  • 如果被访问记录的事务id >= low_limit_id,说明是在当前事务创建read view之 后才新建的事务,那么对当前事务是不可见的

  • 如果被访问记录的事务id 在 > up_limit_id 并且 < low_limit_id,说明这个事务在当前事务创建read view 时还没有被提交。因为活跃事务列表是实时的,当

    • 被访问记录的事务id在trx_ids 中,说名该事务还没有被提交,对当前事务是不可见的
    • 被访问记录的事务id 不在trx_ids 中,说名该事务已经被提交,那么对当前事务是可见的

隔离级别

MVCC只在隔离级别为:可重复读,读已提交 下工作,因为在 READ UNCOMMIT 隔离级别下,每次读取的都是最新的数据,而不是符合当前事务版本的数据行。在 SERIALIZABLE 隔离级别下,事务都是串行执行,根本不存在并发。

**rc级别:**每次操作都会创建新的read view

**rr级别:**仅在第一次操作时生成read view,后面一直用这个read view


MVCC的优缺点

优点: 提高了并非性能,使得大多数读操作可以不用加锁
缺点:要维护系统版本号,需要额外的存储空间、检查和维护工作

InnoDBMVCC(多版本并发控制)是一种用于实现并发访问的机制,它可以提供一定级别的一致性读取。在 InnoDB 中,每个新插入的行都会保存一个当前系统版本号作为行版本号,每个删除的行也会保存一个当前系统版本号作为行删除标识。对于更新操作,InnoDB 会插入一行新记录,并保存当前系统版本号作为行版本号,同时在原来的行上保存当前系统版本号作为删除标识(实际上是通过undo log来备份旧记录)。这样,对于读取操作,可以通过生成一个数据请求时间点的一致性数据快照(Snapshot)来提供一定级别的一致性读取,并且数据库可以提供同一数据的多个版本。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [InnoDB下的MVCC](https://blog.csdn.net/shade7/article/details/119939547)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [InnoDBMVCC的实现](https://blog.csdn.net/fuzhongmin05/article/details/91351933)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗罗的1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值