理解MySQL中的MVCC机制

第一个问题,什么是MVCC?

MVCC是一种版本控制,其实是一种虚拟的东西,它是一种模型,也可以说是一种算法。程序在高并发的情况下的线程安全问题是人们永远关注的问题,MySQL也不例外,MySQL为了提高并发量,并不建议使用悲观锁直接串行化来操作数据库,而是牺牲掉一部分的数据一致性来满足并发性。


在探讨MVCC之前,我们先复习一下MYSQL中的事务相关的信息

事务的四大特性,ACID这里不在赘述了,重点来复习一下其中的隔离性

事务的隔离性有四种级别,从低到高分别是

  1. 读未提交:A事务可以读取到B事务还没有commit的数据,最垃圾的隔离级别,近似于没有隔离,基本不使用,带来的问题是脏读,不可重复读,幻读
  2. 读已提交:A事务可以读取到B事务已经提交的数据。解决了脏读问题,随之而来的问题是不可重复读和幻读。
  3. 可重复读:A事务在开始和提交之前,数据库的状态保持一直,也就是说,A事务只要没提交,任何适合select查询数据库时的每条记录都不会变。其他事务对于某条数据库的改变对事务A时不可见的。解决了不可重复读,可是还有幻读问题。
  4. 串行化:最高的隔离级别,加了悲观锁,所有事务只能串行化,杜绝了一切的线程安全问题,代价是并发量太差,近乎于没有。

其中,脏读,不可重复读的概念大家估计已经背烂了,也就不在多说了。但是很多人对于幻读还是有不理解的地方,很多人会和不可重复读搞混。

他俩最根本的区别是:不可重复读指的是某一条数据的内容两次读取的结果不一样,而幻读指的是一个范围内的记录行数发生了变化。比如一开始读,1到100内符合条件的有20条数据,可是下一次读的时候变成了30条数据,莫名其妙的多了10条数据,像是产生了幻觉一样。


复习完了事务的隔离级别,下面我们来接触一下MVCC

MVCC指的就是对于每条数据(表中的每一行),会通过链表记录每一次数据的改变,换句话说,就是会记录这行数据以前的每个版本,每一个节点是一个时期的数据,头节点是当前最新的数据,尾节点是这个条记录初始的数据。通过这个链表可以轻松的找到老版本的数据,可以实现roll back事务回滚。这个链表就存在 undo log回滚日志中,这也是日志回滚的原理。

快照读和当前读

1.由于mvcc是支持多个事务并发的同时操作某条数据,因此读操作可能读到的并不是当前的最新数据,快照读,顾名思义,读的是一个快照,是当前时刻在这条记录上活跃的事务(操作这条数据,但是没有提交)。最最普通的select语句,就是典型的快照读

2.当前读就是读到的是最新的数据,这就需要我们加锁了,比如lock in share mode ,  for update ,update, insert, delete语句等,这种加锁的语句,他们可以避免并发修改,从而保证当前读的数据就是最新的数据.

什么是ReadView?

说白了 Read View 就是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID (当每个事务开启时,都会被分配一个 ID , 这个 ID 是递增的,所以最新的事务,ID 值越大)
 

假设现在创建ReadView的事务id为 create_tr_id。也就是执行select快照读的事务。

可能获得的ReadView中有很多事务,那么到底哪个事务的结果是我们想要的呢?当然我们会想,当然是越新的事务越好啦。遗憾的是,我们毕竟现在是站在上帝视角看的,可是我们站在当前事务的角度看呢,可能有些事务是在创建快照之后才开启的,那么我们当前的事务可以读到未来的数据吗?当然不可以,因此我们就要选择一个尽量新的数据并且符合规则。

现在的问题是,怎样选择出这个数据呢?

这就是牵扯到了有一个算法了。

在展示之前,我先简化一下 Read View,我们可以把 Read View 简单的理解成有三个全局属性

trx_list
  • 一个数值列表,也就是ReadView中主要的数据结构
  • 用于维护 Read View 生成时刻系统 正活跃的事务 ID 列表
min_trx_id
  • 是 trx_list 列表中事务 ID 最小的 ID
max_tr_id
  • ReadView 生成时刻系统尚未分配的下一个事务 ID ,也就是 目前已出现过的事务 ID 的最大值 + 1

从版本链的第一个节点(头节点,也就是最新的数据)开始,每个节点的事务Id属性进行一下比较,如果三条中有一条符合的,那么这个版本的数据就是我们所要的又新又符合规定的数据,如果没有,那么就看下一个节点,直到找到符合要求的节点。

 以上,就是MVCC版本链实现非阻塞式的高并发的原理了。

事情还没有结束,RC和RR的隔离级别是怎样实现的呢?

道理很简单,RC就是每次快照读都会获得一个新的ReadView,这样每次操作的就是相对于最新的数据,有可能当前事务执行的过程中被其他事务修改了数据,那么第二次的快照读就会产生一个新的readView,从而产生了不可重复读问题。

同理,RR的级别就是,当前事务内,每次的快照读操作都会得到一个相同的ReadView,从而保证了可重复读

你以为事情结束了吗

最后一个问题,MVCC可以解决RR级别下的幻读问题吗?欢迎大家把想法打在评论区!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值