浅谈MySQL 高性能下的产物——MVCC

相信大家多多少少都听过或者了解过MVCC,知道它大致是一个什么东西,但是想把它讲明白,讲清楚却不是那么容易。

首先我们先解析一下MVCC 的中文名称——多版本并发控制。这个词语我们可以拆分为两个部分来解读,一个是多版本,一个是并发控制;

首先多版本的意思就是,在数据库的每一行数据,它都可能存在多个版本;然后并发控制就是要从这多个版本中,选出某个适合当前操作的版本。

当前读和快照读

所谓的当前读就是在我们使用

select ... lock in share modeselect ... for update和insert、update、delete 等操作的时候读取的是数据的最新版本当前读会对读取到的记录加锁当前读是和锁息息相关的

快照读就是想要读取的行 正在执行 DELETE 或 UPDATE 操作,这时读取操作不会去等待行上锁的释放。相反地,会去读取行的一个快照数据(从多个版本中获取某一个版本的数据),这里的读取其实就是基于MVCC 的非阻塞读。

MVCC 的实现的核心

MVCC 的实现依赖于:隐藏字段、Read View、undo log

隐藏字段

在内部,InnoDB 存储引擎为每行数据添加了两个 隐藏字段(其实是四个字段,但是这里MVCC机制一般只需要用到两个字段)

  • DB_TRX_ID(事务id):表示最后一次插入或更新该行的事务 id。
  • DB_ROLL_PTR(回滚id) 回滚指针,指向该行的 undo log 。如果该行未被更新,则为空

undo-log

undo log 主要有两个作用:

  • 当事务回滚时用于将数据恢复到修改前的样子
  • 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现快照读

要注意:Undo-log中并不仅仅只存储一条旧版本数据,其实在该日志中会有一个版本链,啥意思呢?举个例子:

SELECT * FROM `zz_users` WHERE user_id = 1;
+---------+-----------+----------+----------+---------------------+
| user_id | user_name | user_sex | password | register_time       |
+---------+-----------+----------+----------+---------------------+
|       1 | 熊猫      | 女       | 6666     | 2022-08-14 15:22:01 |
+---------+-----------+----------+----------+---------------------+

UPDATE `zz_users` SET user_name = "竹子" WHERE user_id = 1;
UPDATE `zz_users` SET user_sex = "男" WHERE user_id = 1;

比如上述这段SQL隶属于trx_id=1的T1事务,其中对同一条数据改动了两次,那Undo-log日志中只会存储一条旧版本数据吗? 不会!

从上图中可明显看出:同一行的不同版本数据,会以 roll_ptr 回滚指针作为链接点,然后将所有的旧版本数据组成一个单向链表。最新的旧版本数据,都会插入到链表头中.

ReadView(读视图)

通过上面的隐藏字段undo log ,我们已经清楚了同一行数据的多个不同版本是怎么实现的。

但是对于这多个版本,我究竟在不同的情况下要读取哪个版本呢? 这就是我们的read view 来实现的。

Read View主要是用来做可见性判断,里面保存了 “当前对本事务不可见的其他活跃事务”

通过这个读视图的属性,加上特定规则的算法,我们就可以实现一个事务在什么情况下可以看到哪个版本的数据

具体的可见性算法(也称之为版本链的访问规则)如下:

小结

在了解了MVCC 的三大核心之后,我们可以简单的概括出其实现多版本并发控制的机制。InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性(可见性算法),如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。实现每个事务读到的数据版本可能是不一样的

MVCC 在RR 和RC 下不同的表现

同一个事务 T1 下:

RC 可能返回两次快照读是不同的版本数据(毕竟是读已提交嘛)

而RR 则是返回两次快照读都是一样版本数据

这里其实就是这两种隔离级别生成的 readview 的策略不同,RC 的策略是会去读取新的readview ,而RR 得策略是一直使用第一次读得的readview 。

MVCC➕Next-key-Lock 防止幻读(部分)

InnoDB存储引擎在 RR 级别下通过 MVCC和 Next-key Lock 来解决幻读问题:

1、执行普通 select,此时会以 MVCC 快照读的方式读取数据

在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”

2、执行 select...for update/lock in share mode、insert、update、delete 等当前读

在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!InnoDB 使用 临键锁 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读

参考资料:🔥 

(九)MySQL之MVCC机制:为什么你改了的数据我还看不见? - 掘金 (juejin.cn)

InnoDB存储引擎对MVCC的实现 | JavaGuide(Java面试 + 学习指南)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值