MySQL的可重复读隔离级别实现原理分析

MySQL 的 可重复读(Repeatable Read, RR) 隔离级别主要通过 多版本并发控制(Multi-Version Concurrency Control, MVCC)锁机制(特别是间隙锁) 来实现的。其核心目标是:在一个事务内,无论读取多少次相同的行,看到的数据都是一致的(即第一次读取时建立的“快照”),并且防止了“不可重复读”问题(同一个事务内两次读取同一行得到不同值)。同时,在 InnoDB 的 RR 级别下,还通过间隙锁机制(Next-Key Locks)极大地防止了“幻读”问题(同一个事务内两次范围查询得到不同的行集)。

以下是 RR 隔离级别实现的核心机制:

1. 多版本并发控制 (MVCC) - 实现快照读 (Snapshot Read)

MVCC 是 RR 级别实现“可重复读”特性的基石。

  • 隐式字段: InnoDB 在每个聚簇索引记录(数据行)中隐藏了 3 个关键字段:
    • DB_TRX_ID (6 Bytes):记录创建/最后修改该行的事务ID。 当一个事务开始修改某行时,会把自己的事务 ID 写入这个字段。
    • DB_ROLL_PTR (7 Bytes):指向该行在 undo log 中的回滚段指针。 这个指针指向该行之前版本(旧版本)在 undo log 中的位置,形成一个版本链。
    • DB_ROW_ID (6 Bytes):单调递增的行 ID(如果表没有定义主键,InnoDB 会自动生成这个作为聚簇索引)。
  • Undo Log (回滚日志): 当事务修改数据时:
    • 会先将数据行的旧版本复制到 undo log 中。
    • 然后修改数据行(更新 DB_TRX_ID 为当前事务 ID,更新 DB_ROLL_PTR 指向刚写入 undo log 的旧版本记录)。
    • 这样,每个被修改的行都通过 DB_ROLL_PTR 链接成一个历史版本链(链表)。
  • ReadView (一致性视图): 这是 MVCC 的关键数据结构,决定了事务能看到哪些版本的数据。当事务执行第一个 SELECT 语句(或显式开启只读事务)时,InnoDB 会为该事务生成一个 ReadView。一个 ReadView 主要包含:
    • m_ids: 生成 ReadView 时,系统中活跃(未提交)的事务 ID 列表
    • min_trx_id: m_ids 中的最小值。
    • max_trx_id: 生成 ReadView 时,系统应该分配给下一个新事务的 ID(即当前最大事务 ID + 1)。
    • creator_trx_id: 创建该 ReadView 的当前事务自己的 ID(只读事务为 0)。
  • 可见性规则: 当事务需要读取一行时,它沿着该行的版本链(通过 DB_ROLL_PTR 回溯)查找第一个满足以下条件的版本:
    1. 版本对应的 DB_TRX_ID < min_trx_id:该版本是在 ReadView 创建之前就已提交的事务修改的。可见
    2. 版本对应的 DB_TRX_IDm_ids 中:该版本是由 ReadView 创建时还活跃(未提交) 的事务修改的。不可见。继续查找更旧的版本。
    3. 版本对应的 DB_TRX_ID >= max_trx_id:该版本是由 ReadView 创建之后才开启的事务修改的。不可见。继续查找更旧的版本。
    4. 版本对应的 DB_TRX_ID = creator_trx_id:该版本是由当前事务自身修改的。可见
    • 如果找到链头(最初的版本)仍不可见,则认为该行对该事务不可见(如同不存在)。
  • RR 级别的关键点:
    • 在 RR 级别下,一个事务只在第一次执行 SELECT 时生成一个 ReadView
    • 后续在该事务内的所有 普通 SELECT 语句(快照读)复用这个最初的 ReadView
    • 因此,无论之后其他事务如何修改、提交或回滚,该事务看到的数据始终是第一次 SELECT 时那个“快照”版本的数据。这就保证了“可重复读”。

2. 锁机制 (Locking) - 处理当前读 (Current Read) 和防止幻读

MVCC 主要解决了快照读(普通 SELECT 的可重复读问题。但对于当前读(如 SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE, DELETE, INSERT,以及需要防止幻读,就需要用到锁机制,特别是 Next-Key Locks

  • 行锁 (Record Locks): 锁定索引记录本身。
  • 间隙锁 (Gap Locks): 锁定索引记录之间的间隙(一个开区间),防止其他事务在这个间隙中插入新记录。
  • 临键锁 (Next-Key Locks): 行锁 + 间隙锁的组合。锁定索引记录本身以及该记录之前的间隙(一个左开右闭区间)。这是 InnoDB 在 RR 级别下默认使用的锁类型
  • RR 级别下如何防止幻读:
    • 当一个事务执行当前读(例如 SELECT * FROM t WHERE id > 100 FOR UPDATE)时:
      1. InnoDB 不仅会锁住所有满足条件 id > 100现有行(行锁)。
      2. 还会在这些现有行的索引记录范围之后(以及可能的间隙之间)加上间隙锁 (Gap Locks)临键锁 (Next-Key Locks)
    • 例如,如果现有 id 是 101, 105, 110。那么 id > 100 的查询可能会锁定:
      • 现有行:101, 105, 110(行锁)
      • 间隙:(100, 101), (101, 105), (105, 110)(间隙锁)
      • 以及最大值之后的间隙:(110, +∞)(间隙锁)
    • 这些间隙锁会阻止其他事务在这些被锁定的间隙中插入任何新的记录(例如插入 id=102, 106, 115 等)。
    • 因此,在该事务提交之前,其他事务无法插入满足其查询条件(id > 100)的新行。当该事务再次执行相同的范围查询时,就不会看到新插入的行(幻行),从而防止了幻读。
  • 为什么 RR 级别能“极大防止”而非“绝对防止”幻读?
    • 快照读 (SELECT): 基于 MVCC 的 ReadView,它看到的是固定快照,本身就不会看到其他事务新提交的数据(包括幻行)。所以快照读天然不会发生幻读。
    • 当前读 (SELECT ... FOR UPDATE等): 通过 Next-Key Locks 锁住索引记录和间隙,阻止了其他事务在锁定范围内插入新行,从而防止了当前读的幻读。
    • “极大防止”的细微点: 如果一个事务 T1 只使用快照读 (SELECT),它看不到其他事务插入的幻行(因为 MVCC)。但是,如果 T1 之后在同一个事务内执行了一个写操作(如 UPDATEDELETE),而这个写操作恰好影响到了其他事务新插入并提交的行(这些行对 T1 的快照读是不可见的),那么 T1 的写操作会“看到”这些新行(写操作需要当前读并可能加锁)。如果这个写操作修改了这些新行,那么 T1 后续的读操作(即使是快照读)再读取这些被自己修改的行时,就会看到它们了。这种情况理论上构成了一种特殊的幻读现象(写操作发现了之前读操作没看到的行)。不过这种情况相对罕见,且很多业务场景下可以接受或规避。因此,通常说 InnoDB 的 RR 级别通过 Next-Key Locks “极大防止”了幻读。

总结 MySQL RR 隔离级别的实现

  1. MVCC (核心):
    • 利用隐藏字段 (DB_TRX_ID, DB_ROLL_PTR) 和 Undo Log 构建行数据的历史版本链。
    • 事务在第一次执行快照读 (SELECT) 时生成一个 ReadView
    • 所有后续的快照读都复用这个 ReadView,通过版本链的可见性规则访问历史快照数据,保证可重复读
  2. Next-Key Locks (关键补充):
    • 默认使用临键锁(行锁 + 间隙锁)。
    • 当前读 (SELECT ... FOR UPDATE/LOCK IN SHARE MODE, UPDATE, DELETE, INSERT) 时锁定索引记录及其前后的间隙。
    • 阻止其他事务在锁定范围内插入新记录,从而极大防止了幻读的发生。
  3. Undo Log 的清理:
    • 不再被任何活动事务的 ReadView 引用的旧版本数据(在 Undo Log 中)会被 Purge 线程清理掉。

因此,InnoDB 通过巧妙地结合 MVCC 的快照机制(处理读)Next-Key Locks 的锁定机制(处理写和范围控制),高效且强有力地实现了可重复读(RR)隔离级别的语义要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/91434.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用Java自定义格式,循环导出数据、图片到excel

利用Java自定义格式&#xff0c;循环导出数据、图片到excel1、自定义格式循环导出数据1.1.设置格式1.1.1、居中样式1.1.2、应用样式到合并区域1.1.3、合并单元格1.1.4、设置列宽1.2、写入数据1.2.1、创建标签头部1.2.2、写入标签内容2、自定义格式循环导出图片2.1、设置格式并插…

SAP学习笔记 - 开发45 - RAP开发 Managed App New Service Definition,Metadata Extension

上一章讲了在 Data Model View ( CDS View for BO Structure )基础上创建 Projection View ( CDS View for BO Projection )。 SAP学习笔记 - 开发44 - RAP开发 Managed App 建 Projection View&#xff0c;Provider Contract&#xff0c;用 redirected to 设定父子关系-CSDN博…

React强大且灵活hooks库——ahooks入门实践之高级类hook(advanced)详解

什么是 ahooks&#xff1f; ahooks 是一个 React Hooks 库&#xff0c;提供了大量实用的自定义 hooks&#xff0c;帮助开发者更高效地构建 React 应用。其中高级类 hooks 是 ahooks 的一个重要分类&#xff0c;专门用于处理一些高级场景&#xff0c;如受控值、事件发射器、性能…

计算机网络——数据链路层(25王道最新版)

数据链路层前言数据链路层的功能封装成帧&#xff08;组帧&#xff09;字符计数法字节填充法零比特填充法违规编码法小节差错控制检错编码奇偶校验码CRC校验码&#xff08;循环冗余校验码&#xff09;基本思想如何构造如何检错纠错纠错编码海明校验码设计思路求解步骤&#xff…

【PTA数据结构 | C语言版】字符串替换算法

本专栏持续输出数据结构题目集&#xff0c;欢迎订阅。 文章目录题目代码题目 请编写程序&#xff0c;将给定主串 s 中的子串 sub_s 替换成另一个给定字符串 t&#xff0c;再输出替换后的主串 s。 输入格式&#xff1a; 输入给出 3 个非空字符串&#xff0c;依次为&#xff1a…

事物生效,订单类内部更新订单

代码如下以下代码1不生效&#xff0c;2生效解决方案1&#xff0c;外层方法加注解&#xff0c;内层不加2&#xff0c;不要拆分方法&#xff0c;把更新订单操作放在带事物的大方法中3&#xff0c;拆方法&#xff08;内部&#xff09;&#xff0c;注入自己&#xff0c;用代理对象调…

非对称加密:RSA

文章目录 非对称加密:RSA 1、RSA 加解密 2、RSA 生成密钥对(公钥、私钥)、加解密 参考资料 非对称加密:RSA 1、RSA 加解密 <!-- RSA --><!-- 引入jsencrypt库 --><script src="https://cdn.bootcdn.net/ajax/libs/jsencrypt/3.3.2/jsencrypt.min.js&q…

MongoDB 数据库 启用访问控制

0. 最近服务器安装了 MongoDB 被勒索了 测试服务器安装了 MongoDB 等&#xff0c;开放了 27017 对所有 ip。 哈哈哈哈哈哈&#xff0c;问就是有点犯懒&#xff0c;之前都是只允许自己的 ip。 好家伙&#xff0c;然后没过几个小时&#xff0c;数据库集合被清空&#xff0c;只留…

【Unity Sprite属性拓展】

Unity Inspector 精灵图预览为 Unity 中的 Sprite 类型属性提供了​​增强版的 Inspector 显示​​&#xff0c;在保留标准精灵选择功能的基础上&#xff0c;添加了大型预览图和精灵名称显示功能代码 using UnityEngine; using UnityEditor;// 1️⃣ 告诉 Unity&#xff1a;所有…

细菌实验入门:浓度测定与菌种鉴定技术详解

在微生物实验中&#xff0c;细菌浓度的精准测定和菌种的准确鉴定是两项基础且核心的操作。本文将详细介绍相关技术的原理、操作步骤及注意事项&#xff0c;为新手提供系统性指导。一、细菌浓度测定方法1. 光密度法&#xff08;OD600&#xff09;&#xff1a;快速定量的首选原理…

GaussDB 数据库架构师修炼(一)数据库容量规划

1、容量规划的定义GaussDB容量规划是指根据客户业务系统的负载需求或历史运行数据&#xff0c;进行合理规划GaussDB的计算、存储和网络资源配置&#xff0c;以满足业务系统正常使用和未来若干年负载增长诉求的过程。2、容量规划活动主要步骤需求收集调研生产系统的业务特征&…

hashMap原理(一)

概念HashMap是java中一种非常常用的基于哈希表的数据结构&#xff0c;允许o(1)的时间复杂度进行元素插入&#xff0c;查找&#xff0c;和删除。它通过”键-值“ 对的方式存储数据。总的来说&#xff1a;HashMap的底层原理&#xff1a;数组链表红黑树&#xff08;jdk1.8之后还涉…

Ubuntu24 辅助系统-屏幕键盘的back按键在网页文本框删除不正常的问题解决方法

Ubuntu24 辅助系统-屏幕键盘的back按键异常 问题描述ubuntu24这个屏幕键盘&#xff0c;只有在网页的搜索框或者文本框&#xff0c;比如百度首页的搜索框&#xff0c;留言的文本框&#xff0c;才会出现点击back按钮的时候&#xff0c;出现了先选中当前这个字符&#xff0c;删除此…

自然语言指令驱动的工业机器人协同学习系统:大语言模型如何重塑智能体协作范式

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

web:js的switch语句

在js中,switch语句是一种用于根据不同的条件执行不同代码块的控制流语句。它类似于多个if...else if...else语句,但结构更清晰,特别是在有多个条件分支的情况下。 基本语法 switch (expression) {case value1:// 当expression的值等于value1时执行这里的代码break;case va…

为何说分布式 AI 推理已成为下一代计算方式

2024 年&#xff0c;我们见证了人工智能创新的空前爆发。AI 的快速发展令很多人惊叹&#xff0c;为了训练更先进的大语言模型&#xff08;LLM&#xff09;&#xff0c;科技巨头争相获取强大的 GPU。如今&#xff0c;AI 正在无缝融入我们世界的每个角落。在众多新兴 AI 公司、模…

阿里云 RabbitMQ 可观测性最佳实践

阿里云 RabbitMQ 阿里云 RabbitMQ 是一款高性能、高可靠的消息中间件&#xff0c;支持多种消息协议和丰富的功能特性。它提供消息队列功能&#xff0c;能够实现应用间的消息解耦和异步通信&#xff0c;提升系统扩展性和稳定性。其支持多种消息持久化策略&#xff0c;确保消息不…

vue-router 导航式编程 参数的设置

主要是想记录一下this.$router.push、replace、go等方法的参数如何设置。字符串路径router.push(/home)直接使用字符串&#xff08;或模板字符串&#xff09;路径&#xff0c;可跳转到相应的URL路径。对象式路径路径也可以是一个对象&#xff0c;对象里以key:value的形式表示UR…

Swift实现股票图:从基础到高级

目录一、核心实现方案1. 原生方案&#xff1a;使用 Core Graphics 绘制2. 使用第三方库&#xff1a;Charts3. 跨平台方案&#xff1a;使用 SwiftUI Canvas二、技术指标实现1. 移动平均线 (MA)2. 布林带 (Bollinger Bands)3. MACD (Moving Average Convergence Divergence)三、…

【unitrix】 6.4 数特征(number.rs)

一、源码 这段代码定义了一个名为Number的trait&#xff08;特质&#xff09;以及它的实现。 use crate::sealed::Sealed; use crate::number::{V, BaseNumber, TNumber};/// 数值的统一标记特质 /// 可以是编译时类型化数字(TNumber)或运行时变量(V<T>) pub trait Numbe…