学习 MySQL MVCC

MVCC 基本概念

当前读

读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select…lock in share mode【共享锁】,select…for update、update、insert、delete【排他锁】都是一种当前读。

快照读

简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。

Read Committed:每次select,都生成一个快照读。

Repeatable Read:开启事务后第一个 select 语句才是快照读的地方。

Serializable:快照读会退化为当前读。

演示当前读和快照读的区别:

在这里插入图片描述

MVCC

全称 【Multi-Version Concurrency Control】,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为 MySQL 实现 MVCC 提供了一个非阻塞读功能、一种无锁的实现方式。

MVCC 的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView

MVCC 实现原理

隐藏字段

MySQL 中的行数据,除了我们肉眼能看到的字段之外,其实还包含了一些隐藏字段,它们在内部使用,默认情况下不会显示给用户。如下图除了 id、age、name 用户创建的字段,还有 DB_TRX_ID、DB_ROLL_PTR

在这里插入图片描述

字段含义
DB_TRX_ID最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
DB_ROLL_PTR回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
DB_ROW_ID隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。

undo log 日志

undo 日志(Undo Log)是 MySQL 中的一种重要的事务日志,undo 日志的作用主要有两个方面:

  • 事务回滚:记录数据在执行之前是什么样子的,在 insert、update、delete 的时候产生的便于数据回滚的日志。
  • MVCC 实现:MVCC 是 InnoDB 存储引擎的核心特性之一。通过使用 undo 日志,MySQL 可以为每个事务提供独立的事务视图,使得事务读取数据时能看到一致且符合隔离级别要求的数据版本。

undo log 的版本链

不同事务或相同事务对同一条记录进行修改,会导致该记录的 undo log 生成一条记录版本链表,链表的头部是最新的旧记录,链表的尾部是最早的旧记录,回滚指针指向最近的一次的修改记录。

如下图:

事务 2 将 age 改为 3,修改该行数据时,数据库会先对该行加排他锁,然后把该行数据拷贝到 undo log 中作为旧记录,即在 undo log 中有当前行的拷贝副本,当拷贝完成后,隐藏字段中的 BD_TRX_ID 会自增,DB_ROLL_PTR 回滚指针指向 undo log 的副本记录,即表示我的上一个版本就是它,事务提交后,释放锁。

事务 3、4 同理,指向上一个版本,此时就形成了一条版本链。

在这里插入图片描述

具体流程如下:

  1. 在更新或删除操作之前,MySQL 会将旧值写入 undo log 中。
  2. 当事务需要回滚时,MySQL 会根据事务的 undo log 记录,通过 DB_ROLL_PTR 找到对应的 undo log。
  3. 根据 undo log 中记录的旧值,MySQL 将旧值恢复到相应的数据行中,实现数据的回滚操作。

ReadView

ReadView(读视图)是快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。

ReadView 中包含了四个核心字段:

字段含义
m_ids当前活跃的事务ID集合(或者叫未提交的事务ID)
min_trx_id最小活跃事务ID(在m_ids找个最小的)
max_trx_id预分配事务ID,就是当前最大事务ID+1(因为事务ID是自增的)
creator_trx_idReadView 创建者的事务ID

其中版本链数据访问规则为:

在这里插入图片描述

不同的隔离级别,生成 ReadView 的时机不同:

READ COMMITTED:在事务中每一次执行快照读时生成ReadVieW。

REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该 ReadView,所以才叫可以重复读。

为方便大家记忆访问规则可以看下图:

绿色:可见
红色:不可见
在这里插入图片描述

RC 隔离级别下的 MVCC

在这里插入图片描述

以第一次查询为例:

trx_id(当前事务ID):4 -> 3 -> 2 -> 1

m_ids(当前活跃的事务ID集合):{3,4,5}

min_trx_id(最小活跃事务ID):3

max_trx_id(预分配事务ID,当前最大事务ID+1):6

creator_trx_id(ReadView创建者的事务ID):5

从条件一至到条件四,trx_id一直从 4 -> 3 -> 2 -> 1,只要有规则成立的就返回当条记录

在图上例子中,当 trx_id 等于 2 的时候,trx_id < min_trx_id 成立了,所以就返回该条记录

RR 隔离级别下的 MVCC

在 RC 隔离级别,同一个事务中读取两次相同的记录是一样的,因为在第一次读取数据时生成一个 ReadView,后面会复用第一次生成的,所以才叫可重复读。

在这里插入图片描述

MVCC解决了什么问题

  1. 脏读:在没有MVCC的情况下,一个事务可能读取到另一个未提交事务修改过的数据,如果后者回滚,那么前者读取的就是“脏”数据。MVCC通过确保事务只能读取到已提交的数据版本,从而避免了脏读问题。

  2. 不可重复读:在数据库操作期间,如果没有适当的隔离机制,一个事务多次读取同一数据可能会得到不同的结果,因为其他事务可能在此期间修改了这些数据。MVCC通过为每个事务提供一个一致的数据快照,使得在同一个事务中的多次读取操作能够得到相同的结果,从而解决了不可重复读问题。

  3. 幻读:幻读是指在同一个事务中,执行相同的查询语句,但第二次查询却返回了第一次查询中没有的新记录。虽然MVCC并不能完全解决幻读问题,但它可以在一定程度上减少幻读的发生,尤其是在读取时没有主动加锁的情况下。在某些数据库系统中,如MySQL的InnoDB存储引擎,幻读问题可以通过使用间隙锁来进一步解决。

  4. 提高并发性能:MVCC允许读写操作同时进行,读操作不需要等待写操作完成,写操作也不会阻塞读操作。这种并发控制方式显著提高了数据库的并发处理能力,尤其是在读多写少的场景下。

  5. 减少锁的使用:虽然MVCC本身也是一种形式的锁定机制,但它减少了传统意义上的行锁或表锁的需求。通过维护多个数据版本,MVCC能够在不加锁的情况下实现数据的并发访问,从而减少了锁竞争和锁开销。

  6. 避免数据丢失:在写写操作中,如果没有合适的并发控制机制,可能会导致数据丢失问题。MVCC通过确保每个写操作都在其自己的数据版本上进行,从而避免了数据丢失问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tiantian17)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值