首页 > 数据库 >MySQL(二十一)MVCC多版本并发控制

MySQL(二十一)MVCC多版本并发控制

时间:2023-07-18 09:24:47浏览次数:41  
标签:事务 加锁 快照 MySQL 并发 版本 MVCC id

MySQL(二十一)MVCC多版本并发控制


1 什么是MVCC

  • MVCC(Multiversion Concurrency Control)多版本并发控制。即通过数据行的多个版本管理来实现数据库的并发控制,使得在InnoDB事务隔离级别下执行一致性读操作有了保障。
  • 就是为了查询一些正在被其他事务更新的值的时候,能够查到它们被更新之前的值,这样做就能在查询的时候不必等待更新事务的提交
  • MySQl中只有InnoDB支持MVCC,其他存储引擎不支持

2 快照读和当前读

  • MVCC在InnoDB中的实现主要是为了提高数据库的并发性能,用更好的方式处理读写冲突,做到即使有读写冲突,也能不加锁实现非堵塞并发读,这个读指的是快照读而不是当前读
  • 当前读实质上是一种加锁的操作,是悲观锁的体现;而MVCC是采用乐观锁的一种方式
2.1 快照读
  • 快照读又称作一致性读,对于普通的不加锁的简单SELECT都属于快照读,即不加锁的非堵塞读
  • 快照读的实现基于MVCC,在很多情况下,避免了加锁操作,降低了开销
  • 由于是多版本数据,所以快照读独到的可能不是最新的数据而是之前更新的历史版本
  • 快照读的前提是隔离级别不是可串行化可串行化快照读会退化为当前读
2.2 当前读
  • 当前读要求读取的是最新版本的数据
  • 还要求保证其他并发事务不能修改当前事务,会对读取的记录加锁
  • 加锁的SELECT(共享或排它锁)或者对数据进行增删改操作(自动添加排它锁)都会进行当前读

3 回顾

3.1 再谈隔离级别
image-20230508204516647

​ 在MySQL中默认的隔离级别是可重复读,从SQL标准的定义看它能解决脏读、不可重复读问题,但是不能解决幻读问题,如果想要解决幻读问题,需要提高隔离级别标准,设置为可串行化但响应地并发程度也会降低。

​ MVCC可以不使用可串行化的锁机制,而是通过乐观锁(MVCC )+ Next-key lock 临键锁的方式来解决幻读问题,可以在大多数情况下替代掉行级锁,降低系统的开销。

image-20230509094937068
3.2 隐藏字段、Undo log版本链

​ undo log的版本链,对于使用InnoDB存储引擎的表来说,它的聚簇记录中包含两个必要的索引列:

  • trx_id:每次事务对聚簇记录进行修改的时候,就会将该事务的id复制给trx_id隐藏列
  • roll_pointer:每次对每条聚簇索引进行改动的时候,都会将旧的版本信息写入undo log中,通过回滚指针就能找到记录修改前的信息

​ 比如插入一条记录,记录的示意图如下:

image-20230509095703376

insert log只在事务中起到回滚的作用,当事务提交之后,该类型的undo log记录就无效了,它占用的undo log segment也会被系统回收(undo log占用的页面链表要么被重用,要么被释放)

​ 假设两个事务id分别为10、20的事务分别对这条记录进行Update操作

image-20230509100311039

在InnoDB中,会对增删改操作自动添加排它锁,因此两个事务不会出现脏写的情况,也就是不会出现两个事务交叉着对同一条记录进行修改,必须等待第一个事务提交才能进行第二个事务

​ 每次对记录进行改动,都会记录一条undo log,每个undo log都包含创建它的事务id,每条undo log都会有一个roll pointerINSERT操作不会有,因为插入没有更新的版本),这些undo log通过roll pointer连接起来,串成一个链表,这个链表就成为undo log 版本链

image-20230509100559256

4 MVCC的实现原理--ReadView

​ MVCC的实现依赖于:隐藏字段Undo logRead View

4.1 什么是ReadView
  • 在MVCC中,多个事务对同一行记录进行更新会产生多个历史快照,这些记录保存在Undo Log里

  • Read View就是事务在使用MVCC机制在进行快照读操作时产生的快照

  • 快照记录创建这个Read View的事务id、活跃的事务中最小的id、系统最大的事务id,并且InnoDB会为每个事务构建了一个数组,用来记录并维护系统当前活跃事务的ID(活跃指的是启动了还没有提交)

  • 等到访问某条记录的时候,就可以根据上面记录的内容判断记录版本对当前事务可不可见

    • 如果Read Viewcreator_id当前事务的id相同,则意味着当前事务在访问它修改过的id,所以该记录版本可以被事务访问
    • 如果当前访问版本记录的trx_id小于Read Viewup_limit_id,则意味着修改该数据版本的事务已经提交,所以该版本的记录可以被当前事务访问
    • 如果当前访问版本记录的trx_id大于等于Read Viewlow_limit_id,则意味着创建该数据版本的事务是在ReadView生成之后才出现的,因此当前事务不能访问
    • 如果当前访问版本记录的trx_idRead Viewup_limit_idlow_limit_id之间,则需要判断trx_id是否在Read Viewtrx_ids活跃事务列表中,如果在则说明事务还没有提交当前事务不能访问,否则可以访问
4.2 ReadView的组成
  1. creator_id:创建这个Read View的事务id

  2. trx_ids:表示创建这个Read View的时候正在活跃的事务id列表

  3. up_limit_id:活跃的事务中最小的id

  4. low_limit_id:表示生成low_limit_id时系统应该分配给下一个事务的id值,low_limit_id是系统最大的事务id(而不是活跃的最大事务id)

    low_limit_id并不是trx_ids的最大值而是系统能够分配的事务id最大值,事务id是递增分配的,并且只有事务在进行增删改操作的时候才会分配事务ID。比如现在有1 2 5三个事务,那么id为5的事务提交后,一个新事务在生成ReadView的时候,trx_ids就包括1 2,up_limit_id就是1,low_limit_id就是6

举例

image-20230509103313272

​ 如上,此时如果有事务创建Read View,则

  • trx_ids=[trx2, trx3, trx5, trx8]
  • up_limit_id=trx2
  • low_limit_id=trx8+1
4.3 MVCC的整体流程

​ 当查询一条技术的时候,系统

  1. 首先获取查询操作的事务的版本号
  2. 获取当前系统的ReadView
  3. 将查询到的数据与ReadView中的事务版本号进行比较
  4. 如果不符合ReadView的规则,则通过回滚指针形成的Undo Log版本链undo log中获取符合规则的历史快照
  5. 返回符合规则的数据
4.4 隔离级别设计思路
  • 读未提交:能够读取未提交的事务修改的数据,所以直接读取最新的记录就可以,不必使用MVCC
  • 读已提交:不能读取未提交的事务修改的数据,并且不能进行重复读取,所以查询的时候每次都获取一次MVCCReadView视图
  • 可重复读:不能读取未提交的事务修改的数据,并且能进行重复读取,所以只在第一次查询的时候获取一次ReadView,之后查询都只查看已经生成的ReadView副本
  • 可串行化:InnoDB规定使用加锁的方式来访问记录
4.5 MVCC在可重复读下解决幻读问题

​ MySQL可以通过两种方式解决幻读问题

  • 读写加锁,也就是使用可串行化的隔离模式

  • 使用MVCC进行快照读,写使用临键锁

    添加的临键锁不会影响快照读,只会影响到想要获取锁的读操作

​ 可以回顾一下这一部分:MySQL在Repeatable Read隔离级别下是可以解决幻读问题的,解决的方案有两种:

  • 通过MVCC

    读操作利用多版本并发控制MVCC),写操作加

    MVCC就是生成一个ReadView,通过ReadView能够找到符合条件的记录版本(历史版本由undo log提供查询),查询语句执行查询已经提交的事务做出的更改,对于没由提交的事务和ReadView创建之后的事务做出的更改是看不到的。而写操作肯定是针对的最新版本的记录,因此读记录的历史版本和写操作的最新记录版本并不会冲突,也就是采用MVCC时,读写操作并不会冲突

    普通的SELECT语句在READ COMMITTED 和 REPEATABLE READ隔离级别下的读操作就是利用MVCC进行的读

    • READ COMMITTED:由于不会读取没有提交的事务修改的数据版本,因此避免了脏读问题
    • REPEATABLE READ:由于不会读取Read View创建之后的事务更改的数据(一个事务只有在第一次执行SELECT语句才会生成一个Read View,之后的SELECT语句都在复用),因此避免了可重复读和幻读问题
  • 通过加锁的方式

    读、写操作都采用加锁的方式

    在一些业务场景中,不允许读取数据的历史版本,即每次都需要去读取磁盘中最新的数据,这样也就意味着读操作也需要和写操作一样排队执行。

    如此一来,脏读不可重复读问题都得到了解决,因为读操作和写操作的串行执行,不会出现一个事务读取另一个未提交事务的数据以及一个事务读取过程中另一个事务修改数据提交导致前一个事务前后读取数据不一致的情况(第二个事务根本无法开始)

    标签:事务,加锁,快照,MySQL,并发,版本,MVCC,id
    From: https://www.cnblogs.com/tod4/p/17561857.html

相关文章

  • MySQL(十五)分析优化器的查询计划:Trace
    1MySQL(十五)分析优化器的查询计划:Trace​ OPTIMIZER_TRACE是mysql5.6引入的一项追踪功能,它可以追踪优化器做出的各种决策(比如访问表的方法、各种开销计算和各种转换等等),并将结果记录到表INFORMATION_SCHEMA.OPTIMIZER_TRACE表中。​ Trace功能默认是关闭的,需要开启trace,设置JS......
  • Mysql基础4-数据查询
    一、DQL介绍DQL全称:DataQueryLanguage(数据查询语言),用来查询数据库中表的记录。关键字:select 二、DQL语法select字段列表from表名列表where条件列表groupby分组字段列表having分组后条件列表orderby排序字段列表limit分页参数注意:本章......
  • .NET Core6.0 通过EF的方式如何链接MySQL
    之前一直用的是EF链接SQLServer今天聊一聊EF链接MySQL第一步我们首先创建一个简单的用户实体类 这里呢创建了一个主键为string类型的用户Id和一个用户名称第二步我们创建一个类库里面创建上下文类 但是这里需要注意引用的NuGet包和链接SQLServer的包有个是不一样的这是......
  • linux Mysql 备份与还原
    数据库是企业中非常重要的部分,数据是企业的根本,不可丢失的,需要备份和还原。目录一、数据备份的重要性二、数据库备份类型三、常见的备份方法四、MySQL完全备份五、数据库完全备份分类六、实战案列七、总结   一、数据备份的重要性1.数据备份的重要......
  • mysql 更新时where条件缺失导致更新全表问题
    1、问题更新时where条件缺失导致更新全表问题2、错误sqlupdateorderset`status`=1in('XX001','XX002','XX003');错误分析:更新整个order表,并没有限制更新的范围。原因是这个SQL语句在IN子句中没有提供任何条件,导致MySQL将IN子句视为一个包含了所有order_id值的列表。......
  • API接口技术开发分享,获得亚马逊AMAZON国际站商品详情案例,可以多语言请求,支持高并发演
    ​ 响应参数数据展示名称类型必须示例值描述detail_urlString0https://www.amazon.cn/dp/B014QN8RG0?th=1&psc=1商品链接crumbsMix0{"162371071":"徒步鞋、登山鞋","2029189051":"鞋靴","2112046051":"男鞋","......
  • mysql 笔记
    行转列: namecoursegradezhangsanjava20zhangsanc#60zhangsanpython40lisijava109lisic#30lisipython20wangwujava33 selectname,sum(casewhencourse='java'thengradeend)as'java',sum(casewhen......
  • 浅谈oracle,mysql数据备份
    oracle备份 方案1:Navicat工具迁移1.1开启Navicat,打开工具-数据同步   1.2选定原数据源与需要迁移到的数据源  1.3选择下一步,比对后开始进行数据同步   方案2:数据库服务器迁移2.1登录源数据库切换用户su-oracle切换到临时目录cd/u01/app/oracle......
  • Window安装解压版MySQL5.7
    软件下载官网地址:https://www.mysql.com下载地址:https://dev.mysql.com/downloads/mysql/安装步骤下载后会得到zip安装文件将zip安装文件解压到某个目录下,解压的目录最好不要有中文和空格以E:\Dev\languages\mysql\mysql-5.7.25目录为例添加环境变量:电脑-属......
  • mysql报错:You must reset your password using ALTER USER statement before executin
    mysql报错:YoumustresetyourpasswordusingALTERUSERstatementbeforeexecutingthisstatement.新安装mysql后,登录后,执行任何命令都会报错:YoumustresetyourpasswordusingALTERUSERstatementbeforeexecutingthisstatement. 【解决办法】MySQL版本5.7.6版本......