浅谈Mysql(三)——MySQL/InnoDB 事务隔离级别分享

主要内容

在这里插入图片描述

事务特性

• 原子性(Atomicity
• 一致性(Consistency)
• 隔离性(Isolation)
• 持久性(Durability

日志体系-更新语句的执行过程

• redo log
• binlog

事务隔离

隔离性遇见的问题

隔离级别相关技术

  • 四种隔离级别;
  • 一致性视图(read view);
  • 当前读;
  • 快照读;
  • 隔离性实现;
  • undo log;
  • 可见性分析;
  • 锁;

目标

  • 事物隔离级别 ,带有多事物演示案例,来说明种隔离级别下对脏读 可重复 患读 的不同制约;
  • redolog binlog undolog几种日志的作用;
  • mvcc实现原理 目的 以及多事物并行时案例;

一、事务特性

事务:最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)。

  • 原子性(A):事务是最小单位,不可再分(更多关注多行);
  • 一致性©:事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败;
  • 隔离性(I):事务A和事务B之间具有隔离性;
  • 持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中);

其中:

  • 持久性依赖redo log;
  • 隔离性+原子性 依赖undolog;
  • 最终保证了一致性;

二、日志体系

2.1 redo log

阅读本节之前,请先查看这篇文章:mysql日志系统之redo log和bin log

作用: 确保事务的持久性,故障恢复。

2.1.1 先写日志,再写磁盘(write-ahead logging )

  当有一条记录需要更新的时候, InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候更新就算完成了。同时, InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面。

2.1.2 redo log记录方式

  redolog的大小是固定的innodb_log_files_in_grou和innodb_log_file_size配置日志文件数量和每个日志文件大小, redo log采用循环写的方式记录,当写到结尾时,会回到开头循环写日志 。

比如可以配置:

innodb_log_files_in_group=4
innodb_log_file_size=1GB

那么整个redo log的结构,就相当于有四个文件组成,每个文件大小事1GB,从头开始写,写到末尾循环写,如下图:redolog 记录方式

2.1.3 疑问

  在同一个事务中,每当数据库进行修改数据操作时,将修改结果更新到内存后,会在redo log添加一行记录记录“需要在哪个数据页上做什么修改”,并将该记录状态置为prepare,等到commit提交事务后,会将此次事务中在redo log添加的记录的状态都置为commit状态,之后将修改落盘时,会将redo log中状态为commit的记录的修改都写入磁盘。

疑问:在prepare和commit这段时间内,发生了页分裂,原数据页的这个位置存储的已经不是要修改的这行数据了,mysql怎么重新定位?

如果在 prepare 和 commit 阶段之间发生了页分裂,而 commit 阶段之前的 redo 日志中记录的是原数据页的修改操作,MySQL >仍然能够正确地将修改应用到对应的数据页上。这是因为 redo 日志中记录了修改的位置和内容,MySQL 会根据这些信息找到>正确的数据页并应用修改。

因此,即使在 prepare 和 commit 阶段之间发生了页分裂,MySQL 通过 redo 日志的记录仍然可以保证数据的一致性。

疑问:页分裂前和页分裂后的数据都会记录在redo日志中?

 在 MySQL 中,页分裂前和页分裂后的数据都不会直接记录在 redo 日志中。Redo 日志只会记录事务所做的修改操作,而不会记录具体的数据内容。

 当页分裂发生时,MySQL 会在 redo 日志中记录相应的页分裂操作,而不是记录分裂前和分裂后的具体数据。页分裂操作包括分裂前的页信息、分裂后的新页信息以及相应的指针调整等内容。这些记录足以在恢复过程中重新执行页分裂操作,以保证数据的一致性。

 因此,在事务的 prepare 和 commit 阶段之间发生页分裂时,redo 日志中只会记录页分裂操作,而不是记录分裂前和分裂后的具体数据。通过重放 redo 日志中的页分裂操作,MySQL 可以在恢复过程中重新执行分裂操作,确保数据的正确性。

2.2 binlog日志

redo log是InnoDB引擎特有的日志,而Server层也有自己的日志,称为binlog日志(归档日志)。

  • binlog是server层实现的,意味着所有引擎都可以使用binlog日志;
  • binlog通过追加的方式写入的,可通过配置参数max_binlog_size设置每个binlog文件的大小,当文件大小大于给定值后,日志会发生滚动,之后的日志记录到新的文件上;
  • binlog有3种记录模式, statement格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有,第3种混合使用;
  • binlog常用于主从复制,数据同步等场景,京东有binlog组件,叫做binlog平台。大体场景是用于无业务侵入,数据变更发送mq同步其他业务的目的,保证了数据一致性;

2.3 redo log对比binlog

  • redo log是InnoDB引擎特有的; binlog是MySQL的Server层实现的,所有引擎都可以使用;
  • redo log是物理日志,记录的是“在某个数据页上做了什么修改”; binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”;
  • redo log是循环写的,空间固定会用完; binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志;

2.4 一条更新语句的执行过程

请查看这篇文章第4.1节:
浅谈Mysql(一)——索引、隔离级别、死锁等

三、事务隔离

3.1 四种隔离级别

  • 脏读:此情况仅会发生在: 读未提交的的隔离级别
      事务A读取了事务B还未提交的事务,事务B回滚了,事务A再次读,发现值不一样,产生了脏读。
  • 不可重复读:此情况仅会发生在:读未提交、读提交的隔离级别
      事务A先读取一行数据,事务B对这行进行修改并提交,事务A再次读取这行数据,发现数据不一样了,这就是不可重复读。
  • 幻读:此情况会回发生在:读未提交、读提交、可重复读的隔离级别
      一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。
      幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。

隔离级别与它对应的问题:

  • READ UNCOMMITTED: 出现脏读;
    可以读取未提交记录。此隔离级别,不会使用,忽略。
  • READ COMMITTED:解决了脏读,存在不可重复读;
    1.快照读避免脏读,会有不可重复读
    2.针对当前读, RC隔离级别保证对读取到的记录加锁(记录锁),存在幻读现象。
  • REPEATABLE READ:解决了不可重复读;
    1.快照读避免不可重复读,会有幻读。
    2.针对当前读, RR级别保证对读取到的记录加锁(记录锁),同时保证对读取的范围加锁,新事物的满足查询条件的记录不能够插入(间隙锁,需要等待前一事务提交才能插入),在一定程度上避免了幻读现象。但是无法完全避免幻读,比如前一事务没有锁柱另一事务的insert,而且前一事务变成当前读了新数据。
  • REPEATABLE READ:存在幻读;
    从MVCC并发控制退化为基于锁的并发控制。不区分快照读与当前读。所有的读操作均为当前读,读都加锁(S锁),写都加锁(X锁)。

SERIALIAZABLE 隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。

隔离性增高,性能降低,安全性增高, MySQL数据库默认 REPEATABLE READ。

3.2 几个概念

概念内容
一致性读视图即consistent read view,用于支持RC(Read Committed,读提交)和RR(Repeatable Read,可重复读)隔离级别的实现。
当前读读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。 示例: select … lock in share mode; select … for update ; update, insert ,delete
快照读不加锁的select操作就是快照读,即不加锁的非阻塞读; 示例: 简单的select操作(不包括 select … lock in share mode, select … for update)

事实上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。

  • “可重复读” 隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。
  • “读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候 创建的。
  • “读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;
  • “串行化”隔离级别下直接用加锁的方式来避免并行访问。

3.3 隔离性实现

阅读本节之前,请先查看这篇文章:MySQL日志系统:redo log、binlog、undo log 区别与作用

  在MySQL中,在每一次更新的时候,都会记录一条回滚操作,记录上最新的值,通过回滚操作,都可以得到前一个状态的值。

  比如一个值从1,被顺序改成了2、 3、 4,在回滚日志里面就会有类似下面的记录:
在这里插入图片描述

  • 当前值是4;
  • 不同时刻启动的事务会有不同的read-view;
  • 在视图A、 B、 C里面,这一个记录的值分别是1、 2、 4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC);
  • 即使现在有另外一个事务正在将4改成5,这个事务跟read-view A、 B、 C对应的事务是不会冲突的;

3.3.1 案例

RR级别下: 我们继续看一组案例:
开始时: status=1

session1session2session3
start transaction with consistent snapshot;start transaction with consistent snapshot;
update tbl set status =status+1 where id = 3 ;
update tbl set status =status+1 where id = 3 ;
select status from tbl where id =3 ;select status from tbl where id =3 ;
COMMIT ;COMMIT ;

问题:

  1. session2里面查询的结果, status=3
  2. session1里面查询的结果, status=1
    结论与你了解的事务隔离是否矛盾?

3.4 undo log

前文我们说过 回滚日志,何为回滚日志(undo log),在哪里?
在这里插入图片描述
  实际上:上图中三个虚线箭头,就是undo log,而且, V1, V2, V3并不是物理上真实存在的,而是每次需要的时候根据当前的版本和undo log计算出来的。

比如需要V2,就是通过V4依次执行U3, U2计算出来。

其中V,我们成为Read View(读视图)

3.4.1 undo log结构原理

在这里插入图片描述
修改数据时,先复制数据到undo log
所有历史的事务修改,会在Undo log中形成链表, undo log的链首就是最新的旧记录

3.5 可见性分析(要求理解)

上图里面,其中DB_TRX_ID 代表当前事务ID(每个事务开启,都会被分配,递增)。

前文Read View,是另一个维度,维护了一组DB_TRX_ID,组成:

  1. 已提交事务(trx_id 最小值);
  2. 当前事务(未提交事务集合);
  3. 未开始事务(目前最大trx_id+1,下次分配用)

可见性规则如下: 将要被修改的数据的最新记录中的DB_TRX_ID(即当前事务ID)取出来。规则对比:
在这里插入图片描述

  1. 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
  2. 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
  3. 如果落在黄色部分,那就包括两种情况
    a. 若 row trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见;
    b. 若 row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。

结论:

  • 一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上一个版本”;
  • 如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新的数据,它自己还是要认的;

3.5.1 可见性分析-session1分析

我们先分析session1中的status为什么等于1:
在这里插入图片描述

  1. 事务1开始前,假设系统里面只有一个活跃事务ID是99;
  2. 事务1、 2、 3的版本号分别是100、101、 102,且当前系统里只有这四个事务;
  3. 三个事务开始前, (3,1)这一行数据的row trx_id是99。

这样,
事务1的视图数组 : [99,100],
事务2的视图数组: [99,100,101],
事务3的视图数组: [99,100,101,102]。

  如果现在事务1要来读数据了,它的视图数组是[99,100]。当然了,读数据都是从当前版本读起的。所以,事务1查询语句的读数据流程是这样的:

  • 找到(3,3)的时候,判断出row trx_id=101,比高水位大,处于红色区域,不可见;
  • 接着,找到上一个历史版本,一看row trx_id=102,比高水位大,处于红色区域,不可见;
  • 再往前找,终于找到了(1,1),它的row trx_id=99,比低水位小,处于绿色区域,可见;
      这样执行下来,虽然期间这一行数据被修改过,但是事务A不论在什么时候查询,看到这行数据的结果都是一致的,所以我们称之为一致性读。

3.5.2 可见性分析- session2分析

  如果前面所说的,都是正确的,按照一致性读,事务2的update语句,结果是否有问题?
  如果事务2的视图数组是生成的,之后事务3才提交,不是应该看不见(3,2)么,那么接下来的查询怎么会是(3,3)呢?

  这里就用到了这样一条规则:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)
因此,在更新的时候,当前读拿到的数据是(3,2),更新后生成了新版本的数据3,3),这个新版本的row trx_id是101。
这里我们提到了一个概念,叫作当前读。其实,除了update语句外, select语句如果加锁,也是当前读

3.5.3 修改一下session3,立马提交改为延迟提交

此时我们修改一下session3,前边的案例是session立马提交,如果不是这样的,结果会怎么样呢?

session1session2session3
start transaction with consistent snapshot;start transaction with consistent snapshot;
update tbl set status =status+1 where id = 3 ;
update tbl set status =status+1 where id = 3 ;
select status from tbl where id =3 ;select status from tbl where id =3 ;COMMIT ;
COMMIT ;COMMIT ;

这里涉及另外一块知识 锁的概念: “两阶段锁协议”。

  两阶段锁协议:行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。锁的概念不在本次分享范畴,我们暂时知道有个这样的规则即可。

  事务3没提交,也就是说(3,2)这个版本上的写锁还没释放。而事务2是当前读,必须要读最新版本,而且必须加锁,因此就被锁住了,必须等到事务2释放这个锁,才能继续它的当前读。

四、加锁汇总

MySQL加锁,是通过在索引上加锁实现的,如果有索引 以下操作:

  1. 主键索引;
    RC、 RR给主键索引加行锁
  2. 唯一索引;
    RC、 RR给唯一索引加行锁,给对应的主键索引加行锁
  3. 普通索引;
    如果 RC,普通索引加行锁,对应的逐渐索引加行锁;
    如果RR,普通索引加行锁,以及这些行锁数据的间隙加范围锁。主键索引对应行加锁;

五、# 行、表锁、间隙锁

行、表锁、间隙锁相关知识以后会单写一篇文章来讲,暂时先读一下其他人的讲解:
MySQL行锁、表锁、间隙锁详解

六、MVCC

MVCC相关知识以后会单写一篇文章来讲,暂时先读一下其他人的讲解:
MVCC是什么?有何用?原理是什么?

七、其他

以下是一些跟本篇文章没有关系的知识,先放在这里,以后迁移。

7.1 SimpleDateFormat不是线程安全的:

  在多线程环境下使用同一个SimpleDateFormat对象时,可能会发生线程不安全的问题。这是因为在SimpleDateFormat的format方法中,会操作一个全局的Calendar对象,这个对象在不同的线程中被共享。当一个线程正在格式化一个日期时,可能会在格式化过程中被另一个线程修改。例如,如果线程A正在格式化日期,而线程B修改了Calendar对象中的时间,那么线程A在后续操作中获取的时间就会是线程B修改后的结果,这会导致数据转换和处理的不正确。1234567

  为了避免这种问题,可以在单线程环境下使用SimpleDateFormat,或者为每个线程创建不同的SimpleDateFormat实例。
拓展阅读:
高并发下的SimpleDateFormat

7.2 excel的VLOOKUP函数

略;

7.3 mysql流式查询

深入了解MySQL的流式查询机制

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Golang-Gin光速入门

安装 go get -u github.com/gin-gonic/gin初始化项目并启动服务 go mod init gin-project package mainimport "github.com/gin-gonic/gin"func main() {r : gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message"…

2.11 Python关键字(保留字)

Python关键字(保留字)一览表 保留字是Python 语言中一些已经被赋予特定意义的单词,这就要求开发者在开发程序时,不能用这些保留字作为标识符给变量、函数、类、模板以及其他对象命名。 Python 包含的保留字可以执行如下命令进行…

GRE VPN——配置实验

1,按照图示配置IP地址 r1: r2: r3: 2,在R1和R3配置默认路由使公网区域互通 [R1]ip route-static 0.0.0.0 0 100.1.1.2 R3]ip route-static 0.0.0.0 0 100.2.2.2 3,在R1和R3上配置GRE VPN,使…

追踪Aurora(欧若拉)勒索病毒,Emsisoft更新解密工具

Aurora(欧若拉)勒索病毒首次出现于2018年7月左右,加密后的文件后缀为Aurora,2018年11月,此勒索病毒的一款变种样本,加密后的文件后缀为Zorro,同时发现了此勒索病毒的一个BTC钱包地址: 18sj1xr86c3YHK44Mj2…

第二证券今日投资参考:低空经济迎利好 自动驾驶商业化提速

昨日,两市股指盘中弱势震动,午后加快下探,沪指失守3000点大关,深成指、创业板指跌超2%;到收盘,沪指跌1.26%报2993.14点,深成指跌2.4%报9222.47点,创业板指跌2.81%报1789.82点&#x…

RabbitMQ 实验消费原始队列消息, 拒绝(reject)投递死信交换机过程

如果你想通过 RabbitMQ 的死信队列功能实现消费者拒绝消息投递到死信交换机的行为,你可以按照以下步骤操作: 创建原始队列,并将其绑定到一个交换机上: export RABBITMQ_SERVER127.0.0.1 export RABBITMQ_PORT5672 export RAB…

小米汽车正式发布:开启智能电动新篇章

随着科技的不断进步,汽车产业正经历着前所未有的变革。智能电动汽车作为这一变革的重要方向,正吸引着越来越多的目光。在这个充满机遇和挑战的时代,小米汽车凭借其卓越的技术实力和深厚的市场底蕴,终于迈出了坚实的一步。今天&…

微服务demo(三)nacosfeign

一、feign使用 1、集成方法 1.1、pom consumer添加依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><version>2.2.6.RELEASE</version></dependency&…

3.28总结

1.java学习记录 1.方法的重载 重载换而言之其实就是函数名不变&#xff0c;但是其中的参数需要改变&#xff0c;可以三个方面改变&#xff08;参数类型&#xff0c;参数顺序&#xff0c;参数个数这三个方面入手&#xff0c;这样可以运用的&#xff09; 但是&#xff1a;注意…

深度学习论文: Attention is All You Need及其PyTorch实现

深度学习论文: Attention is All You Need及其PyTorch实现 Attention is All You Need PDF:https://arxiv.org/abs/1706.03762.pdf PyTorch: https://github.com/shanglianlm0525/PyTorch-Networks 大多数先进的神经序列转换模型采用编码器-解码器结构&#xff0c;其中编码器将…

IP种子是什么?理解和应用

在网络世界中&#xff0c;IP种子是一个广泛应用于文件共享和网络下载领域的概念。它是一种特殊的标识符&#xff0c;用于识别和连接到基于对等网络&#xff08;P2P&#xff09;协议的文件共享网络中的用户或节点。本文将深入探讨IP种子的含义、作用以及其在网络中的应用。 IP地…

Windows 最佳文件管理器:快速、简单、直观、自由 | 开源日报 No.175

files-community/Files Stars: 30.6k License: MIT Files 是为 Windows 构建的最佳文件管理器应用程序。该项目解决了在 Windows 上进行文件管理时的困难。 它具有以下主要功能和优势&#xff1a; 采用直观设计&#xff0c;使浏览文件变得更加简单支持标签、预览和自定义背景…

OceanBase OBCA 数据库认证专员考证视频

培训概述 OceanBase 认证是 OceanBase 官方推出的唯一人才能力认证体系&#xff0c;代表了阿里巴巴及蚂蚁集团官方对考生关于 OceanBase 技术能力的认可&#xff0c;旨在帮助考生更好地学习 OceanBase 数据库产品&#xff0c;早日融入 OceanBase 技术生态体系&#xff0c;通过由…

Intellij IDEA安装配置Spark与运行

目录 Scala配置教程 配置Spark运行环境 编写Spark程序 1、包和导入 2、定义对象 3、主函数 4、创建Spark配置和上下文 5、定义输入文件路径 6、单词计数逻辑 7、输出结果 8、完整代码&#xff1a; Scala配置教程 IDEA配置Scala&#xff1a;教程 配置Spark运行环境 …

3.Labview字符串与路径精讲(下) — 字符串及路径的用法汇总

本章讲解labview中的字符串和路径具体实践用例&#xff0c;从前面板字符串属性到后面板字符串函数应用做出详细概述&#xff0c;通过本文的学习希望大家了解到字符串及路径在labview编程中的重要地位。 本系列文章为labview 从基础到强化到精通的学习文章&#xff0c;大家可以随…

网站为什么要选择使用安全加速SCDN?

安全加速SCDN&#xff08;安全内容交付网络&#xff09;是一种网络加速服务&#xff0c;旨在提高网站和应用程序的性能和安全性。它使用专门的技术和基础设施来加速内容传输并保护网站免受网络攻击。 安全加速SCDN可以通过内容缓存、快速传输和动态路由技术来加速网站和应用程…

使用Jenkins打包时执行失败,但手动执行没有问题如ERR_ELECTRON_BUILDER_CANNOT_EXECUTE

具体错误信息如&#xff1a; Error output: Plugin not found, cannot call UAC::_ Error in macro _UAC_MakeLL_Cmp on macroline 2 Error in macro _UAC_IsInnerInstance on macroline 1 Error in macro _If on macroline 9 Error in macro FUNCTION_INSTALL_MODE_PAGE_FUNC…

LeetCode:718最长重复子数组 C语言

718. 最长重复子数组 提示 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子数组的长度 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,3,2,1], nums2 [3,2,1,4,7] 输出&#xff1a;3 解释&#xff1a;长度最长的公共子数组是 [3,…

钡铼技术R40路由器助力智能船舶航行数据实时传输与分析

钡铼技术R40路由器在智能船舶领域的应用&#xff0c;对于航行数据的实时传输与分析具有重要意义。随着航运业的不断发展和智能化水平的提升&#xff0c;船舶航行数据的及时传输和有效分析对船舶的安全、运营效率等方面至关重要。而引入钡铼技术R40路由器&#xff0c;则可以实现…

k8s1.28.8版本配置prometheus监控告警

文章目录 官方架构图组件的具体介绍kube-prometheus包含的组件简介&#xff1a;文件存储路径&#xff1a; 结构分析官网自带的一些规则自己总结流程 1-创建规则磁盘使用率报警规则 详解上面rule流程Alertmanagerg查看 2-报警接收器2.1-邮件报警修改Alertmanager配置查看现有的s…