MySQL的MVCC模式

一、MVCC介绍

在早期的数据库中,只有读读之间的操作才可以并发执行,读写,写读,写写操作都要阻塞,这样就会导致MySQL的并发性能极差。
采用了MVCC机制后,只有写写之间相互阻塞,其他三种操作都可以并行,这样就可以提高了MySQL的并发性能。

二、MVCC生效前提

  • MySQL使用InnoDB引擎。
  • 事务隔离级别设置为READ COMMITTED或REPEATABLE READ。

三、MVCC依赖的数据结构

  • 隐式字段
    • DB_ROW_ID:隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
    • DB_TRX_ID:最近修改(修改/插入)事务ID,记录创建这条记录/最后一次修改该记录的事务
    • IDDB_ROLL_PTR:回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
    • DELETED_BIT:记录被更新或删除并不代表真的删除,而是删除flag变了。
  • undo log
    针对数据库中的一条记录的每次修改都会有一条修改记录,方便事务的回滚,undo log主要氛围以下三种。
    • Insert undo log :插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉。
    • Update undo log:修改一条记录时,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值。
    • Delete undo log:删除一条记录时,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
      删除细节:删除操作都只是设置一下老记录的DELETED_BIT,并不真正将过时的记录删除。为了节省磁盘空间,InnoDB有专门的purge线程来清理DELETED_BIT为true的记录。为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view);如果某个记录的DELETED_BIT为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。
      我们以update表中的记录为例,查看一下undo log记录的流程。
      表中已存在的记录如下
      在这里插入图片描述
      现在来了一个事务1对该记录的name做出了修改,改为Tom在事务
  1. 修改该行(记录)数据时,数据库会先对该行加排他锁
  2. 然后把该行数据拷贝到undo log中,作为旧记录,即在undo log中有当前行的拷贝副本
  3. 拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务ID为当前事务1的ID, 我们默认从1开始,之后递增,回滚指针指向拷贝到undo log的副本记录,即表示我的上一个版本就是它
  4. 事务提交后,释放锁
    在这里插入图片描述
  • ReadView
    什么是Read View,说白了Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大)。

    所以我们知道 Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,即可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

    Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来,与系统当前其他活跃事务的ID去对比(由Read View维护),如果DB_TRX_ID跟Read View的属性做了某些比较,不符合可见性,那就通过DB_ROLL_PTR回滚指针去取出Undo Log中的DB_TRX_ID再比较,即遍历链表的DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的DB_TRX_ID, 那么这个DB_TRX_ID所在的旧记录就是当前事务能看见的最新老版本。
    那么这个判断条件是什么呢?在展示之前,我先简化一下Read View,我们可以把Read View简单的理解成有三个全局属性。

    • trx_list 未提交事务ID列表,用来维护Read View生成时刻系统正活跃的事务ID
    • up_limit_id 记录trx_list列表中事务ID最小的ID
    • low_limit_id ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1

    判断是否可见算法(本事务要查看的数据为DB_TRX_ID)

    • 首先比较DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断(如果DB_TRX_ID为当前事务,则可见)。
    • 接下来判断DB_TRX_ID大于等于low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在ReadView生成后才出现的,那对当前事务肯定不可见,如果小于则进入下一个判断
    • 判断DB_TRX_ID是否在活跃事务之中,trx_list.contains(DB_TRX_ID),如果在,则代表我ReadView生成时刻,你这个事务还在活跃,还没有Commit,你修改的数据,我当前事务也是看不见的;如果不在,则说明,你这个事务在ReadView生成之前就已经Commit了,你修改的结果,我当前事务是能看见的。

    RR和RC中ReadView生成规则

    • RR中的ReadView已经事务开启,事务全局会共享一个
    • RC中的ReadView事务开启后所有事务共享一个最新的

原文链接:https://pdai.tech/md/db/sql-mysql/sql-mysql-mvcc.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值