• MVCC是什么


    什么是MVCC

    MVCC是一种同一主键的行记录保存多个版本的机制,实现在并发读-写情况下不加锁读,同时解决了脏读、不可重复读。

    MVCC如何实现

    MVCC通过版本链实现。

    什么是版本链

    版本链就是一个链表,每个节点上保存的是行记录的一个版本,其中头节点是行记录的最新版本,头节点后的第一个节点是行记录最新版本的上一个版本,头节点后的第二个节点是行记录最新版本的上上一个版本,以此类推。

    版本链如何实现

    版本链又是如何实现的呢?其实InnoDB的行记录中除了我们自定义的列之外,还有三个隐藏列,分别是trx_idroll_pointerrow_id

    trx_id:事务id,表示这条行记录是哪个事务生成/修改的

    roll_pointer:指向undo log中这条行记录的上一个版本

    row_id:当用户没有指定主键且没有非null的唯一列时,使用row_id作为主键

    通过roll_pointer就形成了版本链,有了版本链就可以查询到行记录的每个版本。

    如何解决脏读、不可重复读问题

    假如是我们来解决脏读和不可重复读问题,在有版本链的情况下应该如何解决呢?(一条行记录,创建/修改它的事务已经提交了,下面将简称已提交行记录,反之为未提交行记录)

    脏读是指读到了未提交行记录,我们很容易想到:版本链上那么多版本,我们读一条已提交行记录不就行了?

    不可重复读是指在一个事务中两次读取一个行记录,但是获取到的数据不一致,原因是在两次读取的间隔中有其他事务修改了这个行记录并提交了,那如果在当前事务提交之前无论读多少次都读取版本链上的同一个已提交行记录,这样的话读到的数据不就一样了?

    那么哪个版本才能让我们在当前事务提交之前的各个时期都能读取到呢?那就是当前事务开始时的已提交行记录。至于当前事务开始时的未提交行记录甚至是当前事务开始之后才生成的未提交/已提交行记录都是不行的。

    提炼一下解决脏读和不可重复读需要的条件:

    • 解决脏读是需要读一个已提交行记录

    • 解决不可重复读是需要读取当前事务开始之前的已提交行记录

    也就是说我们只要读取当前事务开始时的已提交行记录,并且当前事务没有提交之前每次都读同一个版本,那就可以解决脏读和不可重复读问题。

    非礼勿视——ReadView

    大概的思路就是这样,但是我们怎么知道我们读取的行记录是不是在当前事务开始时就已经提交了呢?这时候就轮到行记录隐藏字段trx_id上场了。

    可能大家会想到,喔!我懂了!直接判断行记录的事务id和当前事务id的大小就行了,小于当前事务id的就是在当前事务开始时就已经提交的行记录;大于当前事务id的就是在当前事务开始之后才提交的行记录。

    需要注意的是,事务id是事务对行记录创建/修改时分配的,而不是事务提交的时候分配的。一个事务的事务id小,但是因为操作比较多,可能比其他事务id大的事务提交的时间还要晚。所以只凭行记录事务id和当前事务id是无法判断生成这条行记录的事务是否是在当前事务之前提交的。

    那么到底是如何实现判断的呢?InnoDB设计了一个ReadView,这个ReadView很简单,就4个部分,分别是:

    • m_ids:当前事务生成ReadView时活跃事务的列表(简称活跃列表)
    • min_trx_id:活跃列表中最小的事务id(最小事务id)
    • max_trx_id:生成ReadView时下一个要分配的事务id(最大事务id)
    • creator_trx_id:生成ReadView的事务id(当前事务id)

    有了ReadView我们就可以知道我们读取的这个行记录是不是在当前事务开始时就已经提交了:

    1. 首先这个版本的行记录事务id一定不能大于或者等于最大事务id,如果不满足,说明生成这个行记录的事务是在当前事务开始之后才生成的,肯定不行。

    2. 如果这个版本的行记录事务id比最小事务id要小,说明在当前事务生成时它已经提交了,可以读。

    3. 如果这个版本的行记录事务id和当前事务id一样,说明这个记录是当前事务修改/生成的,可以读。

    4. 如果这个版本的事务id比最小事务id要大,但是又比最大事务id要小,那就难办了,我们得看它是否在活跃事务列表中:

      • 如果在,那说明这个行记录在当前事务开始时还没有提交,不行;

      • 如果不在,那说明这个行记录在当前事务开始时提交了,可以读。

    为什么需要活跃列表:一些事务的事务id比最小事务id要大,但是它可能在当前事务开始之前就已经提交了。

  • 相关阅读:
    使用Optional和直接返回null,哪个更好?
    08-Nginx缓存集成
    谷歌研究员走火入魔事件曝光:认为AI已具备人格,被罚带薪休假,聊天记录让网友San值狂掉...
    基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发
    基于springboot敬老院管理系统毕业设计-附源码161551
    P1208 [USACO1.3] 混合牛奶 Mixing Milk
    在 Kubernetes 环境中实现证书管理的自动化
    《计算机网络》:考研 2024/3/5 2.1.1-物理层基本概念引入
    第十三届蓝桥杯国赛真题 PythonB组 复盘以及获奖感言(国一!!!)
    【Git命令】git commit --amend
  • 原文地址:https://blog.csdn.net/zhan13253807836/article/details/126073912