MySql之深入理解MVCC

 🎬作者简介:大家好,我是小徐🥇
☁️博客首页:CSDN主页小徐的博客
🌄每日一句:好学而不勤非真好学者

📜 欢迎大家关注! ❤️

一、基本介绍

MVCC,是Multiversion Concurrency Control的缩写,翻译过来是多版本并发控制,和数据库锁一样,他也是一种并发控制的解决方案。

我们知道,在数据库中,对数据的操作主要有2种,分别是读和写,而在并发场景下,就可能出现以下三种情况:

  • 读-读并发
  • 读-写并发
  • 写-写并发

我们都知道,在没有写的情况下读-读并发是不会出现问题的,而写-写并发这种情况比较常用的就是通过加锁的方式实现。那么,读-写并发则可以通过MVCC的机制解决。

要想搞清楚MVCC的机制,最重要的一个概念那就是快照读。

所谓快照读,就是读取的是快照数据,即快照生成的那一刻的数据,像我们常用的普通的SELECT语句在不加锁情况下就是快照读。如:

SELECT * FROM xx_table WHERE...

和快照读相对应的另外一个概念叫做当前读,当前读就是读取最新数据,所以,加锁的SELECT,或者对数据进行增删改都会进行当前读,比如:

SELECT * FROM xx_table LOCK IN SHARE MODE;
SELECT * FROM xx_table FOR UPDATE;
INSERT INTO xx table ...
DELETE FROM xx_table ...
UPDATE xx table ...

可以说快照读是MVCC实现的基础,而当前读是悲观锁实现的基础。

那么,快照读读到的快照是从哪里读到的的呢?换句话说,快照是存在哪里的呢?

二、UndoLog

undo log是Mysql中比较重要的事务日志之一,顾名思义,undo log是一种用于回退的日志,在事务没提交之前,MySQL会先记录更新前的数据到 undo log日志文件里面,当事务回滚时或者数据库崩溃时,可以利用 undo log来进行回退。

这里面提到的存在undo log中的"更新前的数据"就是我们前面提到的快照。所以,这也是为什么很多人说UndoLog是MVCC实现的重要手段的原因。

那么,一条记录在同一时刻可能有多个事务在执行,那么,undo log会有一条记录的多个快照,那么在这一时刻发生SELECT要进行快照读的时候,要读哪个快照呢?

这就需要用到另外几个信息了。

三、行记录的隐式字段

其实,数据库中的每行记录中,除了保存了我们自己定义的一些字段以外,还有一些重要的隐式字段的:

  • db_row_id:隐藏主键,如果我们没有给这个表创建主键,那么会以这个字段来创建聚簇索引。
  • db_trx_id:对这条记录做了最新一次修改的事务的ID
  • db_roll_ptr:回滚指针,指向这条记录的上一个版本,其实他指向的就是Undo Log中的上一个版本的快照的地址。

因为每一次记录变更之前都会先存储一份快照到undo log中,那么这几个隐式字段也会跟着记录一起保存在undo log中,就这样,每一个快照中都有一个db trx id字段表示了对这个记录做了最新一次修改的事务的ID ,以及一个db roll ptr字段指向了上一个快照的地址。(db trx id和db roll ptr是重点,后面还会用到)

这样,就形成了一个快照链表:

有了undo log,又有了几个隐式字段,我们好像还是不知道具体应该读取哪个快照,那怎么办呢?

四、Read View

这时候就需要Read View 登场了

Read View 主要来帮我们解决可见性的问题的,即他会来告诉我们本次事务应该看到哪个快照,不应该看到哪个快照。

在 Read View 中有几个重要的属性,

  • trx_ids,系统当前未提交的事务 ID 的列表。
  • low_limit_id,应该分配给下一个事务的id 值。
  • up_limit_id,未提交的事务中最小的事务 ID。
  • creator_trx_id,创建这个 Read View 的事务 ID。

每开启一个事务,我们都会从数据库中获得一个事务 ID,这个事务 ID 是自增长的,通过ID 大小,我们就可以判断事务的时间顺序。

那么,一个事务应该看到哪些快照,不应该看到哪些快照该如何判断呢?

其实原则比较简单,那就是事务ID大的事务应该能看到事务ID小的事务的变更结果,反之则不能!举个例子!

假如当前有一个事务3想要对某条记录进行一次快照读的时候,他会先创建一个Read View并且把当前所有还未提交的事务的信息记录下来。比如up limit id=2,low limit id=5trx ids= [2,4,5],creator trx id= 3

我们前面说过,每一条记录上都有一个隐式字段db trx id记录对这条记录做了最新一次修改的事务的ID,如db trx id =3;

那么接下来,数据库会拿这条记录db trx id和Read View进行可见性比较。
如果db trx id<up limit id,则说明,在Read View中所有未提交的事务创建之前,db trx id = 3的这个事务就已经提交了,并且在这期间,并没有新的事务提交。所以,这条记录对当前事务就应该是可见的。

如果,db trx id>low limit id,则说明,db trx id = 3的这个事务是在Read View中所有未提交的事务创建之后才提交的,也就是说,在当前事务开启之后,有别的事务修改了数据并作了提交。所以,这个记录对于当前事务来说应该就是不可见的。(不可见怎么办呢?后面讲)

那么,还有另外一种情况,那就是up limit id < db trx id <low limit id,这种情况下,会再拿db trx id和Read View中的trx ids进行逐一比较。

如果,db trx id在trx ids列表中,那么表示在当前事务开启时,并未提交的某个事务在修改数据之后提交了,那么这个记录对于当前事务来说应该是不可见的。

如果,db trx id不在trx ids列表中,那么表示的是在当前事务开启之前,其他事务对数据进行修改并提交了,所以,这条记录对当前事务就应该是可见的。

所以,当读取一条记录的时候,经过以上判断,发现记录对当前事务可见,那么就直接返回就行了。那么如果不可见怎么办?没错,那就需要用到undo log了。

当数据的事务ID不符合Read View规则时候,那就需要从undo loq里面获取数据的历史快照,然后数据快照的事务ID再来和Read View进行可见性比较,如果找到一条快照,则返回找不到则返回空。

所以,总结一下,在InnoDB中,MVCC就是通过Read View+Undo Log来实现的,undolog中保存了历史快照,而Read View 用来判断具体哪一个快照是可见的。

五、MVCC和可重复读

其实,根据不同的事务隔离级别,Read View的获取时机是不同的,在RC下,一个事务中的每一次SELECT都会重新获取一次Read View,而在RR下,一个事务中只在第一次SELECT的时候会获取一次Read View。

所以,可重复读这种事务隔离级别之下,因为有MVCC机制,就可以解决不可重复读的问题因为他只有在第一次SELECT的时候才会获取一次Read View,天然不存在重复读的问题了。

  • 31
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Mysql的锁机制是用来处理并发访问数据库时的问题,特别是在使用InnoDB引擎支持事务的情况下。锁机制可以按照锁的粒度分为表级锁和行级锁。表级锁是对整张表进行加锁,实现简单,消耗的资源较少,加锁快速,不容易出现死锁。而行级锁则是对当前操作的行进行加锁,锁定粒度更小,可以提高并发性,但加锁的代价较高。 MySQL的InnoDB存储引擎默认的事务隔离级别是RR(可重复读),这是通过行级锁和多版本并发控制(MVCC)一起实现的。在正常读取数据时,不会加锁,而在写入数据时才会进行加锁操作。 MVCC是通过一些技术实现的,包括隐藏字段、Read View和Undo log。隐藏字段用于存储数据版本信息,Read View用于控制事务的隔离级别,而Undo log则用于记录事务对数据的修改操作,以便在需要回滚时进行恢复。 总结起来,Mysql的锁机制包括表级锁和行级锁,用于处理并发访问数据库时的问题。而MVCC则是InnoDB存储引擎实现事务隔离级别的一种机制,通过隐藏字段、Read View和Undo log来实现数据的一致性和并发控制。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Mysql锁机制+MVCC](https://blog.csdn.net/qq_45901741/article/details/120245265)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [MySQL之锁和事务](https://download.csdn.net/download/weixin_38739919/13683140)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [mysql的锁机制和mvcc](https://blog.csdn.net/u014618114/article/details/115534734)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小徐很努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值