MySQL如何解决脏读、幻读、不可重复读?

InnoDB的四种隔离级别

RU——Read Uncommitted未提交读 会有Dirty Read脏读

RC——Read Committed提交读 这种级别存在不可重复读Nonrepeatable Read。指一个事务里相同select出的数据不一致

RR——Repeatable Read可重复读 解决了不可重复读的问题,没法解决幻读Phantom Read。幻读指用户读取某一范围的数据行,另一个事务又在该范围内插入了新行,或者删除了记录,当用户再读取该范围的数据行,会发现新的”幻影行“。InnoDB和Falcon存储引擎通过MVCC解决了该问题

Serializable可串行化 顺序执行

在这里插入图片描述

InnoDB是如何解决脏读、幻读、不可重复读的?

在InnoDB中,通过MVCC解决脏读和不可重复读,通过MVCC+间隙锁解决幻读

脏读 当事务在“读已提交“隔离级别下执行读取操作,InnoDB获取当前最新的全局事务ID,这个ID表示在当前时刻所有已经提交事务的最新状态。InnoDB会检查每个数据行的版本,如果该版本是由一个小于等于当前事务ID的事务修改的,并且该事务已提交,则这个版本是可见的。这保证了事务只能看到它开始之前已经提交的数据版本。

不可重读 InnoDB使用MVCC来解决不可重读的问题。在RR这种级别下,当我们使用快照读进行数据读取的时候,只会在第一次读取的时候生成一个Read View,后续所有的快照读都是用同一个快照,所以就不会发生不可重复读的问题了。

幻读 InnoDB的RR级别中,基于MVCC+间隙锁可以避免幻读,但无法完全避免,当一个事务中发生当前读的时候,会导致幻读的发生


关于ReadView

ReadView是实现MVCC的基础,也是支持不同事务隔离级别的基础,同时提高系统的并发能力和性能。一言蔽之,ReadView显示本次事务应该看到哪个快照,不应该看到哪个快照

快照

实现RR和RC的读数据,即RR要求在一个事务中,多次读取的结果保持一致,而RC要求每次都要读取到最新的值

那么,具体实现读数据不同现象,就要通过快照

在可重复读RR下,ReadView事务开始时创建一次,并在整个事务期间保持不变

在读已提交RC下,ReadView在每次查询时重新创建,以反映数据库中最新的提交更改

ReadView的几个比较重要的属性

  • trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
  • low_limit_id,应该分配给下一个事务的id 值。
  • up_limit_id,未提交的事务中最小的事务 ID。
  • creator_trx_id,创建这个 Read View 的事务 ID。

trx_id比低位小就说明事务在生成ReadView前就已经提交了,那么事务的结果就是可见的;

大于高位说明生成ReadView后才生成,事务结果就是不可见的;

在高低位中间时,

​ 如果事务ID在trx_ids列表中,表示在当前事务开启时,该事务活跃,这个记录对当前事务是不可见的

​ 如果事务ID不在trx_ids列表中,表示该事务开启之前,其他事务对数据进行修改并提交了,所以该记录对当前事务是可见的

​ 还有trxid和creatortrxid相等,那肯定也是可见的

总之,一个事务,能看到的是他开始之前就已经提交事务的结果,而未提交的结果都是不可见的

不可见的话,就用undolog

如何理解MVCC

MVCC,多版本并发事务控制

快照读和当前读

快照读 就是读取的是快照生成的那一刻数据,常用的select语句在不加锁的情况下就是快照读

当前读 就是读取最新数据,加锁的select 或者 常规增删改操作都会进行当前读

在MySQL中,只有RC和RR这两种事务隔离级别才会使用快照读

在RR中,快照会在事务中第一次select语句执行时生成,只有本事务中对数据进行更改才会更新快照

在RC中,每次读取都会重新生成一个新的快照,总是读取行的最新版本

快照读是MVCC实现的基础,当前读是悲观锁实现的基础


UndoLog

UndoLog是一种用于回退的日志,在事务没提交之前,MySQL会先记录更新前的数据到UndoLog日志文件里,当事务回滚或者数据库崩溃时,可以利用undolog回退

其中更新前的数据就是前面提到的快照。所以,这也是说Undolog是MVCC实现的重要手段的原因。

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

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

注意,以上字段,只有在聚簇索引的行记录中才会有,而在普通二级索引中是没有这些值的,至于二级索引和MVCC的支持,应届生一般很少被问到,简单点说,如果发现二级索引页有一个 PAGE_MAX_TRX_ID 太新的,或者如果二级索引中的记录被删除标记, InnoDB可能需要使用聚集索引来查找记录。

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

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

在这里插入图片描述

UndoLog,几个隐式字段,整合ReadView:
在这里插入图片描述

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


MVCC和可重复读

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

所以,在RR这种级别下,因为有MVCC机制,就可以解决不可重复读的问题,因为他只有在第一次select的时候才会获取ReadView,天然不存在重复读的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值