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

浅谈Mysql的MVCC机制(RC与RR隔离级别)

MVCC(Multi-Version Concurrency Control)多版本并发控制

说这个我们先来了解一下Mysql的隔离级别,因为MVCC和Mysql的隔离级别是有关的。

Mysql默认的隔离级别是RR(可重复读)

其他的隔离级别是读未提交(RU)读已提交(RC)、可重复读(RR)、可串行化

读未提交我们知道会产生脏读,脏读也就是一个事务读到另外一个事务未提交的数据,造成数据不一致问题。

读已提交不会造成脏读,从名字来看就是已提交,就是解决读未提交产生的问题,它不会产生脏读,但是它会产生不可重复读,不可重复读,指的就是一个事务一直读取同一条记录,但是会被其他事务的更新操作影响到读取的结果,造成数据不一致。

举个例子:

这时候最后一条读出来的结果就是zhangsan了。

这就是不可重复读。

可重复读,从名称上来看就是可以重复select,也就是为了解决读已提交造成的不可重复读。

那么你们有没有想过,为什么读已提交不会造成读未提交产生的脏读,却会造成不可重复读呢?可重复读为什么不会造成读已提交产生的不可重复读呢?其实背后都是MVCC控制的。

MySQL中的行数据,除了我们肉眼能看到的字段之外,其实还包含了一些隐藏字段,它们在内部使用,默认情况下不会显示给用户。

字段含义
DB_ROW_ID隐含的自增ID(隐藏主键),用于唯一标识表中的每一行数据,如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
DB_TRX_ID该字段存储了当前行数据所属的事务ID。每个事务在数据库中都有一个唯一的事务ID。通过 DB_TRX_ID 字段,可以追踪行数据和事务的所属关系。
DB_ROLL_PTR该字段存储了回滚指针(Roll Pointer),它指向用于回滚事务的Undo日志记录。

Read View

一致性视图,会用一个列表存放当前活跃的事务ID,也就是未提交的事务,当我们事务进行select 读操作的时候,就会生成这么一个ReadView视图,ReadView就会判断undo log版本链中的哪个版本对当前事务是可见的

这里提到了Undo log那我们就看看这个是什么东西,就是为了进行版本回滚的。

维护了一条版本链,链上都是每一行记录,包括刚刚提到的trx_id事务id,row_id,roll_pointer

当我们对数据进行更新的时候,会先将更新前原记录进行拷贝,然后undo Log日志就会开始存放,每次更新的时候都是这样,至此就会形成一条版本链。

那么ReadView是怎么判断哪些事务ID是可见的呢?

从Undo Log 版本链中的第一条记录开始往前找,获取当前版本的trx_id 事务id,只要事务ID在活跃的事务列表里面,那肯定是不可见的,如果比最小事务ID小,也是不可见,比最大事务ID还大,那也是不可见,也就是说只有在最小事务ID和最大事务ID之间才是可见的。

我们可以简单的举一个例子:

T4 时刻事务C查询的结果是什么呢?

我们看现在活跃的事务id有100,

最小事务ID是100,

最大事务ID是400,

那么从Undo Log版本链中找

第一条事务ID是100,100在活跃的事务列表里面,不可见。(所以这就验证了为什么RC级别下不会造成读未提交产生的脏读,未提交的在列表里面,直接不可见了)

继续往前找,还是100,还是不可见,

继续往前找,1,1不在事务ID列表里面,继续判断1是不是等于当前生成ReadView视图的事务ID300?不等于,1是否大于最大事务ID100?不大于。继续判断1是否小于最小事务ID100,1<100可见,所以RC级别下此时select查询出来的结果就是小明。RR也是小明。

我们来看一下T6时刻,如果是RC级别下,此时Undo Log的版本链是长这样的:

此时活跃的事务ID列表有:200,

最小事务ID是200,

最大事务ID是400,

创建ReadView视图的当前事务ID是300

从第一个版本看,200,在活跃的事务ID列表里面,不可见,

继续向前找200,还是在活跃的事务ID列表里面,不可见,

继续向前找,100,100 不在活跃的事务ID列表里面,100也不等于创建当前ReadView的事务ID300,100比最小事务ID还小,可以访问,所以RC级别下读到的结果就是小红,那么如果是RR级别下读到的是什么呢?还是小明,因为RR级别的ReadView视图会一直延用第一次相同Select创建的ReadView,这也就是为什么RR级别不会造成不可重复读,而RC会造成不可重复读的原因,RC每次Select创建的ReadView视图都是新创建的,而RR都是延用第一次Select产生的ReadView视图。所以也就验证了前面的那个疑问:为什么读已提交不会造成读未提交产生的脏读,却会造成不可重复读呢?可重复读为什么不会造成读已提交产生的不可重复读呢?

总结:

        RC级别下,select的时候ReadView视图每次都是会创建新的,所以这就是会造成不可重复读的原因;ReadView在判断版本链中哪个版本对当前事务可见的时候,如果是未提交的事务ID,是直接不可见的,这就是为什么不会造成脏读的原因。

        RR级别下,Select的时候ReadView视图都是延用第一次Select创建的ReadView视图,而不会产生新的ReadView视图,这也就是为什么RR级别不会造成不可重复读的原因。

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

相关文章:

  • uniapp-商城-72-shop(5-商品列表,购物车实现回顾)
  • 【git】 pull + rebase 或 pull + merge什么区别?
  • 1. 编程语言进化史与JavaScript
  • Vue3 中 Axios 深度整合指南:从基础到高级实践引言
  • MySQL#Select语句执行过程
  • hbuilder中h5转为小程序提交发布审核
  • 文档注释:删还是不删
  • 【数据结构】单链表练习
  • JVM 性能优化终极指南:全版本兼容、参数公式与场景实战
  • 分布式爬虫监控架构设计
  • MySQL的参数 innodb_force_recovery 详解
  • 学习vue3:跨组件通信(provide+inject)
  • Alibaba Sentinel 入门教程:从理论到实战
  • 2.3 TypeScript 非空断言操作符(后缀 !)详解
  • 【菜狗work前端】小程序加if判断时不及时刷新 vs Web
  • 01 NLP的发展历程和挑战
  • TCP 三次握手:详解与原理
  • LabVIEW累加器标签通道
  • 在 Unity 中,Start 方法直接设置 RectTransform 的位置,时出现问题,与预计位置不匹配。
  • 永磁同步电机控制算法--IP调节器
  • Ubuntu 25.04 锁屏不能远程连接的解决方案
  • Java 自动装箱和拆箱还有包装类的缓存问题
  • java-jdk8新特性Stream流
  • 大语言模型 21 - MCP 自动操作 Figma+Cursor 实现将原型转换为代码
  • QNAP NEXTCLOUD 域名访问
  • Spring MVC深度解析:控制器与视图解析及RESTful API设计最佳实践
  • 华为OD机试真题——信道分配(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
  • 比亚迪“双剑”电池获中汽中心权威认证,堪称“移动安全堡垒”。
  • 【mysql】mysql的高级函数、高级用法
  • 了解一下C#的SortedSet