【MySQL基础】MVCC多版本并发控制

文章目录

  • MVCC - 多版本并发控制
  • 一、MVCC 概述
    • 1、三种并发场景
    • 2、当前读 & 快照读
    • 3、MVCC的作用
    • 4、结合MVCC处理并发问题
  • 二、MVCC 实现原理
    • 1、隐式字段
    • 2、回滚日志 Undo Log
    • 3、一致性视图 Read View
      • 1)什么时候生成?
      • 2)可见性判断
      • 3)可见性算法(属性)
      • 4)可见性算法(实现)
      • 5)可见性算法(小结)
    • 4、举例说明(版本链)
  • 三、MVCC 与 可重复读 RR
    • 1、事务的启动时机
    • 2、事务A读取流程
    • 3、更新前 要 当前读
    • 4、当前读 遇上 行锁
    • 5、小结
  • 四、MVCC 与 读已提交 RC
    • 1、Read View 创建时机
    • 2、事务A 读取流程
  • 五、MVCC 与 幻读
    • 1、案例
    • 2、案例分析 - 可重复读
    • 3、案例分析 - 读已提交
    • 4、结论

MVCC - 多版本并发控制

MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。通过维持一个数据的多个版本,来控制并发。

一、MVCC 概述

1、三种并发场景

  • 读-读:不存在任何问题,也不需要并发控制。
  • 读-写:有并发问题,可能会造成事务隔离性问题,可能遇到脏读幻读不可重复读
  • 写-写:有并发问题,可能会存在更新丢失更新覆盖的问题。

2、当前读 & 快照读

  • 当前读

    读取的是记录的最新版本,读取时要加锁,保证其他并发事务不能修改当前记录。(如共享锁和排他锁)

  • 快照读

    读取的可能是记录的历史版本,读取时不加锁

注意:快照读在MySQL的串行隔离级别下会上升为当前读,即使是select操作也会加锁。

3、MVCC的作用

MVCC就是为了不采用悲观锁这样性能不佳的形式去解决读-写的并发问题,而提出的解决方案。

  • 可以做到读操作不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能(解决了读-写并发问题)
  • 可以解决脏读幻读不可重复读等事务隔离问题,但不能解决更新丢失的问题(仍存在写-写并发问题)

4、结合MVCC处理并发问题

因为有了MVCC,所以我们可以形成两个组合:

  • MVCC + 悲观锁:MVCC解决读-写的并发问题,悲观锁解决写-写的并发问题

  • MVCC + 乐观锁:MVCC解决读-写的并发问题,乐观锁解决写-写的并发问题

这种组合的方式,可以最大程度的提高数据库并发性能,并解决数据库的并发问题。

二、MVCC 实现原理

MVCC 在 MySQL 中的具体实现是由 隐式字段 + Undo Log + Read View 完成的。

1、隐式字段

数据库中的每行记录,除了我们自定义的字段外,还有数据库隐式定义的一些字段:

名称 字段 占用 作用
隐式主键 DB_ROW_ID 6 byte 如果数据表没有主键,InnoDB会自动根据 DB_ROW_ID 产生一个聚簇索引。
事务ID DB_TRX_ID 6 byte 记录 创建这条记录最后一次修改这条记录 的事务ID(即最新的事务ID)
回滚指针 DB_ROLL_PTR 7 byte 指向这条记录的上一个版本(存储于rollback segment里)

2、回滚日志 Undo Log

MVCC 使用到的快照存储在 undo log 中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。

【MySQL基础】MVCC多版本并发控制_第1张图片

如何获取一条记录的历史版本呢?

【MySQL基础】MVCC多版本并发控制_第2张图片

虚线框里是同一行数据的4个版本,图中的三个虚线箭头U1、U2、U3就是undo log。

  • V1、V2、V3并不是物理上真实存在的,而是每次需要的时候,根据 当前版本V4undo log 计算出来的。

3、一致性视图 Read View

上一节说到,多个事务对同一行记录进行更新会产生多个历史版本,这些历史版本可以通过 当前版本undo log 计算出来。

但是,如果一个事务要想查询某条行记录,需要读取哪个版本的行记录呢?这时就需要根据 Read View 来进行判断了。

1)什么时候生成?

不同的隔离级别下,生成Read View的时机也是不一样的(MVCC主要解决 RC 和 RR 两种隔离级别)

  • 可重复读(RR):当前事务第一次select时,创建Read View,之后事务里的其他查询都共用这个Read View。
  • 读已提交(RC):每一个select执行之前,都会重新创建一个新的Read View。

2)可见性判断

如何通过 Read View 判断当前事务能够看到哪个版本的数据呢?

  1. 获取行记录的最新事务ID(即DB_TRX_ID
  2. 根据 Read View 的 可见性算法 判断可见性。
  3. 若不符合可见性,就通过 回滚指针 取出 上一版本的事务ID 再进行判断
    • 遍历版本链的 事务ID(从链首到链尾,从最新的版本开始判断),直到找到符合可见性的 事务ID
  4. 符合可见性的事务ID所在的版本,就是当前事务能看见的最新版本。

3)可见性算法(属性)

// MySQL源码分析
class ReadView {
   
    trx

你可能感兴趣的:(MySQL,mysql)