MySQL的 MVCC详解

embedded/2025/2/7 12:56:29/

MVCC是多版本并发控制,允许多个事务同时读取和写入数据库,而无需互相等待,从而提高数据库的并发性能。

在 MVCC 中,数据库为每个事务创建一个数据快照。每当数据被修改时,MySQL不会立即覆盖原有数据,而是生成新版本的记录。每个记录都保留了对应的版本号或时间戳。

依赖实现:隐藏字段(rowId+trxId)+undolog+readview

MVCC本质是采用乐观锁思想, 非阻塞并发读 ,而这个读指的就是快照读 , 而非当前读

  • 当前读就是加锁操作,是悲观锁的实现。当前读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录

SELECT ... FOR UPDATE           #为查询到的每一条记录添加排他锁  
SELECT ... LOCK IN SHARE MODE   #为查询到的每一条记录添加共享锁
  • 快照读又叫一致性读,读的是快照数据,可能不是最新数据,而是历史数据,不加锁的 简单select都属于快照读

ReadView

隐藏字段和 undo 版本链决定是时返回的数据,具体返回哪个数据版本,由这个ReadView控制,即 进行快照读操作时产生的读视图

设计思路

这个ReadView其实就是维护了一个集合,主要包含4个部分,分别如下:

  • creator_trx_id,创建这个 Read View 的事务 ID

  • trx_ids: 表示在生成 ReadView 时当前系统中活跃的读写事务的事务id列表

  • up_limit_id:活跃事务中最小的事务 ID

  • low_limit_id:生成 ReadView 时,应该分配给下一个事务的 ID 值。(最大事务 ID +1)

规则

有了ReadView,那么在访问某条记录时,只需要按照下边的步骤查看记录的对应快照版本

整体操作流程

比如查询一条记录的时候,系统如何通过MVCC找到它:

  1. 首先获取事务自己的版本号trxId,也就是事务 ID;

  2. 获取 当前的系统的 ReadView ,然后与 ReadView 中的事务版本号进行比较;

  3. 如果不符合 ReadView 规则,就从 Undo Log版本链中依次往下获取该记录的历史快照事务ID进行按照上面规则比较;

  4. 最后返回符合规则的数据。若最后一个版本都不可见,说明该条记录对于目前该事务完全不可见(没提交),也就查不到该条记录

读已提交和可重复读

在隔离级别为读已提交时,一个事务中的每一次SELECT查询都会重新获取一次Read View。

这时如果 Read View 不同,就可能产生不可重复读或者幻读的情况

当隔离级别为可重复读的时候,解决了不可重复读,并通过 间隙锁+MVCC 解决了大部分的幻读问题

大部分幻读解决:

  • 因为可重复读复用,第一次readView,所以事务的查询快照结果是一样,不会平白无故多出数据来,通过readView解决了快照读的幻读

  • 间隙锁的话解决了当前读的幻读问题,防止其他事务在这个间隙间插入新的记录。

例外情况:

  • 比如,如果两个事务,事务1先进行快照读,然后事务2插入了一条记录并提交,在事务1中进行了当前读之后,再进行快照读也会发生幻读。

因为此时一个事务只在第一次 SELECT 的时候会获取一次 Read View,而后面所有的查询都会复用这个 Read View

举例说明
读已提交

每次读取数据前都生成一个ReadView

-- 现在有两个 事务id 分别为 10 、 20 的事务在执行
​
# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
​
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...

此刻,表student 中 id1 的记录得到的版本链表如下所示:

假设现在有一个使用 READ COMMITTED 隔离级别的事务30开始执行:

# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 10、20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三',因为王五李四事务是活跃的,所以查undolog 读取历史快照张三

之后,我们把 事务id10 的事务提交一下:

# Transaction 10
COMMIT;

然后再到 事务id20 的事务中更新一下表 studentid1 的记录:

# Transaction 20
...
UPDATE student SET name="钱七" WHERE id=1;
UPDATE student SET name="宋八" WHERE id=1;

此刻,表student中 id1的记录的版本链就长这样:

然后再到事务30中继续查找这个 id 为 1 的记录

# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1;   # 得到的列name的值为'王五'

所以看到,两次读取的记录并不一样,所以不可重复嘛

可重复读

只会在第一次执行查询语句时生成一个 ReadView ,之后的查询就不会重复生成

得到的列name的值仍为'张三',因为复用第一次的readview,所以上图还是认为王五没有提交


http://www.ppmy.cn/embedded/160304.html

相关文章

chrome插件模板;使用 React 18 和 Webpack 5 的 Chrome 扩展样板

一、软件介绍(文末提供下载) 这是一个基本的 Chrome 扩展样板,可帮助您编写模块化和现代的 Javascript 代码,轻松加载 CSS,并在代码更改时自动重新加载浏览器。 github地址:https://github.com/lxieyang/c…

【Block总结】MDCR,多尺度深度可分离卷积,捕捉不同感受野范围的空间特征

论文信息 HCF-Net(Hierarchical Context Fusion Network)是一种新提出的深度学习模型,专门用于红外小目标检测。该论文于2024年3月16日发布,作者包括Shibiao Xu、ShuChen Zheng等,主要研究机构为北京邮电大学。该模型…

Spring Boot实现多数据源连接和切换

文章目录 前言一、多数据源配置与切换方案二、实现步骤 1. 创建多个 DataSource 配置类2. 创建 DataSource 配置类3. 创建动态数据源路由类4. 实现 DynamicDataSource 类5. 创建 DataSourceContextHolder 来存储当前的数据源标识6. AOP 方式切换数据源7. 自定义注解来指定数据…

计算机网络知识速记:HTTP与HTTPS

计算机网络知识速记:HTTP与HTTPS 一、HTTP基础知识 HTTP(超文本传输协议)是一个无状态的协议,通常用于在客户端与服务器之间传输数据。HTTP采用的是一种请求-响应模型,客户端发出请求后,服务器返回数据。…

c++:vector

1.使用 1.1构造函数 常见的三种构造方式:空构造,拷贝构造,指定元素构造 1.2iterator begin和end也分为正向和反向。 注意:反向迭代器可以反向遍历是因为在定义rbegin和rend函数的时候把尾地址给到了rbegin,而不是说改…

流媒体娱乐服务平台在AWS上使用Presto作为大数据的交互式查询引擎的具体流程和代码

一家流媒体娱乐服务平台拥有庞大的用户群体和海量的数据。为了高效处理和分析这些数据,它选择了Presto作为其在AWS EMR上的大数据查询引擎。在AWS EMR上使用Presto取得了显著的成果和收获。这些成果不仅提升了数据查询效率,降低了运维成本,还…

基于Flask的商城应用系统的设计与实现

【FLask】基于Flask的商城应用系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统包括前端商城和后台管理系统,旨在为用户提供一个全面、便捷的在线购物平台及高…

大模型 RAG 优化之预生成qa对

1. 写在前面 检索增强生成 (Retrieval-Augmented Generation, RAG) 是一种将检索 (Retrieval) 和生成 (Generation) 相结合的技术,它利用检索到的相关信息来增强大型语言模型 (LLM) 的生成能力。传统的 RAG 系统通常直接使用用户输入的 query 在文档库中进行检索 (query-doc …