MySQL一致性读实现原理

news2025/7/20 23:48:49

一致性读实现原理

  • 前言
  • MySQL中的事务
    • 事务的ACID
    • 事务的并发问题
    • 事务隔离级别
  • InnoDB并发控制
    • MVCC特性
    • MVCC快照
    • 隔离级别与快照
      • REPEATABLE READ
      • READ COMMITTED
    • Undo回滚段
    • 一致性读
  • 总结

前言

现在,主流关系型数据库产品基本都实现了MVCC的特性,快照在MVCC中起着重要的作用,代表某一时刻数据的版本,它是实现一致性读的基础。在更新操作没提交前,数据的前镜像存储在Undo中,利用Undo可以实现一致性读,事务回滚以及异常恢复等操作,下面来聊聊MySQL事务,MVCC,快照及一致读的原理与实现。

MySQL中的事务

事务在RDBMS系统中概念基本都是一样的,是由一组DML语句构的工作单元,要么全部成功,要么全部失败。
在这里插入图片描述
开发过程中,比较关心长事务,即包含DML语句多的工作单元,事务太长会导致一些错误,例如可能由于事务数据包大小超过参数max_allowed_packet 设置会导致程序报错,也可能有事务中某个SQL对应接口报错,导致整个服务调用失败,在程序设计时,应该考虑避免长事务带来的业务影响。

事务的ACID

在这里插入图片描述
原子性是事务隔离的基础,隔离性和持久性是手段,最终目的是为了保持数据的一致性。

事务的并发问题

  • 脏读:事务A读取了事务B未提交的数据。
  • 不可重复度:事务A多次读取同一份数据,事务B在此过程中对数据修改并提交,导致事务A多次读取同一份数据的结果不一致。
  • 幻读:事务A修改数据的同时,事务B插入了一条数据,当事务A提交后发现还有数据没被修改,产生了幻觉。

不可重复读侧重于update操作,幻读侧重于insert或delete。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

事务隔离级别

事务隔离是数据库处理的基础之一,隔离级别在多个事务同时进行更改和执行查询时,对性能与结果的可靠性、一致性和可再现性之间的平衡进行调整,InnoDB利用不同的锁策略支持不同隔离级别。MySQL中有四种隔离级别,分别是读未提交(READ UNCOMMITTED),读已提交(READ COMMITTED),可重复读(REPEATABLE READ)以及串行化(SERIALIZABLE)。

隔离级别脏读不可重复读幻读
READ UNCOMMITTED - 读未提交YesYesYes
READ COMMITTED - 读已提交NoYesYes
REPEATABLE READ - 可重复读NoNoYes
SERIALIZABLE - 串行化NoNoNo

InnoDB并发控制

MVCC特性

InnonDB是一个支持行锁的存储引擎,为了提供更好支持的并发,使用了非锁定读,不需要等待访问数据上的锁释放,而是读取行的一个快照,该方法是通过InnonDB MVCC特性实现的。

MVCC是 Multi-Version Concurrency Control 的简称,即多版本并发控制,作用是让事务在并行发生时,在一定隔离级别前提下,可以保证在某个事务中能实现一致性读,也就是该事务启动时根据某个条件读取到的数据,直到事务结束时,再次执行相同条件,还是读到同一份数据,不会发生变化。

MVCC的好处
读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,可以增加系统的并发性能。

在MVCC中,有两种读操作:快照度和当前读。
在这里插入图片描述

MVCC快照

MVCC内部使用的一致性读快照称为Read View,在不同的隔离级别下,事务启动时或者SQL语句开始时,看到的数据快照版本可能也不同,在REPEATABLE READ、READ COMMITTED隔离级别下会用到 Read view。
在这里插入图片描述
InnoDB 里面每个事务有一个唯一的事务ID,称为Transaction ID,它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。而每行数据都有多个版本。每次事务更新数据的时候,都会生成一个新的数据版本Read View,并且把Transaction ID赋值给这个数据版本的事务 ID,标记为 row_trx_id。同时旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它,数据表中的一行记录,其实可能有多个数据版本 ,每个版本有自己的 row_trx_id

InnoDB行格式
目前InnoDB默认的行格式Dynamic,是Compat格式的增强版,记录头结构信息占用5个字节,事务ID和回滚指针分别占用6和7个字节,行格式如下:
在这里插入图片描述
记录头结构

项目大小(bit)描述
()1Unknown
()1Unknown
deleted_flag1数据行删除标记
min_rec_flag1=1如果该记录被预先被定义为最小的记录
n_owned4拥有的记录数
heap_no13索引堆中该条记录的排序位置
record_type3记录类型;000:普通,001:B+树叶子节点,010:伪列Infinum,011:Supernum,1xx:保留
next_record16page中下一条记录的相对位置
Transaction ID48记录中的事务ID,固定6个字节
Rollback Pointer56回滚指针,固定7个字节

数据行存储

#创建表
mysql> create table store_users (id int not null auto_increment primary key comment '主键id',name varchar(20) not null default '' comment '姓名');
# 查看表状态信息
mysql> show table status like 'store_users'\G
     Row_format: Dynamic    #默认行格式为Dynamic
           Rows: 0          #行数
 Avg_row_length: 0          #平均行长度
    Data_length: 16384      #初始化段大小16K
    
#开启事务,插入数据
mysql> begin;
mysql> insert into store_users values(null, 'aaaaa'),(null, 'bbbbb');
#查看InnoDB分配的事务ID
mysql> select trx_id from information_schema.innodb_trx\G
trx_id: 8407246  #事务ID

分析表的行头信息以及隐藏的事务ID和回滚指针。

# 用Linux下的工具hexdump进行分析
$ hexdump -C -v /usr/local/var/mysql/test/store_users.ibd > store_users.txt 
$ vi store_users.txt
00010060  02 00 1b 69 6e 66 69 6d  75 6d 00 03 00 0b 00 00  |...infimum......|
00010070  73 75 70 72 65 6d 75 6d  05 00 00 10 00 1c 80 00  |supremum........|
00010080  00 01 00 00 00 80 48 ce  83 00 00 01 d8 01 10 61  |......H........a|   #Record Header信息
00010090  61 61 61 61 05 00 00 18  ff d6 80 00 00 02 00 00  |aaaa............|
000100a0  00 80 48 ce 83 00 00 01  d8 01 1d 62 62 62 62 62  |..H........bbbbb|
000100b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

00010070 73 75 70 72 65 6d 75 6d 05 00 00 10 00 1c 80 00 |supremum…|
00010080 00 01 00 00 00 80 48 ce83 00 00 01 d8 01 1061 |…H…a|
00010090 61 61 61 61 05 00 00 18 ff d6 80 00 00 02 00 00 |aaaa…|

  • 0表示变长字段长度,只有一个varchar(20)没有超过256字节,且没有NULL值。
  • 00代表NULL标志位,第一行没有为NULL数据。
  • 字符a的十六进制是61,即61 61 61 61 61代表的是字段值aaaaa
  • 00 00 00 80 48 ce 6个字节就是Transaction ID,转换成十进制8407246,正是上面information_schema.innodb_trx.trx_id列的值,trx_id: 8407246
  • 83 00 00 01 d8 01 10 7个字节是Rollback Pointer。
  • 1c 80 00 00 01 是5个字节,代表Record Header信息。

隔离级别与快照

REPEATABLE READ

默认的隔离级别,一致读快照(Read View)是在第一次SELECT发起时建立,之后不会再发生变化。如果在同一个事务中发出多个非 锁定SELECT语句,那么这些SELECT语句在事务提交前返回的结果是一致的。
在这里插入图片描述
在REPEATABLE READ隔离级别下快照Read View不是事务发起时创建,而是在第一个SELECT发起后创建。
在这里插入图片描述

READ COMMITTED

在READ COMMITTED读已提交下,一致读快照(Read View)是在每次SELECT后都会生成最新的Read View,即每次SELECT都能读取到已COMMIT的数据,就会存在不可重复读、幻读 现象。
在这里插入图片描述

Undo回滚段

在这里插入图片描述
当开启事务执行更新语句(insert/update/deeldte),会经过Server层的处理生成执行计划,然后调用存储引擎层接口去读写数据,用户没有触发COMMIT或ROLLBACK之前,这些Uncommitted Data的数据称为前镜像(Post Image),数据存储在Undo Log,以便用户回滚或者MySQL Server Crash的恢复,同时Undo Log是循环覆盖使用。

#开启事务,更新账户余额,不提交事务。
mysql> start transaction;
mysql> update account set balance = 100000 where account_no = 10001;
Rows matched: 1  Changed: 1  Warnings: 0

上面在REPEATABLE READ隔离级别下,开启一个事务,做update更新操作,不提交事务,通过show engine innodb status\G查看undo情况。

Trx id counter 8407258
Purge done for trx's n:o < 8407257 undo n:o < 0 state: running but idle
History list length 33
......
---TRANSACTION 8407257, ACTIVE 154 sec
2 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1

Trx id counter 8407258当前的事务ID,undo log entries 1使用了的undo entries,ACTIVE 154 sec事务持续时间,事务commit后,会调用Purge Thread把undo中的老数据清理掉。

回滚记录

  • insert:反向操作是delete,undo里记录的是delete相关信息,存储主键id即可。
  • udpate:反向操作是update,undo里记录的是update前的相关数据。
  • delete:反向操作是insert,undo里记录的是insert values(……)相关的记录。

从这里可以知道,更新操作占用Undo空间的大小排序如下:
delete > update > insert

所以不建议物理delete删除数据,会产生大量的Undo Log,Undo快被写满就会发生切换,在次期间会有大量的IO操作,导致业务的DML都会变得很慢。

一致性读

MySQL官方文档对一致读的描述
在这里插入图片描述
读操作基于某个时间点得到一份那时的数据快照,而不管同时其他事务对数据的修改。查询过程中,若其他事务修改了数据,那么就需要从 undo log中获取旧版本的数据。这么做可以有效避免因为需要加锁(来阻止其他事务同时对这些数据的修改)而导致事务并行度下降的问题。

在可重复读(REPEATABLE READ,简称REPEATABLE READ)隔离级别下,数据快照版本是在第一个读请求发起时创建的。在读已提交(READ COMMITTED,简称READ COMMITTED)隔离级别下,则是在每次读请求时都会重新创建一份快照。

一致性读是InnoDB在REPEATABLE READ和READ COMMITTED下处理SELECT请求的默认模式。由于一致性读不会在它请求的表上加锁,其他事务可以同时修改数据不受影响。
在这里插入图片描述
一行数据有多个版本,每个数据版本有自己的trx_id,每个事务或者查询通过trx_id生成自己的一致性视图。普通select语句是一致性读,一致性读会根据row trx_id和一致性视图确定数据版本的可见性,图中UR1,UR2就是undo,存储在Undo Log中,每次查询时根据当前data page和 Undo page构造出一致性数据页(Consistent Read Page),通过读取CR Page将数据返回给用户。

总结

MySQL事务,快照,MVCC以及Undo,虽然这些东西比较抽象,但是搞清楚这些东西是一件很有意义的事,能够帮助我们更好的理解和使用MySQL,也可以把这种设计思想用在自己业务系统中。其中Undo在MySQL中的作用很重要,它是MVCC能够快速创建快照基础,支撑系统的高并发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/35783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Eureka(注册中心)

1.SpringCloud快速开发入门 1.1 搭建和配置一个服务提供者 我们知道&#xff0c;SpringCloud构建微服务是基于SpringBoot开发的。 1、 创建一个SpringBoot工程&#xff08;SpringBoot的Web工程&#xff09;&#xff0c;并且添加SpringBoot的相关依赖&#xff1b; 2、 创建服…

Linus 文件处理(二)

目录 一、前言 二、标准 I/O 库 1、fopen 2、fread 3、fwrite 4、fclose 5、 fflush 6、fseek 7、fgetc, getc, and getchar 8、fputc, putc, and putchar 9、fgets and gets 三、文件和目录维护 1、chmod 2、chown 3、unlink, link, and symlink 4、mkdir and…

Anaconda3安装与配置教程(2022.11)

&#xff08;1&#xff09;首先前往 Anaconda 官网&#xff1a;Anaconda&#xff0c;下载安装文件&#xff1a; &#xff08;2&#xff09;安装时只需要注意设置好安装路径即可&#xff0c;本文安装路径为&#xff1a;D:\Anaconda3&#xff0c;安装好后打开开始菜单能看到如下启…

Java集合框架详解(四)Map接口、HashMap类、LinkedHashMap类

一、Map接口 Map接口的特点&#xff1a; &#xff08;1&#xff09;映射键值对的形式&#xff08;key和value&#xff09;&#xff1b; &#xff08;2&#xff09;Map集合中&#xff0c;key是不能重复的&#xff0c;value是可以重复的&#xff1b; &#xff08;3&#xff09;…

电脑视频压缩软件哪个好?分享:一款支持上千种视频格式压缩神器

为了追求高清画质&#xff0c;现在的视频文件越来越大了。超大的文件虽然可以给人们带来更好的视觉效果&#xff0c;也给传输和发送带来了很大的麻烦。那有没有一款电脑视频压缩软件解决视频无损压缩问题呢&#xff1f;答案是肯定的&#xff01; 当前&#xff0c; 视频压缩已经…

自动控制系统实验总结

目录 自动控制系统实验总结 稳定性 稳态精准 动态过程 自动控制系统实验总结 自动控制系统是否能很好地工作,是否能精确地保持被控量按照预定的要求规律变化这取决于被控对象和控制器及各功能元器件的特性参数是否设计得当。 在理想情况下,控制系统的输出量…

DM-微皮恩

DM_VPN 动态多站点&#xff0c;Hub-spoken 总分站点的VPN架构&#xff0c;常用于总分之间的结构。 一、DMVPN的四个组件 1. MGRE 2. NHRP(下一跳地址解析协议) 3. Dynamic Routing Protocl 4. IPsec VPN 二、MGRE 1.配置(多点GRE隧道) interface tunnel 0 #进入隧道接口…

FPGA时序约束(一)基本概念入门及简单语法

文章目录一、建立时间和保持时间是什么&#xff1f;二、时序分析分类三、时钟约束方法3.1 时钟约束3.2 输入延时约束3.3输出延时约束3.4时序例外四、时序约束语法补充文章目前大部分参考明德扬时序约束&#xff0c;只是一个学习总结&#xff0c;侵权删 原文链接&#xff1a;FPG…

【跨境电商】如何创建吸引人的免费送货电子邮件营销

关键词&#xff1a;跨境电商、电子邮件营销、免费送货 除了用于接触新受众的新策略外&#xff0c;电子邮件营销仍然是推动更多电子商务网站转化的最有效选择之一。在大多数情况下&#xff0c;电子邮件用于推销新产品&#xff0c;通知客户有关销售、交易和季节性活动的信息&…

域名服务:域名迁移

摘要&#xff1a;这里记录一下西部数据域名的迁移步骤&#xff0c;迁移的域名为从西部数据一个账号迁移至另一个西数数据账号。域名的实体认证为同一主体&#xff0c;迁移后无需重新备案。此处为一家公立医院。 相关文章 域名服务&#xff1a;西部数据域名迁移阿里云Symantec免…

leetcode337打家劫舍3刷题打卡

337. 打家劫舍 III - 力扣&#xff08;Leetcode&#xff09; 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个…

索引生命周期管理ILM看完不懂你找我

阅读完本文你可以学到什么是索引生命周期管理&#xff0c;各个阶段可以做的操作以及如何使用索引模版使用索引生命周期策略&#xff0c;下面就跟我一起来吧 基础理论篇 索引生命周期管理&#xff08;ILM&#xff09;是一种可以让我们随着时间推移自动化的管理索引的一种方式。我…

常见APP攻击方法 以及防御方法介绍(移动安全)

一、常见APP攻击风险以及防御方法介绍 二、APP攻击以及防御方法 案例讲解 1、基于组件攻击 - 应用劫持 用户打开安卓手机上的某一应用&#xff0c;这时&#xff0c;恶意软件侦测到用户的这一动作&#xff0c;如果立即弹出一个与该应用类似的界面&#xff0c;拦截了合法的应用…

logcat日志文件分析

3:显示时间戳日志 adb logcat -v time > d:\文件\log.txt 日志文件分析 输出的日志格式由5部分组成 1:写下日志的时间 2:优先级&#xff0c;日志优先级从低到高分以下几种 v -verbose 最低级别&#xff0c;开发调试中的一些详细信息&#xff0c;仅在开发中使用&#…

动静分离LNMP

目录 安装LNMP 搭建wordpress 搭建WeCenter 根据需求实现动静分离&#xff0c;当客户端访问nginx网站服务时&#xff0c;静态网页nginx本机反馈&#xff0c;动态网页访问PHP&#xff0c;所以需要在nginx服务器中部署论坛后需要拷贝到PHP服务器中。但是如果有NFS或GFS服务器时可…

【数据结构】堆的实现(简单易懂,超级详细!!!)

目录 1、堆的概念及结构 概念 规律 2、堆的实现 2.1结构设计 2.2接口实现 2.3 初始化 2.4堆的向下调整算法 主要思想 涉及问题 代码实现 2.5建堆 思想 代码实现 建堆的时间复杂度 2.6 堆的向上调整算法 主要思想 ​涉及问题 代码实现 2.7 插入 2.8删除…

【HDU No. 2586】 树上距离 How far away ?

【HDU No. 2586】 树上距离 How far away &#xff1f; 杭电 OJ 题目地址 【题意】 有n 栋房屋&#xff0c;由一些双向道路连接起来。 每两栋房屋之间都有一条独特的简单道路&#xff08;“简单”意味着不可以通过两条道路去一个地方&#xff09;。人们每天总是喜欢这样问&a…

CUDA——向量化内存

许多 CUDA 内核受带宽限制&#xff0c;新硬件中触发器与带宽的比率增加导致更多带宽受限内核。 这使得采取措施缓解代码中的带宽瓶颈变得非常重要。 在本文中&#xff0c;我将向您展示如何在 CUDA C/C 中使用矢量加载和存储来帮助提高带宽利用率&#xff0c;同时减少执行指令的…

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台

【附源码】计算机毕业设计JAVA疫情社区志愿者组织的资源管理平台 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#…

Rust权威指南配套手把手笔记

Rust权威指南配套手把手笔记 持续更新ing 共20章&#xff0c;110小节 P1 1.1 - 简介 06:46 P2 1.2 - 安装 Rust 03:18 P3 1.3 - Hello World 04:11 P4 1.4 - Hello Cargo 07:49 P5 2.1 - 猜数游戏&#xff1a;一次猜测 11:47 P6 2.2 - 猜数游戏&#xff1a;生成神秘数字 …