• MySQL高级篇——MVCC


    一、前言

    数据库会并发执行一些事务,多个事务之间可能会对相同的数据进行读写。如果什么都不做,那么可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题。为了解决并发问题,mysql提供了隔离级别、锁、mvcc等。

    在之前我们已经讲解过锁了,通过加锁是可以实现mysql不同的隔离级别并解决上述并发问题的。

    读-读之间不会产生什么问题,写-写之间只有用锁才能解决并发问题。那么我们目前需要讨论的只有读-写写-读之间能否有除了加锁以外更高效的方式(应为数据被加了写锁,那么所有读的请求就都要等待。而如果先被加了读锁,写操作可能要等很久才能被执行)。mysql的innodb通过mvcc来实现并发读写

    在读该文章时,您应该了解mysql事务、锁和undo日志


    二、简介

    1. 什么是MVCC

    MVCC(Multiversion Concurrency Control),多版本并发控制。MVCC通过数据行的多个版本管理来实现数据库的并发控制。
    MVCC没有正式标准,不同的DBMS中MVCC的实现方式可能是不同的,也不是普遍使用的。在mysql中目前只有innodb支持mvcc,其他存储引擎并不支持。

    2. 快照读和当前读

    • 当前读 : 读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。如共享锁select for share, 排他锁select for update,update,insert,delete等操作。
    • 快照读 : 不加锁的简单select都属于快照读,即非阻塞读。它是基于mvcc的,所以读取到的不一定是数据的最新版本,可能是之前的历史版本。快照读的前提是隔离级别不是串行级别,在串行级别下的快照会退化成当前读。

    3. 隐藏列

    对于innodb存储引擎中的聚簇索引中的行格式,包含3个隐藏列(其中有一个是当没有主键时,自动生成的主键列)。

    • trx_id 每次一个事务对该记录进行改动时,会把事务id写到该隐藏列。
    • roll_pointer 每次对某条聚簇索引记录进行改动时,会把旧版本写道undo日志中,然后这个隐藏列相当于一个指针,可以找到它之前版本的信息。

    4. 作用的隔离级别

    对于READ UNCOMMITTED隔离级别,由于可以读到未提交事务修改过的记录,所以之间读取记录的最新版本就可以。
    对于SERIALIZABLE 隔离界别,innodb采用加锁的方式来访问记录。
    所以mvcc只要是针对于RCRR隔离级别,这两种隔离级别都必须保证读到的是已提交的事务。


    三、MVCC实现原理之ReadView

    MVCC的实现依赖于:隐藏字段、undo log 、ReadView
    ReadView就是事务在使用mvcc进行快照读操作时产生的读视图。该视图记录了一些信息去保证在后续判断中该读到那个版本的数据。
    ReadView中主要包含4个比较重要的内容:

    • creator_trx_id 创建该ReadView的事务id(只有在对表中记录改动时,才会为该事务分配id)
    • trx_ids 在生成ReadView时当前系统中活跃的读写事务id列表
    • up_limit_id 活跃的事务中最小事务id
    • low_limit_id 系统中最大的事务id+1

    当生成了ReadView后,在通过mvcc访问某条记录时,按下边步骤来判断记录的那个版本是可见的:

    • 如果被访问版本的trx_id和ReadView的creator_trx_id相同,意味当前事务在访问自己修改过的记录,所以当前版本可以被事务访问。
    • 如果被访问版本的trx_id小于ReadView的up_limit_id,表明生成该版本的事务在当前事务之前就已经提交了,所以该版本可以看到。
    • 如果被访问版本的trx_id大于ReadView的low_limit_id,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被访问。
    • 如果被访问版本的trx_id在up_limit_idlow_limit_id之间,那么就要判断在不在trx_ids中。如果在,就说明该版本的事务还是活跃的不可被访问。如果不在,说明该版本的事务已经被提交,记录可以被访问。

    当不能访问当前版本的数据时,就会顺着版本链找到历史版本进行判断。如果最后一个版本也不可见,那么查询结果就查不出该记录。


    四、能否解决幻读

    在RC隔离级别下,每次读取数据前都会生成一个ReadView。还未提交的事务属于活跃事务,所以在RC隔离级别在无法看到未提交的数据,解决了脏读。
    在RR隔离级别下,只会在第一次执行查询语句的时候生成一个ReadView,之后就不会重复生成了。所以RR级别下只会读到第一次读到的数据,解决了不可重复读。并且感觉上应该也是解决了幻读,应为读不到新插入的数据了(在不可重复度级别下,新增修改删除的列都感知不到)。
    但是如果一开始是快照读,第二次是当前读,还是会存在幻读。所以mvcc解决了快照读的幻读问题,对于当前读还是有可能存在幻读的。


  • 相关阅读:
    jvm内存和线程信息查看
    “大数据分析”相比“传统数据分析”优势明显,体现在哪些方面
    2.5 晶体管单管放大电路的三种基本接法
    CentOS MySQL安装及问题解决
    【Mysql】清理binlog日志的方法
    嵌入式Linux应用开发-第七章-RK3288和 RK3399的 LED驱动程序
    JVM——11.JVM小结
    vue项目本地打包可以,线上部署报错Module parse failed: Unexpected token
    word转PDF 版本不一样可能过程各种找不到文件路径,需要自行修改
    java项目-第133期ssm物流服务管理平台系统-java毕业设计
  • 原文地址:https://blog.csdn.net/qq_51114283/article/details/126698825