MySQL-MVCC全网最详细解读

异步潮涌
• 阅读 1137

什么是MVCC

在学习MVCC前,先了解一下当前读和快照读

  • 当前读

    比如lock in share mode(共享锁),for update,update,delete,insert(排它锁)这些操作都是一些当前读,当前读的定义就是读取当前数据的最新版本,读取时还要保证其它并发事务不能修改数据,会对读取的记录进行加锁

  • 快照读

    不加锁的select就是快照读,即不加锁的非阻塞读,快照读的前提是隔离级别不是串行级别,串行级别下快照读会退化为当前读。快照读的实现是基于多版本并发控制,即MVCC。即快照读可能读取到的数据不一定是最新版本,而有可能是之前的历史版本

MVCC即多版本并发控制,保留有关已更改行的旧版本的信息以及支持事务功能,例如 并发和回滚,这些信息以回滚段的方式存储在UndoLog中。MVCC就是为了实现读写不冲突不加锁,读也就是快照读,不是当前读

什么是UndoLog

回滚日志(撤销日志),MVCC的重要组成部分,单个事务对记录变更操作的记录集合。如果另一个事务需要查看原始数据满足一致性读,则需要去undolog中检索,UndoLog保存在UndoLog Segments中,UndoLog Segments 保存在Rollback Segments中,回滚段保存在Undo表空间和全局临时表空间中。UndoLog一般是逻辑日志,记录数据的变更版本信息

mvcc实现原理

基于隐式字段+UndoLog+Read View实现

UndoLog中的每条记录都有隐藏字段

  • DB_TRX_ID 创建该条记录或者最后修改该条记录的事务ID
  • DB_ROLL_PTR 回滚指针,指向当前这条记录的上一个版本
  • DB_ROW_ID 隐藏的主键

UndoLog ,在insert,update,delete 时产生的回滚日志

Read View 可读视图

组成 [未提交事务数组],最大的事务id

可读视图判断规则

生成一致性视图规则read-view

由执行时所有未提交事务的id数组(数组里最小的trx_id为min_id)和已创建的事务id(max_id)组成,查询的数据结果根据生成 的read-view做对比得到快照的结果

MySQL-MVCC全网最详细解读

版本链比对判断规则

  • 如果落在绿色部分(trx_id<min_id) ,表示这个版本是已提交事务生成的,这个事务是可见的
  • 如果落在黄色部分(min_id<=trx_id<=max_id),分两种情况处理

    a. 若row的trx_id在数组中,表示这个事务是由还没提交的事务生成的,不可见,当前自己的事务是可见的

    b. 若row的trx_id不在数组中,表示这个事务是已经提交的事务生成的,可见

  • 如果落在红色部分(trx_id>max_id), 表示这个版本是由将来启动的事务创建的 ,肯定不可见

对于删除的情况可以认为是update的特殊情况,delete数据的时候会将版本上最新的数据复制一份,然后将trx_id修改成删除操作的trx_id,同时将该条记录的头信息(record header )里的(deleted_flag)标记位写上true,表示当前记录已经被删除,在查询时按照上面的规则查到对应的记录,如果deleted_flag标记位为true,意味着数据已经被删除,则不会被返回。

举例说明MVCC实现原理

表说明

  • t 事务操作表,演示表,undolog日志演示操作表
  • t1 ,辅助表,生成事务id

隔离级别

可重复读

验证

  • 查看数据库隔离级别(只有可重复读和读提交下MVCC有效)

    在RR级别下的某个事务对某条记录的第一次快照读会创建一个快照及read-view,将当前系统活跃的其它事务记录起来,此后在调用快照读的时候还是使用的同一个read-view,所以只要当前事务在其它事务更新提交之前使用快照读,那么之后的快照读都是统一个read-view,所以对之后的修改不可见。

    在RR级别下,快照读生成read-view时,read-view会记录此时所有其它事务的快照,这些事务对与当前事务都是不可见的,而早于read-view创建的事务都是可见的

    而在RC级别下,事务中每次都会生成一个新的read-view和快照,这就是我们在RC级别下可以看到别的事务提交的更新的原因

    总之就是在RC级别下,每个事务都会获取最新的read-view;而在RR级别下,则是同一个事物的第一个selete才会创建快照,之后的快照读都是第一次创建的read-view,都是同一个read-view

  • 下面为不同时刻不同事务时序图

    transaction100,200,300 为三个更新事务

    select1,2为两个读取事务

    Transaction 100Transaction 200Transaction 300select 1select 2
    begin;begin;begin;begin;begin;
    update t1 set t1col='t1colval' where id = '1';(辅助表t1中更新数据,生成一个事务ID100)
    update t1 set t1col='t1colval200' where id = '2';(辅助表t1生成事务ID200)
    update t set name = 'zuiyu300' where id = '1' ;(第一次更新数据)
    commit;
    select name from t where id = '1' ;(第一次读,创建readview[100,200] 300) 此时读出来的结果是name=zuiyu300
    update t set name='zuiyu100' where id = '1';
    Update t set name ='zuiyu101' where id = '1';
    select name from t where id = '1'; (第二次读,继续使用第一次创建的readview[100,200] 300) 此时读出来的结果是name=zuiyu300
    commit;
    update t set name='zuiyu200' where id = '1';
    update t set name='zuiyu201' where id = '1';
    select name from t where id = '1'; (第三次读,继续使用第一次创建的readview[100,200] 300) 此时读出来的结果是name=zuiyu300select name from t where id = '1';(此时会生成一个readview [200],300),此时结果name=zuiyu101
    commit; commit;commit;
  • 第一次读取时

    生成的read view [100,200]300,min_id=100,max_id=300

    版本链信息如下:

    MySQL-MVCC全网最详细解读

    查找规则为

    1、第一条记录trx_id=300,落在中间区域

    2、300不在获取事务数组中,所以可见,返回name=zuiyu300

  • 第二次读取时

    当前隔离级别为可重复度,使用第一次读取时生成的read view,生成的read view [100,200]300,min_id=100,max_id=300,

    版本链信息如下

    MySQL-MVCC全网最详细解读

    查找规则

    1、第一条记录trx_id=100,落在中间黄色区域

    2、事务id100在活跃事务数组中,表明不可见,继续往下找

    3、第二条记录trx_id=100,同比不可见

    4、第三条记录trx_id=300,落在中间区域,不在活跃事务数组,可见,返回name=zuiyu300

  • 第三次读取时

    当前隔离级别为可重复度,使用第一次读取时生成的read view,生成的read view [100,200]300,min_id=100,max_id=300

    版本链信息如下

    MySQL-MVCC全网最详细解读

    查找规则

    1、第一条记录trx_id=200,落在中间黄色区域,200在活跃事务数组中,不可见

    2、第二条记录trx_id=200,不可见

    3、第三条记录trx_id=100,落在中间黄色区域,100在活跃事务数组中,不可见

    4、第四条记录trx_id=100,不可见

    5、第五条记录trx_id=300,落在中间黄色区域,不在活跃事务数组中,可见返回name=zuiyu300

  • select 2 事务读取时

    生成的read view [200],300,min_id=200,max_id=300

    此时的活跃事务只有事务id为200的,当前的版本链信息如下:

    查找规则为

    1、从上往下,第一条记录trx_id=200,在活跃事务数组中,不可见

    2、第二条记录trx_id=200,不可见

    3、第三条记录trx_id=100,100<min_id,所以事务可见,返回结果name=zuiyu101

    MySQL-MVCC全网最详细解读

总结

可重复读隔离级别下,读取的历史版本数据,根据UndoLog版本链中trx_id在判断规则中比较,获取符合条件的数据返回

扫码关注,回复【面试】获取面试宝典

MySQL-MVCC全网最详细解读

点赞
收藏
评论区
推荐文章
Peter20 Peter20
4年前
【MySQL笔记】正确的理解MySQL的MVCC及实现原理
MVCC多版本并发控制如果觉得对你有帮助,能否点个赞或关个注,以示鼓励笔者呢?!!首先声明,MySQL的测试环境是5.7前提概要什么是MVCC什么是当前读和快照读?当前读,快照读和MVCC的关系MVCC实现原理隐式字段undo日志ReadView(读视图)
Wesley13 Wesley13
3年前
mysql主从数据对比工具简介
1Checksum1.1checksum原理checksumtable的原理是对表中的数据进行一行一行的较验和计算,在执行checksum命令时,表会被加一个读锁(readlock),因此对于大表,这是一个很耗时的过程。读锁:又叫S锁/共享锁;当MySQL
Wesley13 Wesley13
3年前
MySQL 中的共享锁和排他锁的用法
在MySQL中的行级锁、表级锁和页级锁中,咱们介绍过,行级锁是MySQL中锁定粒度最细的一种锁,行级锁能大大减少数据库操作的冲突。行级锁分为共享锁和排他锁两种,本文将详细介绍共享锁和排他锁的概念、使用方式及注意事项。共享锁(ShareLock)共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任
Wesley13 Wesley13
3年前
mysql 锁
第一章概述锁的分类:从对数据操作的粒度分表锁、行锁。从对数据的操作类型(读\\写)分读锁(共享锁)、写锁(排它锁)读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。写锁(排它锁):当前写操作没完成前,它会阻断其他写锁和读锁。第二章 表锁(偏读)偏向MyISAM存储引擎,开销小,加
Wesley13 Wesley13
3年前
JAVA中 ReentrantReadWriteLock读写锁详系教程,包会
一、读写锁简介现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。 针对这种场景,JAVA的并发包提供了读写锁ReentrantReadW
Wesley13 Wesley13
3年前
Java并发编程:Java中的锁和线程同步机制
锁的基础知识锁的类型锁从宏观上分类,只分为两种:悲观锁与乐观锁。乐观锁乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一
Wesley13 Wesley13
3年前
INNODB锁(2)
在上一篇文章写了锁的基本概述以及行锁的三种形式,这一篇的主要内容如下:一致性非锁定读自增长与锁外键和锁一致性性非锁定读一致性非锁定读是InnoDB通过多版本并发控制(MVCC,multiversionconcurrencycontrol)的方式来读取当前执行时间数据库中的最近一次快照,如果读取的行正在执行
Stella981 Stella981
3年前
SSM框架(3):配置Spring的事务管理器,实现事务控制
 一、相关概念1、不可重复读和幻读的区别  很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。  如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但
Stella981 Stella981
3年前
ReentrantReadWriteLock读写锁详解
一、读写锁简介现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。 针对这种场景,JAVA的并发包提供了读写锁ReentrantReadW
Wesley13 Wesley13
3年前
MySQL隔离级别
事务具有ACID四种特性。但是Isolation并发可能引起如下问题:1.脏读允许读取到未提交的脏数据。2.不可重复读如果你在时间点T1读取了一些记录,在T2时再想重新读取一次同样的这些记录时,这些记录可能已经被改变、或者消失不见。3.幻读解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(
Easter79 Easter79
3年前
SSM框架(3):配置Spring的事务管理器,实现事务控制
 一、相关概念1、不可重复读和幻读的区别  很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。  如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但