MySQL 高性能下的产物——MVCC

MVCC

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

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

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

当前读和快照读

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

select ... lock in share mode,select ... 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中并不仅仅只存储一条旧版本数据,其实在该日志中会有一个版本链,啥意思呢?举个例子:

sql 复制代码
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面试 + 学习指南)

相关推荐
新知图书28 分钟前
Rust编程与项目实战-模块std::thread(之一)
开发语言·后端·rust
盛夏绽放1 小时前
Node.js 和 Socket.IO 实现实时通信
前端·后端·websocket·node.js
Ares-Wang1 小时前
Asp.net Core Hosted Service(托管服务) Timer (定时任务)
后端·asp.net
Rverdoser2 小时前
RabbitMQ的基本概念和入门
开发语言·后端·ruby
Tech Synapse3 小时前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
.生产的驴3 小时前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
微信-since811923 小时前
[ruby on rails] 安装docker
后端·docker·ruby on rails
代码吐槽菌5 小时前
基于SSM的毕业论文管理系统【附源码】
java·开发语言·数据库·后端·ssm
豌豆花下猫5 小时前
Python 潮流周刊#78:async/await 是糟糕的设计(摘要)
后端·python·ai
YMWM_5 小时前
第一章 Go语言简介
开发语言·后端·golang