MVCC

功能

通过维护数据历史版本,从而解决并发情况下的读一致性问题。

核心内容

主要包含了:

  • 版本链:每一个数据行包含两个隐藏数据,事务id和回滚指针,将每一次更改通过回滚指针连起来。
  • 事务链表:事务开始的时候,就会加入事务链表。提交后,就会在事务链表中删除。
  • ReadView:一个事务在开始执行SQL的时候,会先创建一个ReadView。该ReadView存储着事务链表。通过判断要访问的事务是否在ReadView中,从而判断该事务里的数据该不该读。
    preview

版本链

每一个数据行有两个隐藏的数据列(实际上,如果数据行没有主键且没有唯一列,InnoDB除了以上两个以外,还会新建一个隐藏列作为索引主键):

  • trx_id(事务id)
  • roll_pointer(回滚指针)
    假设hero表中只有一行记录,当时插入的事务id为80。此时,该条记录的示例图如下:

img

假设之后两个事务id分别为100200的事务对这条记录进行UPDATE操作,操作流程如下:

img

由于每次变动都会先把undo日志记录下来,并用roll_pointer指向undo日志地址。因此可以认为,对该条记录的修改日志串联起来就形成了一个版本链,版本链的头节点就是当前记录最新的值。如下:

img

事务链表

MySQl中的事务在开始到提交的时候都会存进事务链表中。用来表示当前活跃的事务。 一旦提交就会从事务链表中清除。
img

ReadView

preview

  • 参数
    • trx_ids:用来表示当前活跃的事务
    • up_trx_id:trx_ids中的最大值
    • low_trx_id:trx_ids中的最小值
    • creator_trx_id:创建该ReadView的事务id
  • 描述
    一个事务在开始执行SQL语句的时候,会创建一个ReadView。有了ReadView之后,就可以判断某个版本的记录是否对当前事务可见。
    • 被访问的记录trx_id与creator_trx_id相同:可以访问。
    • 被访问的记录trx_id小于low_trx_id:说明已经提交,可以访问。
    • 被访问的记录trx_id大于等于up_trx_id:可能是在创建ReadView之后,提交情况未知,不可访问。
    • 被访问的记录trx_id在low_trx_id和ip_trx_id之间
      • 如果在trx_ids中,说明未提交,不可访问
      • 如果不在trx_ids中,说明已经提交,可以访问

MVCC与隔离级别

参考MySQL事务隔离级别和MVCC

  • Read Committed:在每一次select的时候,会创建readview表。当要访问某个数据的时候,先去查看该数据行的版本链,
    • 如果最新的版本链的事务id小于readview的low_trx_id,那么表示最近修改该数据行的事务已经提交,可以访问;
    • 如果大于readview的max_trx_id,那么不可以访问,顺延回滚指针查看更老的版本数据行;
    • 如果在readview trx_ids里,那么说明该事物活跃,不可以访问,顺延回滚指针;
    • 如果在(low_trx_id, up_trx_id)范围里,且不在m_id,说明该事务已经提交,可以访问。
  • Repeatable Read:只在第一次读的时候,生成一个ReadView,即使readview范围里,有事务提交了,也不会去读,从而保证了,读到的数据前后一致性。
  • 在Repeatable Read事务机制下,使用Next-Key Lock(锁定一个范围) 可以解决幻读。
    • 对于快读,select,可以直接使用mvcc的特性进行读;
    • 对于当前读,增删改,那么就需要使用Next-Key Lock加锁。也就是当一个事务1正在select一个事务的时候,会对相应的查找范围加一个next-key的锁,当事务2想要对这个范围的加入新的数据行是会被拒绝的。
05-09
### MVCC 的原理及实现 #### 什么是 MVCCMVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库管理系统中用于提升并发性能的一种机制。它通过保存数据的历史版本来减少读写之间的冲突,从而允许多个事务在同一时间访问同一份数据的不同版本而不互相干扰。 在 MySQL InnoDB 存储引擎中,MVCC 是通过隐藏列 `DB_TRX_ID` 和 `DB_ROLL_PTR` 来实现的[^1]。这些隐藏列分别存储了最后修改该记录的事务 ID 和指向历史版本的回滚指针。 --- #### MVCC 的核心组件 以下是 MVCC 实现的核心组成部分: 1. **两个隐式字段** - `DB_TRX_ID`: 记录最后一次修改该行数据的事务 ID。 - `DB_ROLL_PTR`: 指向 Undo Log 中该行数据的上一个版本地址,形成一条版本表[^3]。 2. **Undo Log (回滚日志)** 当某条记录被更新时,旧版本的数据会被保存到 Undo Log 中,并通过 `DB_ROLL_PTR` 进行接。这样就形成了一个版本,使得不同事务能够看到不同的数据版本[^3]。 3. **Read View (一致性视图)** Read View 是由事务启动时生成的一组信息,主要包括以下几个部分: - 所有已提交事务的最大事务 ID (`max_trx_id`)。 - 非活跃事务列表。 - 活跃事务列表及其对应的事务 ID[^1]。 使用 Read View 可以判断某一版本的数据是否对当前事务可见。具体来说,如果某个版本的 `DB_TRX_ID` 小于等于 `max_trx_id` 并且不在活跃事务列表中,则该版本对当前事务可见[^3]。 --- #### 快照读与当前读的区别 - **快照读**: 不加锁的 SELECT 查询属于快照读,它是基于 MVCC 实现的非阻塞读操作。快照读不会直接读取最新的数据版本,而是根据 Read View 判断哪些版本对该事务可见并返回相应版本的数据[^2]。 - **当前读**: 加锁的 SELECT 查询或者涉及索引范围扫描的操作称为当前读。当前读总是读取最新的数据版本,并会对所读取的数据施加共享锁或排他锁,因此它不属于 MVCC 的范畴[^2]。 --- #### 如何解决隔离级别的问题 MVCC 主要应用于 RC(Read Committed)和 RR(Repeatable Read)两种隔离级别,在这两种模式下可以通过一致性的快照读避免以下问题: 1. **脏读**: 如果事务 A 修改了一条记录但尚未提交,其他事务无法读取未提交的状态,因为它们只会查看已经提交的版本[^1]。 2. **不可重复读**: 在 RR 隔离级别下,同一个事务多次读取同一条记录的结果是一致的,这是因为每次查询都会使用相同的 Read View[^1]。 3. **幻读**: 对于快照读而言,由于 MVCC 提供的是逻辑上的快照而非物理锁定,所以可以有效防止幻读现象的发生。然而需要注意的是,对于当前读场景下的幻读问题仍然需要借助间隙锁等手段加以处理。 --- #### 性能优化特点 相比传统的悲观锁策略,MVCC 显著提高了系统的吞吐量和响应速度。其主要原因在于大多数只读型工作负载无需等待写入完成即可继续运行,减少了因资源竞争而导致的延迟[^2]。 ```sql -- 示例代码展示如何触发快照读 vs 当前读 SELECT * FROM table WHERE id = 1; -- 快照读 SELECT * FROM table WHERE id = 1 FOR UPDATE; -- 当前读 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值