当前位置: 首页 > article >正文

2025年精通MVCC

今年找工作,无一例外又问到了MVCC这个知识点。几乎每次换工作都会被问到这个面试有用,工作毫无 * 用的知识。但是环境就是这样,既然如此,我们用一篇文章彻底搞懂MVCC

1.MVCC是什么

MVCC(Multi-Version Concurrency Control,多版本并发控制) 是数据库中常用的一种 并发控制机制,它通过为每个事务提供数据的多个版本,实现了 读写分离、读不加锁,大幅提升数据库并发性能。

也就是说,它是为了解决当出现多个线程访问数据库时出现的一些问题。那么首先先搞清楚有哪些问题,再说解决方案。

2解决了什么问题

2.1 用锁来实现事务的效率问题

如果没有MVCC,那么想实现事务只能加读锁和写锁了,首先写锁是肯定要加的,不能A操作一个数据时,B也能操作同一个数据吧。且A操作一个数据时,B也不能读,因为没有MVCC,你这时候去读就能读到A操作的中间态数据。就像五我才写了一横,你就直接读了,你以为我写的是一。既然事务是必须要实现的,不管你是用锁,还是MVCC。那么我们来思考如何不用锁,也能实现事务。

这里提到事务的隔离级别:

READ UNCOMITTED:读未提交

READ COMMITTED:读提交

REPEATABLE READ:可重复读

SERIALIZABLE:串行化

上文提到,如果连锁都不用,属于读未提交,能读到其他事务的中间态(事务未提交的数据),那也没什么隔离可说了。MySQL既然是事务型数据库,读未提交就不需要讨论(某些业务场景需要获取到未提交的数据状态时,可以将隔离级别设为读未提交,比如输出一些状态日志。)。

串行化就等于加读写锁,没有MVCC又要保证事务那就是这个级别,但是效率又太低。那么这时候MVCC来了。它通过判断trx_id的大小决定哪些数据对我可见,那么别人写的时候,我就可以通过这个判定依据决定我可以看见哪个版本下的数据了,我就不会读到脏数据,也就不需要加锁,也就实现了多版本并发控制。

2.2 脏读问题

首先要明确,读到别人未提交的数据,就是脏读,那自然可以知道READ COMMITTED:读提交,这个隔离级别是可以阻止脏读的。那如何实现读提交隔离级别呢?

那么MVCC来了。

当事务 T 读取数据时,对于某一行记录的版本 trx_id(事务ID):

  1. 如果 trx_id < 当前 Read View 中的最小活跃事务ID ⇒ 可见(早已提交)

  2. 如果 trx_id 是正在进行中的事务 ⇒ 不可见

  3. 如果 trx_id == 当前事务ID ⇒ 可见(自己写的可见)

  4. 否则,根据 undo log 找前一版本,继续判断。

说人话就是,当你去查询某一行数据时,这一行数据有一个历史记录列表(undo log实现,且列表每一行都有一个trx_id)。你去对比这个历史记录列表,现在活跃事务id中有10个是活跃的,事务id是自增的,你找到最小的活跃事务id,然后你能看见的数据就是历史记录列表中trx_id小于最小活跃事务id的记录数据。也就是你只能看见已经提交,不处于活跃状态的数据。

也就是说,数据库是通过对比trx_id,来控制判断哪些已操作完成的数据你可以看见,正在操作的数据你不可以看见。

那么就引出了最常见的面试核心内容:
 

InnoDB 如何实现 MVCC?

InnoDB 在行级存储事务控制的基础上,通过以下机制实现 MVCC:

 1. 每行记录隐含两个字段:

字段名含义
trx_id最后修改该行的事务ID
roll_pointer(或称roll_ptr指向旧版本数据的指针(Undo Log)

2. 使用 Undo Log(回滚日志)保存历史版本

  • 每次对数据的更新/删除操作都会记录旧版本到 Undo Log;

  • 读取操作会根据当前事务的 Read View,从 Undo Log 中找到合适的版本供事务“读取”。

3. Read View(读取快照)

  • 每个事务在执行 SELECT 时,会创建一个 Read View;

  • 这个视图记录了哪些事务“对我来说是不可见的”,以此决定读取哪个版本。

2.3 不可重复读

我们知道MVCC工作在:

READ COMMITTED:读提交

REPEATABLE READ:可重复读

两个级别下,READ COMMITTED是有不可重复读的问题的,同一个事务内,如果其他事务提交了数据,我是能感知到的。那么解决不可重复读问题,就是采用默认的REPEATABLE READ:可重复读隔离级别。就是采用同一个事务中,始终只访问第一次select时的Read View快照(读取快照)。

3 如何实现

那么记住核心的内容:

采用每一行记录trx_id来对比大小,判定可见区域。

采用roll_pointer来寻找上一条旧数据,链式寻找,也就是Undo Log一个最大的作用。

采用Read View读快照来保存历史数据。

最后,记住MVCC的实现,以及读提交,可重复读这两个隔离级别不能避免幻读问题。这个关于间隙锁,后面说。

http://www.lryc.cn/news/2400744.html

相关文章:

  • 硬路由与软路由
  • OpenCV C++ 心形雨动画
  • Fullstack 面试复习笔记:Java 基础语法 / 核心特性体系化总结
  • 安卓Compose实现鱼骨加载中效果
  • 使用qt 定义全局钩子 捕获系统的键盘事件
  • FreeType 字体信息检查工具 - 现代C++实现
  • el-table 树形数据,子行数据可以异步加载
  • 【使用JAVA调用deepseek】实现自能回复
  • 【Linux系列】rsync命令详解与实践
  • Windows系统工具:WinToolsPlus 之 SQL Server Suspect/质疑/置疑/可疑/单用户等 修复
  • C++——智能指针 unique_ptr
  • 【Python训练营打卡】day43 @浙大疏锦行
  • 1-【源码剖析】kafka核心概念
  • JavaScript中判断两个对象是否相同(所有属性的值是否都相同)
  • Flask 应用的生产环境部署指南
  • 思科设备网络实验
  • Oracle OCP与MySQL OCP认证如何选?
  • AWS之数据分析
  • C# Onnx 动漫人物头部检测
  • 【Ragflow】24.Ragflow-plus开发日志:增加分词逻辑,修复关键词检索失效问题
  • gin 常见中间件配置
  • 蚂蚁森林自动收能量助手:Ant_Forest_1_5_4_3绿色行动新选择
  • Zookeeper 集群部署与故障转移
  • Redis最佳实践——电商应用的性能监控与告警体系设计详解
  • 区域徘徊检测算法AI智能分析网关V4助力公共场所/工厂等多场景安全升级
  • 修复与升级suse linux
  • 电力高空作业安全检测(2)数据集构建
  • 嵌入式开发之STM32学习笔记day18
  • [论文阅读]PPT: Backdoor Attacks on Pre-trained Models via Poisoned Prompt Tuning
  • 一键 Ubuntu、Debian、Centos 换源(阿里源、腾讯源等)