MVCC(Multi-Version Concurrency Control,多版本并发控制)。是一个在数据库管理系统中用于处理并发控制的核心技术。理解它对于深入掌握数据库(尤其是 InnoDB、PostgreSQL 等)的工作原理至关重要。

1. 什么是 MVCC?

MVCC 的全称是 多版本并发控制

核心思想:在数据库中,同一份数据可以保留多个历史版本。当事务需要读取数据时,它会根据一定的规则(比如事务的开始时间)看到一个特定的、一致性的“快照”(Snapshot),而不是直接读取最新的、可能还未提交的数据。写操作则会创建一个新版本的数据。

你可以把它想象成一个高效的版本控制系统(如 Git):

  • 读操作:就像 git checkout 到某个特定的 commit 版本,你看到的是那个时间点的完整项目状态,即使之后有新的提交,你的视图也不会变。
  • 写操作:就像创建一个新的 commit,它不会覆盖旧的 commit,而是在旧版本的基础上生成一个新版本。

通过这种方式,读操作和写操作可以不再互相阻塞,从而极大地提高了数据库的并发性能。


2. 为什么需要 MVCC?

在传统的数据库并发控制中,主要使用两种机制:

  1. 锁机制

    • 读-写冲突:当一个事务在读取一行数据时,会给它加上共享锁(S锁)。另一个事务如果想修改这行数据,需要加排他锁(X锁),但 S 锁和 X 锁互斥,所以写事务必须等待读事务完成。反之亦然,写事务会阻塞读事务。
    • 问题:并发度低。读和写操作串行化,性能很差。
  2. 基于时间戳的排序

    • 所有操作按时间戳排序执行,如果操作冲突,则回滚其中一个事务。
    • 问题:事务冲突率高,回滚频繁,性能同样不理想。

MVCC 的出现就是为了解决这些问题,它提供了一种“乐观”的并发控制方式:

  • 读写不冲突:读数据(快照读)不会阻塞写数据,写数据也不会阻塞读数据。这是 MVCC 最大的优势。
  • 非锁定读:大多数情况下,普通的 SELECT 查询不需要加锁,避免了锁的开销和死锁的风险。
  • 实现事务隔离:MVCC 是实现数据库事务隔离级别(特别是 READ COMMITTED 和 REPEATABLE READ)的基础。

3. MVCC 是如何工作的?

MVCC 的实现依赖于三个关键组件:隐藏列Undo Log 和 Read View。我们以最经典的 MySQL InnoDB 存储引擎为例来讲解。

a. 隐藏列

InnoDB 会为每一行数据额外添加三个隐藏的字段:

  • DB_TRX_ID (6字节): 最后修改该行的事务ID。记录了最后一次对这行记录进行 INSERT 或 UPDATE 的事务ID。每次事务修改一行,这个字段都会被更新。
  • DB_ROLL_PTR (7字节): 回滚指针。它指向该行上一个版本的数据在 Undo Log 中的位置。通过这个指针,可以形成一个“版本链”,把一个数据行的所有历史版本串联起来。
  • DB_ROW_ID (6字节): 隐藏的行ID。一个单调递增的ID,当表没有显式主键时,InnoDB会用它来生成一个聚集索引。

版本链示例
假设一行数据被事务 10、事务 20 依次修改。

  1. 初始状态:事务 10 插入一行数据。
    • DB_TRX_ID = 10
    • DB_ROLL_PTR = null (因为是第一个版本)
  2. 事务 20 修改:事务 20 更新了这行数据。
    • InnoDB 不会直接覆盖旧数据,而是:
      1. 将旧版本的数据(DB_TRX_ID=10 的版本)复制到 Undo Log 中。
      2. 在原位置创建一个新版本的数据行。
      3. 更新新版本的字段:DB_TRX_ID = 20
      4. 更新新版本的 DB_ROLL_PTR,让它指向 Undo Log 中旧版本的位置。
    • 现在,通过新版本的 DB_ROLL_PTR,我们可以找到旧版本,形成一条 版本链最新版本(20) -> 旧版本(10)
b. Undo Log

Undo Log 主要有两个作用:

  1. 事务回滚:当一个事务需要回滚时,可以利用 Undo Log 中记录的旧版本数据,将数据恢复到修改之前的状态。
  2. 构建版本链:如上所述,它存储了数据行的历史版本,是 MVCC 实现多版本的关键。当需要读取某个历史版本时,就可以从这里获取。
c. Read View(读视图)

Read View 是事务在执行快照读(普通的 SELECT)时,动态生成的一个“可见性判断”标准。它决定了当前事务能看到版本链上的哪个版本。

Read View 主要包含以下几个重要属性:

  • creator_trx_id: 创建该 Read View 的事务的 ID。
  • trx_ids: 创建 Read View 时,当前系统中所有活跃的(未提交的)读写事务的 ID 列表。
  • up_limit_idtrx_ids 列表中事务 ID 的最小值。如果版本链上某个版本的 DB_TRX_ID 小于 up_limit_id,则表示这个版本在创建 Read View 之前已经提交,所以对当前事务是可见的
  • low_limit_id: 创建 Read View 时,系统应该分配给下一个事务的 ID。如果版本链上某个版本的 DB_TRX_ID 大于或等于 low_limit_id,则表示这个版本是在创建 Read View 之后才开启的事务中修改的,所以对当前事务是不可见的

4. MVCC 如何解决并发问题?

现在,我们把这三个组件结合起来,看看一个 SELECT 语句是如何利用 MVCC 找到它应该看到的数据版本的。我们以 InnoDB 的 REPEATABLE READ(可重复读)隔离级别为例。

核心判断流程
当一个事务(假设 ID 为 T1)执行 SELECT 时,它会获取一个 Read View。然后,它会从版本链的最新版本开始,逐个版本地应用以下规则,直到找到一个可见的版本:

  1. 检查 DB_TRX_ID 是否是自己创建的?

    • 如果 DB_TRX_ID == creator_trx_id,说明这行数据是本事务自己修改的,可见
  2. 检查 DB_TRX_ID 是否小于 up_limit_id

    • 如果 DB_TRX_ID < up_limit_id,说明修改这个版本的事务在当前事务开始前就已经提交了,可见
  3. 检查 DB_TRX_ID 是否大于或等于 low_limit_id

    • 如果 DB_TRX_ID >= low_limit_id,说明修改这个版本的事务是在当前事务开始之后才启动的,不可见。需要根据 DB_ROLL_PTR 去 Undo Log 中查找上一个版本,然后重复整个判断流程。
  4. 检查 DB_TRX_ID 是否在 trx_ids 列表中?

    • 如果 up_limit_id <= DB_TRX_ID < low_limit_id,则需要判断 DB_TRX_ID 是否在活跃事务列表 trx_ids 中。
    • 如果在:说明修改这个版本的事务在当前事务创建 Read View 时还未提交,不可见。需要去 Undo Log 中找上一个版本。
    • 如果不在:说明修改这个版本的事务在当前事务创建 Read View 时已经提交了,可见

最终:如果遍历完整个版本链都找不到可见的版本,说明这行数据对当前事务是不可见的(比如被其他事务删除了)。

REPEATABLE READ vs READ COMMITTED 的关键区别
  • REPEATABLE READ (可重复读)

    • 事务中第一次执行 SELECT 时,会创建一个 Read View,之后该事务内的所有 SELECT 都复用这个 Read View
    • 效果:确保了在同一个事务中,多次读取同一数据的结果是一致的,因为判断可见性的标准(Read View)从未改变。这就是“可重复读”的由来。
  • READ COMMITTED (读已提交)

    • 事务中每次执行 SELECT 时,都会重新创建一个新的 Read View
    • 效果:每次读取都能看到其他已提交事务所做的最新修改。因为每次的 Read View 都是最新的,up_limit_id 和 trx_ids 都会更新,所以之前不可见的版本可能就变得可见了。

5. MVCC 的优缺点

优点
  1. 高并发性:读写操作不阻塞,极大地提高了数据库的并发读写性能。
  2. 非锁定读:避免了读操作加锁带来的开销和死锁风险。
  3. 实现一致性读:为不同隔离级别提供了基础,保证了事务的隔离性。
缺点
  1. 存储空间开销:需要维护多个版本的数据,Undo Log 会占用额外的存储空间。对于长事务或更新频繁的表,Undo Log 可能会变得非常大。
  2. 管理开销:需要额外的逻辑来管理版本链、创建和判断 Read View,增加了数据库的复杂性。
  3. 行版本清理:需要后台线程(如 InnoDB 的 Purge 线程)定期清理已经不再需要的旧版本数据(即没有事务再需要访问它们),否则 Undo Log 会无限增长。这个清理过程本身也消耗资源。
  4. 并非万能:MVCC 主要解决的是 SELECT 的并发问题。对于 UPDATEDELETE 之间的冲突,仍然需要使用(比如行锁、间隙锁、Next-Key Locks)来保证数据的一致性和防止幻读。

6. MVCC 与隔离级别的关系

隔离级别MVCC 如何工作能解决的问题
READ UNCOMMITTED (读未提交)基本不使用 MVCC。直接读取最新的数据,即使它未提交。
READ COMMITTED (读已提交)每次 SELECT 都创建新的 Read View。只能读到已提交的数据。解决脏读
REPEATABLE READ (可重复读)事务中第一次 SELECT 创建 Read View,后续复用。保证同一事务内多次读取结果一致。解决脏读、不可重复读
(在 InnoDB 中,结合 Next-Key Locks 还能解决幻读)
SERIALIZABLE (可串行化)基本不使用 MVCC 的快照读。所有 SELECT 语句都会隐式地转换为 SELECT ... LOCK IN SHARE MODE,即加共享锁。读写操作都互相阻塞。解决所有并发问题(脏读、不可重复读、幻读)

7. 总结

MVCC 是一种优雅而强大的并发控制技术,其精髓在于“用空间换时间,用版本换锁”。

  • 核心:通过为数据维护多个版本,让读操作访问历史快照,写操作创建新版本。
  • 关键组件:隐藏列(DB_TRX_IDDB_ROLL_PTR)构建版本链,Undo Log 存储历史版本,Read View 定义可见性规则。
  • 目的:实现读写不阻塞,提高并发性能,并作为实现数据库事务隔离级别的基础。
  • 应用:广泛应用于现代主流数据库,如 MySQL InnoDBPostgreSQLOracle 等。

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

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

相关文章

嵌入式第三十七天(TCP补充,应用层协议(HTTP))

一.TCP机制二.HTTP协议1.2.3.4.5.6.7.8.#ifndef _HEAD_H #define _HEAD_H#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#endif#include "head.h"…

Elasticsearch核心配置详解与优化

Elasticsearch 的核心配置文件主要用于控制节点行为、集群设置、资源分配和日志记录等关键功能。主要配置文件通常位于 ES_HOME/config 目录下&#xff0c;以下是三个最核心的配置文件及其详细说明&#xff1a; 1. elasticsearch.yml 核心集群与节点配置 这是最重要的配置文件…

机器学习框架下:金价近3400关口波动,AI量化模型对PCE数据的动态监测与趋势预测

摘要&#xff1a;本文通过AI多因子模型&#xff0c;结合宏观经济数据、政策动态及市场情绪因子&#xff0c;分析黄金价格波动机制及关键驱动要素。基于量化策略与自然语言处理技术&#xff0c;对美联储独立性争议、美债收益率曲线形态及PCE通胀数据等核心变量进行动态建模&…

【Redis#8】Redis 数据结构 -- Zset 类型

一、引言 定义&#xff1a;有序集合&#xff08;Zset&#xff09;是Redis中的一种数据结构&#xff0c;它结合了哈希表和跳跃列表的特性。每个 member 都有一个分数(score)&#xff0c;根据这个分数进行排序。 特点&#xff1a; member 不能重复&#xff0c;但分数可以相同&…

Postman 模拟mcp tool调用过程

文章目录 初始化调用 mcp server使用modelcontextprotocol 的java sdk编写 初始化 1.网页访问http://localhost:8090/sse,此页面保持开启,会不断接收到sse事件. 会返回一个endpoint,例如/mcp/message?sessionId111 2.初始化请求,postman发送post请求 url:http://localhost:…

init.usb.configfs.rc的USB动态配置

1. 什么是ConfigFSConfigFS 是 Linux 内核提供的一种用户空间可配置的伪文件系统在Linux内核中一个设备&#xff08;如手机&#xff09;作为USB从设备时&#xff0c;成为一个“Gadget”。路径&#xff1a;/config/usb_gadget/&#xff0c;g1表示系统重第一个USB Gadget的配置实…

广东省省考备考(第八十九天8.28)——判断推理(听课后强化训练)

判断推理&#xff1a;定义判断 错题解析 第一步&#xff1a;找出定义关键词。 “为了明确所承运的货物是否发生了残损&#xff0c;以及残损责任是否属于船方”。 第二步&#xff1a;逐一分析选项。 A项&#xff1a;甲船向商检机构申请检查船舶卸货前舱口、风筒的封盖和封识情况…

【C++】C++11的右值引用和移动语义

各位大佬好&#xff0c;我是落羽&#xff01;一个坚持学习进步的学生。 如果您觉得我的文章还不错&#xff0c;欢迎多多互三分享交流&#xff0c;一起学习进步&#xff01; 也欢迎关注我的blog主页: 落羽的落羽 文章目录一、C11简介二、左值和右值是什么三、左值引用与右值引…

Logic Error: 如何识别和修复逻辑错误

逻辑错误是指程序中的代码在语法上是正确的&#xff0c;但在执行时没有按预期工作。这种错误可能导致程序输出错误的结果或行为异常。逻辑错误通常比语法错误更难检测&#xff0c;因为它们不会产生编译或解释错误。本文将详细介绍如何识别和修复逻辑错误。一、识别逻辑错误1. 理…

TUN模式端口冲突 启动失败如何解决?

从日志信息来看&#xff0c;TUN模式启动失败是由于端口冲突导致的。 具体来说&#xff0c;Xray在尝试监听10808端口时失败了&#xff0c;因为该端口已经被其他进程占用。 错误信息分析 Failed to start: app/proxyman/inbound: failed to listen TCP on 10808 > transport/i…

如何调试一个EVM合约:实战操作 + 常见报错说明

在Solidity开发过程中&#xff0c;大多数开发者最常遇到的问题不是“代码写不了”&#xff0c;而是“代码部署了&#xff0c;但行为不对”。本篇文章将带你梳理一套完整的EVM智能合约调试流程&#xff0c;并附上几类真实常见报错场景及排查方法&#xff0c;适用于Hardhat、Remi…

使用Wireshark分析自助终端机网络数据

如果是明文还好&#xff0c; 是密文就没办法了。工具.1自助终端机.2组装结构主流架构选择‌B/S架构‌&#xff1a;通过Web应用调用本地硬件插件&#xff0c;开发速度快但依赖浏览器兼容性。 ‌‌C/S架构‌&#xff1a;直接调用硬件驱动&#xff0c;交互响应快但更新维护复杂。 …

数学建模——马尔科夫链(Markov Chain Model)

数学建模——马尔科夫链&#xff08;Markov Chain Model&#xff09;一、马尔可夫链的定义1. 状态与状态空间2. 无后效性&#xff08;马尔科夫性&#xff09;​3. 转移概率与转移概率矩阵&#xff08;1&#xff09;一步转移概率&#xff08;2&#xff09;转移概率矩阵二、马尔科…

《拉康精神分析学中的欲望辩证法:能指的拓扑学与主体的解构性重构》

在当代人文思想图谱中&#xff0c;雅克拉康以语言学为利刃对弗洛伊德理论进行的结构性重铸构成了20世纪最具颠覆性的理论创造之一。这位被誉为"法国弗洛伊德"的思想巨匠通过"回到弗洛伊德"的口号&#xff0c;实则完成了对精神分析学的哥白尼式革命——将主…

数字时代下的智能信息传播引擎

在商场、楼宇、交通枢纽等公共场所&#xff0c;数字广告机已成为信息传播的重要载体。其背后的广告机系统&#xff0c;是一套集硬件控制、内容管理、网络传输与数据分析于一体的综合技术解决方案&#xff0c;正推动传统静态广告向动态化、交互化、智能化方向演进。系统架构与核…

文献阅读笔记:KalmanNet-融合神经网络和卡尔曼滤波的部分已知动力学状态估计

文献阅读笔记&#xff1a;KalmanNet-融合神经网络和卡尔曼滤波的部分已知动力学状态估计摘要一、研究背景1.1 状态估计问题的重要性1.2 传统方法的局限&#xff1a;非线性与模型不确定性非线性问题噪声统计未知问题1.3 数据驱动方法的兴起与局限1.4 KalmanNet&#xff1a;混合方…

使用EasyExcel根据模板导出文件

文章目录概要工具类核心功能核心代码解析模板导出核心方法文件下载处理HTTP响应设置文件下载处理使用示例概要 在企业级应用开发中&#xff0c;Excel数据导出是一个常见的需求。本文实现一个基于阿里巴巴EasyExcel库实现的根据模板导出文件的工具类&#xff0c;它通过预定义的…

【AI基础:神经网络】19、机器学习实战:径向基函数神经网络(RBFN)指南

一、引言:为什么RBFN是神经网络中的“局部专家”? 在机器学习领域,神经网络的“全局逼近”与“局部逼近”一直是两大核心思路。像我们熟悉的多层感知机(MLP),使用Sigmoid、ReLU等全局激活函数,每个神经元都会对整个输入空间产生响应——就像“全员参与”处理所有数据,…

Linux 性能调优实战:CPU、磁盘 I/O、网络与内核参数

前言 一、CPU 资源调优 1. 调整进程优先级&#xff08;nice/renice&#xff09; 2. 设置 CPU 亲和力&#xff08;taskset&#xff09; 3. 查看 CPU 信息 4. 使用 vmstat 分析系统瓶颈 二、磁盘 I/O 调优 1. ulimit 资源限制 2. 磁盘速度测试 三、内核参数调优 1. 常用…

【进阶篇第五弹】《详解存储过程》从0掌握MySQL中的存储过程以及存储函数

文章目录存储过程一、基本语法(1)创建存储过程(2)调用存储过程(3)查看存储过程(4)删除存储过程(5)设置结束符(6)参数二、变量(1)系统变量(2)用户自定义变量(3)局部变量三、基本语句(1)if判断(2)case(3)while循环(4)repeat(5)loop循环四、游标五、条件处理程序六、存储函数存储过…