• MySQL 事务隔离级别和MVCC版本控制


    一、事务并发执行的问题

    事务并发的问题有四种分别是脏写、脏读、不可重复读、幻读。其中脏读、脏写是在事务并发中读取到了未提交事务修改过的数据,这会导致比较严重的问题。

    问题描述
    脏写一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写
    脏读一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读
    不可重复读一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读
    幻读一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

    二、MySQL支持的隔离级别

    MySQL的默认隔离级别为REPEATABLE READ

    • READ UNCOMMITTED:未提交读,会导致脏读、不可重复读、幻读
    • READ COMMITTED:已提交读,会导致不可重复读、幻读
    • REPEATABLE READ:可重复读,会导致幻读
    • SERIALIZABLE:可串行化,可以杜绝并发出现的问题,但是效率较低。

    三、MVCC 版本控制

    1. 基础知识

    MySQL设计实现版本控制,主要通过InnoDB page记录中的字段和Undo Log实现版本链记录,结合ReadView 事务id的比较实现版本控制。

    1. InnoDB聚族索引中存在两个必要的隐藏列
      • row_id 可以是主键,如果没有主键则是自动生成隐藏列。
      • trx_id 事务对聚族索引改动时将当前修改的事务id赋值给trx_id。
      • roll_pointer 对应undo log日志位置,可以回溯到历史版本。
    2. 版本链
      在多个事务对同一条记录修改时,将修改前的记录保存在Undo Log中,多次的修改将会生成一个版本链。其中tri_id就是不同事务修改赋值给聚族索引的事务id。
      在这里插入图片描述
      在这里插入图片描述

    trx_200需要等待trx_100更完毕后才能更新是因为:InnoDB使用锁来保证不会有脏写情况的发生,也就是在第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。

    有了版本链的记录,MySQL就可以通过当前事务id判断是在提交前读取记录,还是提交之后读取记录了。所以就有了ReadView的概念,实现版本控制

    2. ReadView实现版本控制

    ReadView的概念

    这个ReadView中主要包含4个比较重要的内容:

    • m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
    • min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
    • max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值,是一个自增策略的ID。
    • creator_trx_id:表示生成该ReadView的事务的事务id。
      在读事务中有了ReadView记录,MySQL就可以根据当前活跃的事务存在的ids,以及ids中的最大id和最小id判断undo log中trx_id是否已经提交,是否在当前活跃事务中,从而实现版本的控制。

    记录trx_id和ReadView字段关系总结

    • trx_id = creator_trx_id,当前事务访问自己修改过的记录,可访问
    • trx_id < min_tri_id,生成该版本的事务在当前事务生产ReadView已经提交,可访问
    • trx_id >= max_trx_id,生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
    • min_trx_id < trx_id < max_trx_id ,需要判断trx_id是否在m_ids中,存在则表示是当前正在活跃的事务不可访问,不存在则表示在生成ReadView的时候已经提交,可以访问。

    ReadView进行版本控制主要是对可重复读和读已提交的隔离级别设计的,因为在读未提交直接读取最新版本即可,可串行化则是直接加锁访问。
    而在READ COMMITTEDREPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的。

    所以在判断READ COMMITTED 和REPEATABLE READ 实现版本控制,只要读事务在生成ReadView的记录活跃ids不同即可。
    READ COMMITTED中,开启两个事务trx_100的事务先修改记录,查询记录则ids=[100,200],然后提交trx_100事务。然后trx_200开始修改记录,查询记录生成ReadView的ids=[200],所以可以读取到trx_100提交的记录。前后读事务生成了两次ReadView。
    REPEATABLE READ 中,开启两个事务trx_100的事务先修改记录,但只是在第一次生成ReadView。第二次读事务则不再生成新的ReadView。所以两次查询的接口都是一致的。

  • 相关阅读:
    内部错误: !scandr.cpp@815: eWasOpenForWrite
    洛谷刷题C语言:Imena、Bus、正方、Grave、双生独白
    【ACWing】4275. Dijkstra序列
    AIGC AI绘画 Midjourney 参数大全详细列表
    k8s管理工具kubectl详解(二)
    基于MFC和C++的校园导航系统
    不用bs4的原因居然是名字太长?爬取彩票开奖信息
    90%的面试官都会问到交换网络里面冗余和破环的STP协议
    1. 懒加载的概念、特点和原理?
    meterpreter后期攻击使用方法
  • 原文地址:https://blog.csdn.net/stopping5/article/details/126376088