MySQL~MVCC多版本并发控制机制实现原理(read view、undo log、实现RC和RR)(1)

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 所谓多版本就是数据库表中每一行数据都可能存在多个版本,对数据库的任何修改的提交都不会直接覆盖之前的数据,而是产生一个新的版本与老版本共存,通过读写数据时读不同的版本来避免加锁阻塞。不同的存储引擎实现的MVCC多少有些差异,这里主要讨论下Mysql的默认存储引擎InnoDB对MVCC的实现原理。MVCC的实现主要依赖于数据库在每个表中添加的三个隐藏字段以及事务在查询时创建的快照(read view)和数据库的撤销日志(Undo log)。

  • MVCC的重要特性:

(1)MVCC只支持RC(读取已提交)和RR(可重复读)隔离级别。

(2)MVCC能帮助锁机制解决脏读、不可重复读问题,不能解决丢失更新问题和幻读问题。

(3)MVCC是用来解决读写操作之间的阻塞问题。使得在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。

如何多版本

  • InnoDB会为每个使用InnoDB存储引擎的表添加三个隐藏字段,用于实现数据多版本和无主键前提下的聚集索引,他们的作用如下:
  1. DB_TRX_ID(6字节): 它是最近一次更新或者插入或者删除该行数据的事务ID(若是删除,则该行有一个删除位更新为已删除。但并不是真正的进行物理删除,当InnoDB丢弃为删除而编写的更新撤消日志记录时,它才会物理删除相应的行及其索引记录。此删除操作称为清除,速度非常快)

  2. DB_ROLL_PTR(7字节): 回滚指针,指向当前记录行的undo log信息(指向该数据的前一个版本数据)

  3. DB_ROW_ID(6字节): 随着新行插入而单调递增的行ID。InnoDB使用聚集索引,数据存储是以聚集索引字段的大小顺序进行存储的,当表没有主键或唯一非空索引时,innodb就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了。这个DB_ROW_ID跟MVCC关系不大。

Read View

  • read view是一种快照,里面记录了系统中当前活跃事务的ID以及相关信息,主要用途是用来做可见性判断,判断当前事务是否有资格访问该行数据

Undo log

  • Undo log是逻辑日志,操作数据库数据就必回在这个日志文件中进行记录, 其中存储的是老版本数据,当一个事务需要读取记录行时,如果当前记录行不可见,可以通过回滚指针顺着undo log链找到满足其可见性条件的记录行版本。

  • 在InnoDB里,undo log分为如下两类:

①insert undo log : 事务对insert新记录时产生的undo log, 只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。

②update undo log : 事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,快照读也需要,所以不能轻易删除,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除。

Purge线程:上文提到了InnoDB删除一个行记录时,并不是立刻物理删除,而是将该行数据的DB_TRX_ID字段更新为做删除操作的事务ID,并将删除位deleted_bit设置为true(已删除),将其放入update undo log中。为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。

MVCC实现原理


更新操作实现原理

  • MVCC机制下实现更新还是会用到排他锁,但由于我们读的时候可以通过快照读,读多个版本就好比使用了共享锁,因此可以使得读事务不会因为写事务阻塞。MVCC的优越性在于事务需要读行记录的时候不会因为有事务在更新该行记录而阻塞,事务在写行记录时也不会因为有事务在读数据而阻塞。

  • 更新原理: 假设我现在需要修改行记录A,他们的修改过程如下,

(1)MVCC更新行记录A时会先用排他锁锁住该行记录A;

(2)然后将该行记录复制到update undo log中,生成旧版本行记录B;

(3)使行记录A的回滚指针指向这条旧版本B,再在行记录A中修改 用户需要修改的字段,并将DB_TRX_ID字段更新为更新这条记录的事务ID;

(4)最后提交事务。(用户需要修改的字段指的是业务字段,比如我们要修改name等)

通过回滚指针,形成了一条当前行记录指向历代旧版本行记录的链表,通过这条链表,我们就可以查询该行记录的多个旧版本。

查询操作的实现原理

  • InnoDB中,事务在第一次进行普通的select查询时,会创建一个read view(快照, 其内部会有一个事务的id),用于可见性判断,事务只能查询到行记录对于事务来说可见的数据版本。可见性判断是通过行记录的DB_TRX_ID(最近一次插入/更新/删除该行记录的事务ID)以及read view中的事务的id变量比较来判断。

  • 查询过程如下:

(1) 如果 DB_TRX_ID< up_limit_id,则表明这个行记录最近一次更新在当前事务创建快照之前就已经提交了,该记录行的值对当前事务是可见的,当前事务可以访问该行记录,跳到步骤(4)。

(2) 如果DB_TRX_ID>=low_limit_id,则表明这个行记录最近一次更新是快照创建之后才创建的事务完成的,该记录行的值对当前事务是不可见的,当前事务不可以访问该行记录。因此当前事务只能访问比该行记录更旧的数据版本。通过该记录行的 DB_ROLL_PTR 指针,找到更旧一版的行记录,取出更旧一版的行记录的事务号DB_TRX_ID,然后跳到步骤(1)重新判断当前事务是否有资格访问该行记录。

(3) 如果up_limit_id<=DB_TRX_ID< low_limit_id,则表明对这个行记录最近一次更新的事务可能是活跃列表中的事务也可能是已经成功提交的事务(事务ID号大的事务可能会比ID号小的事务先进行提交),比如说初始时有5个事务在并发执行,事务ID分别是1001~1005,1004事务完成提交,1001事务进行普通select的时候创建的快照中活跃事务列表就是1002、1003、1005。因此up_limit_id就是1002, low_limit_id就是1006。对于这种情况,我们需要在活跃事务列表中进行遍历(因为活跃事务列表中的事务ID是有序的,因此用二分查找),确定DB_TRX_ID是否在活跃事务列表中。

(3.1)若不在,说明对这个行记录最近一次更新的事务是在创建快照之前提交的事务,此行记录对当前事务是可见的,也就是说当前事务有资格访问此行记录,跳到步骤(4)。

(3.2)若在,说明对这个行记录最近一次更新的事务是当前活跃事务,在快照创建过程中或者之后完成的数据更新,此行记录对当前事务是不可见的(若可见则会造成脏读、不可重复读等问题)。因此当前事务只能访问该行记录的更旧的版本数据。通过该记录行的 DB_ROLL_PTR 指针,找到更旧一版的行记录,取出更旧一版的行记录的事务号DB_TRX_ID,然后跳到步骤(1)重新判断当前事务是否有资格访问该行记录。

(4)可以访问,将该行记录的值返回。

当前读和快照读

最后

既已说到spring cloud alibaba,那对于整个微服务架构,如果想要进一步地向上提升自己,到底应该掌握哪些核心技能呢?

就个人而言,对于整个微服务架构,像RPC、Dubbo、Spring Boot、Spring Cloud Alibaba、Docker、kubernetes、Spring Cloud Netflix、Service Mesh等这些都是最最核心的知识,架构师必经之路!下图,是自绘的微服务架构路线体系大纲,如果有还不知道自己该掌握些啥技术的朋友,可根据小编手绘的大纲进行一个参考。

image

如果觉得图片不够清晰,也可来找小编分享原件的xmind文档!

且除此份微服务体系大纲外,我也有整理与其每个专题核心知识点对应的最强学习笔记:

  • 出神入化——SpringCloudAlibaba.pdf

  • SpringCloud微服务架构笔记(一).pdf

  • SpringCloud微服务架构笔记(二).pdf

  • SpringCloud微服务架构笔记(三).pdf

  • SpringCloud微服务架构笔记(四).pdf

  • Dubbo框架RPC实现原理.pdf

  • Dubbo最新全面深度解读.pdf

  • Spring Boot学习教程.pdf

  • SpringBoo核心宝典.pdf

  • 第一本Docker书-完整版.pdf

  • 使用SpringCloud和Docker实战微服务.pdf

  • K8S(kubernetes)学习指南.pdf

image

另外,如果不知道从何下手开始学习呢,小编这边也有对每个微服务的核心知识点手绘了其对应的知识架构体系大纲,不过全是导出的xmind文件,全部的源文件也都在此!

image

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
部的源文件也都在此!

[外链图片转存中…(img-CEHtGL1L-1714711020496)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL的多版本并发控制MVCC)是一种并发控制机制,它主要是为了解决并发读写冲突的问题。在MVCC机制中,每个事务都可以看到数据库中的一个快照,这个快照是在事务开始时确定的。事务读取数据时,实际上是读取了该快照中的数据,而不是实际的数据。当事务需要修改数据时,MySQL会根据数据的版本号来判断是否可以进行修改。 MVCC的实现原理主要是在每一行数据后面保存多个版本号,并且还需要保存该版本号对应的事务ID。当开始一个事务时,MySQL会为该事务分配一个唯一的事务ID,该事务ID会被用于标记事务对应的数据版本号。当一个事务需要读取数据时,MySQL会根据该事务的事务ID和版本号来判断是否允许读取该数据。如果该事务的事务ID小于等于该数据的版本号,那么就可以读取该数据。如果该事务需要修改数据,则MySQL会为该数据在数据库中创建一个新版本,并将该新版本版本号和事务ID保存下来。这样,其他事务就可以继续读取原来的版本,而该事务则可以读取新版本并修改数据,从而实现并发控制。 需要注意的是,MVCC只能解决读写冲突的问题,而不能解决写写冲突的问题。此外,MVCC也会占用一定的存储空间,因为每个数据行都需要保存多个版本号和事务ID。因此,在使用MVCC机制时,需要注意存储空间和性能方面的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值