一文搞懂MVCC

网上MVCC资料太多太乱,总结一下自己学习到的MVCC,没有废话,文章主打一个言简意赅

了解MVCC之前先了解几个前置知识

1 事务

1.1 事务介绍

事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

面试回答:事务可以使「一组操作」要么全部执行成功,要么全部执行失败。目的就是保证数据的最终一致性。

1.2 事务特性(ACID)

  • 原子性(A): 当前事务的操作要么同时成功,要么同时失败。

原子性在是通过undo log日志保证的,undo log记录了事务提交前的数据,一旦事务发生异常,就会回滚到事务提交前的状态。

  • 持久性(D): 一旦提交了事务,它对数据库的改变就应该是永久性的。

说白了就是,会将数据持久化在硬盘上。它主要通过redo log日志保证,一旦事务提交成功就会写入到redo log日志文件,顺序写,速度快。

  • 一致性(C): 一致性确保事务将数据库从一个一致性状态转移到另一个一致性状态。

个人觉得一致性对于刚接触的小白来说并不是很好理解,这里讲一下个人的见解。

我觉得一致性无非就是事务前后数据从一个正常的状态更新另外一个正常的状态,比如我现在更新一个字段age 1->5,那么事务提交前age = 1就是一个正常的状态,事务提交完成后age = 5就是一个正常的状态。那还有一种情况是事务发生异常回滚,那事务回滚后age = 1就是一个正常的状态而不是 age = 5。

  • 隔离性(I): 在事务「并发」执行时,他们内部的操作不能互相干扰。

隔离级别包括读未提交、读已提交、可重复读、可串行化。隔离级别越高隔离性越高,性能越低。
如果不做隔离会出现脏读、不可重复读、幻读等问题。

2 MVCC

2.1 MVCC介绍

MySQL InnoDB依靠MVCC实现事务隔离级别。MVCC其实就是一种并发控制的方法。

MVCC关键知识点:事务版本号、隐式字段、undo log、版本链、快照读和当前读、read view、

2.2 事务版本号

事务开启前都会从数据库中获取一个自增的ID来判断事务执行的先后顺序,这个id就是事务版本号。

注意的是只有更新数据的时候才会真正分配版本号

2.3 隐式字段

如果表中没有id又没有非null唯一值的时候才会出现row_id

2.4 undo log

undo log是回滚日志,记录了事务开始的时候的数据状态,这里不具体讲。

在MVCC中主要两个作用:

  1. 事务回滚。
  2. 用于MVCC快照读。

2.5 版本链

通过回滚指针(roll_pointer)连成的一个链表

2.6 当前读和快照读

  • 当前读:其实就是读取最新的数据,当前读会对数据加锁避免其他事务修改数据。
  • 快照读:读取的是可见版本的数据

2.7 Read View

Read View是事务开始的产生的一个可见视图,作用就是控制事务只能读取到指定版本的数据。

  • trx_ids(min_ids):当前系统(事务开始时)活跃事务id的集合。比如当前系统有A,B,C还在执行中,min_ids就是这三个事务id的集合。
  • up_limit_id(min_limit_id):当前系统活跃的事务的最小事务id,也就是m_ids中最小的。
  • low_limit_id(max_limit_id):表示系统中下一个事务的id。比如我现在min_ids = [2,5,6],还有[7,8]是已经提交完成的事务,那max_limit_id = 9,而不是min_ids集合中最大值+1,这点需要搞清楚。
  • creator_trx_id:创建当前read view的事务id。

搞清楚这些我们再来看看read view是怎么实现可见性的。 我们先定义一个变量事务id,来根据id范围讨论可见性(数据库分配事务id是自增的)

  • id < up_limit_id:这些都是已经提交完成的事务|创建这个视图的事务,那当前事务肯定对这些事务都是可见的。
  • id >= low_limit_id:这些事务都是至少可以确定在当前事务创建之后,当前事务创建的时候这些事务肯定都是还没提交的。那对这些事务肯定是不可见的。
  • up_limit_id <= id < low_limit_id: 这里要分两点讨论
  1. 如果id在trx_ids中,说明当前事务创建的时候这些事务还没提交,不可见
  2. 如果id不在trx_ids中,说吗这些事务已经提交了,可见

这样我们就分析完所有的id了,其实简单点讲,无非是一个事务开始的时候要判断哪些事务是已经提交的,那些事务是未开始或者未提交的。事务只对该事务开始的时候一些已提交完成的事务修改可见。

MVCC小总结

其实MVCC并不复杂,无非就是事务开始时候创建一个read view视图,然后事务中进行快照读的时候根据trx_id和read view判断可见性,如果不可见就通过隐藏字段里面的roll_pointer查看上一个版本,找到可见的版本后通过undo log日志可以拿到具体数据。当前读就加锁,这样别的事务改不了就保证可重复读问题, 如果是更新操作那就更新trx_id,和roll_pointer,并且写到undo log中去。

2.3 脏读、不可重复读、幻读

基于这三个问题,探讨一下MVCC是如何解决的。

  1. 脏读:事务A修改了age = 10。事务B读取到age = 10,这时事务A发生回滚,那么B读到的数据就是脏数据,这就是脏读。

在MVCC控制下,事务读一般是快照读,对于未提交的事务的数据变更是不可见的,比如事务B在读取age的时候读取到的并不是10,因为age = 10这个版本对事务B是不可见的,他会从最新的版本通过undo log日志不断找上一个版本,直到找到可见的版本为止。

  1. 不可重复读:事务A读取age的值,事务B修改了age = 11,事务A再次读取age值,导致事务内两次读取操作值不一致。

快照读通过MVCC的read view控制数据的版本可见性,解决不可重读读问题。比如事务A查看一个数据,只能查看到事务A启动前已经提交的事务,后续事务对这个数据做修改对于事务A上不可见的,所以事务A读取到的这个值都是一致的。

当前读是通过加锁解决的不可重复读,当前读对数据加锁,其他事务不可对数据做修改。

  1. 幻读:事务A查询student表,有20条,事务B插入了3条学生记录,事务A再次查询student表发现是23条记录,这就叫幻读。

mvcc可以通过read view解决快照读场景下的幻读,当前读场景下是通过间隙锁来解决的。

相关推荐
假装我不帅32 分钟前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc
神仙别闹35 分钟前
基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)
后端·asp.net
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
货拉拉技术1 小时前
货拉拉-实时对账系统(算盘平台)
后端
掘金酱2 小时前
✍【瓜分额外奖金】11月金石计划附加挑战赛-活动命题发布
人工智能·后端
代码之光_19802 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi2 小时前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
颜淡慕潇3 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
尘浮生4 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
尚学教辅学习资料4 小时前
基于SpringBoot的医药管理系统+LW示例参考
java·spring boot·后端·java毕业设计·医药管理