从隔离级别和MVCC谈起

前言

最近闲暇时,复习了下MySQL的InnoDB的存储引擎方面的知识,说是复习倒不如说是真正的学习了一波,之前为了应付面试,这方面的八股文倒是背过,这几天看了MySQL45讲】(极客时间丁奇老师的经典)之后,才深切发觉这里面的知识值得好好思考一下,这几天对事务的隔离级别和MVCC有些新的理解,在此抛出一些在思考的问题,和大伙儿相互论证下。

ps :我这有45讲的压缩包,嘘,囊中羞涩的xdm有需要的评论区滴滴我,免费的,我如果看到了会发给你


说明

有必要说明下,在落笔之前,我在此前学习及思考的过程中,查看了很多相关的资料,发现有些博客不只有些片面,甚至有着明显的错误,给我带来很多困惑,在查看了MySQL官方文档(强烈建议去读读原版官方文档)之后,确实解开了一些谜团,但仍有很多存疑的地方,因此希望看到这篇文章的xdm是带着怀疑和思考的角度来读,而不是一个获取直接答案的心态,另外本文讨论InnoDB相关的部分只包括隔离级别和MVCC,其他的太多了,尽量休婚假回来之后再肝,水平有限,希望不会给大伙儿带来困扰,见谅。


事务的隔离级别

InnoDB的事务隔离级别隔离级别解决的问题,相信XDM都很熟悉,贴张图先给大伙儿回忆下。
在这里插入图片描述
首先第一个问题,为什么需要隔离级别,画个简陋的的图,方便理解
在这里插入图片描述

事务隔离级别(Transaction Isolation Levels):Isolation is the I in the acronym ACID; the isolation level is the setting that fine-tunes the balance between performance and reliability, consistency, and reproducibility of results when multiple transactions are making changes and performing queries at the same time

事务隔离级别是指ACID(有人戏称为‘’(acid))中的 I,隔离级别不是MySQL的InnoDB特有的,
InnoDB offers all four transaction isolation levels described by the SQL:1992 standard: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, and SERIALIZABLE. The default isolation level for InnoDB is REPEATABLE READ.
很多数据库的隔离级别的实现都基于此。
我理解隔离级别是对并发事务的多少和性能的一个trade-off(权衡),事务隔离设置的几个级别,随着级别的不断提高,并发性不断降低,性能的消耗越大,但并发产生的问题会越来越少,甚至没有,就是我们常说的脏读,不可重复读以及幻读

本次只讨论可重复读及幻读这两个事务隔离级别。


MVCC在这两个隔离级别中起到的作用

MVCC(多版本并发控制):InnoDB内部在对每一列增加了两个关键的列DB_TRX_IDDB_ROLL_PTR ,利用这两列,InnoDb得以实现了同一行不同的版本,对于第三列DB_ROW_ID ,大家都很熟悉这里就不谈了。
在这里插入图片描述
DB_TRX_ID :inserted or updated 一行后的最新的事务ID。
DB_ROLL_PTR :roll pointer(回滚指针),指向该行数据对应的Undo日志,Undo日志大家或多或少应该都有过了解,一般用于事务回滚,此处告诉了你另一个作用:利用Undo日志中的数据信息建立一个早期版本的数据库行。

It also uses the information to build earlier versions of a row for a consistent read. See Section 15.7.2.3, “Consistent Nonlocking Reads”.

InnoDB利用增加的这两列实现了数据的多版本。
这里有个关键的名词Consistent Nonlocking Reads,与之对应的是Locking Reads,大家可能更熟悉他们的另外的名字快照读当前读


Consistent Nonlocking Reads

一致性非锁定读即我们通常所说的‘快照读’(我在MySQL的官网实际上并没有找到‘snapshot read’ 这个名词,但很多博客都写的煞有介事,不知道是不是我学习的方法不对,还是说大家共同新造了这个名词)

Locking Reads

锁定读也就是我们说的 ‘当前读’


不可重复读的两面性

我们都知道可重复读隔离级别相较于读已提交解决了 不可重复读 的问题,
但是有很多业务的生产环境为了提高并发,会将生产的默认隔离级别改为读已提交
那么不可重复读的危害到底存不存在呢?
从我的理解来看,不可重复读肯定不是一个危害,具体业务具体分析。

例子一

假如不可重复读隔离级别下,
有个优惠券系统,同一个事务中,
计算小明的积分为500,兑换了500的优惠券,
然后处理了别的逻辑,这个时候业务逻辑不恰当,
假如小明在别的操作中积分增加成了1000,又给小明兑换了1000的优惠券,
同一个事务中前后的查询不一致,事务提交,
小明获得了1500的优惠券。
这个时候不可重复读对业务逻辑是有影响的,应当将隔离级别设置为可重复读。

例子二

小明银行卡有100元,
他想买一个100元的亚索皮肤,
在付款前,银行查询他的余额还有100,
这个时候小明女朋友,刷了这个卡50元买口红,
小明买皮肤时扣款成功100元(不考虑银行其他安全措施)
小明的余额变成了-50元。
这种业务场景,肯定需要每次都读到最新的数据,应当将隔离级别设置为读已提交。

什么叫幻读

先来看下官网上
Phantom Rows

翻译过来:幻像行,也就是我们通常所指的幻读

再来看下丁大45讲20章的定义
在这里插入图片描述

可重复读的隔离级别解决了幻读没有

首先很多博客上说可重复读的隔离级别并没有解决幻读,这句话倒是没有什么问题,前天我在一个技术交流圈也提出了这个问题,很多人表达了同一个观点,串行化的隔离级别才彻底解决了幻读,对于这句话我抱有疑惑。
之前在查资料时,看到《MySQL技术内幕-InnoDB存储引擎》这本书上在幻读这节上是这样说的。
在这里插入图片描述
另外官网上在介绍 Phantom Rows(幻读)时也是下相同的描述

To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking.

说下我理解的结论,有不同想法的xdm欢迎交流

  • RR隔离级别并没有解决幻读
  • RR隔离级别配合间隙锁解决了幻读
  • RR隔离级别配合锁相当于将隔离级别提升到了串行化读

结语

InnoDB相关其他的核心部分还需要深入了解的

  • 锁的分类
  • 索引的实现
  • 集群
  • 主从
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值