事务01之事务机制

news2025/2/2 3:37:39

事务机制

文章目录

  • 事务机制
    • 一:ACID
      • 1:什么是ACID
      • 2:MySQL是如何实现ACID的
    • 二:MySQL事务机制综述
      • 1:手动管理事务
      • 2:事务回滚点
      • 3:事务问题和隔离机制(面试)
        • 3.1:事务问题
          • 3.1.1:脏读问题
          • 3.1.2:不可重复读
          • 3.1.3:幻读问题
          • 3.1.4:脏写问题
        • 3.2:四大隔离级别
          • 3.2.1:读未提交级别(RU)
          • 3.2.2:读已提交(RC)
          • 3.2.3:可重复读级别(RR)
          • 3.2.4: 可序列化/串行化
        • 3.3:事务隔离机制的命令
    • 三:事务实现原理
      • 1:一条SQL的事务机制
      • 2:多条SQL的事务机制
      • 3:事务的恢复机制

一:ACID

1:什么是ACID

在这里插入图片描述

2:MySQL是如何实现ACID的

原子性: -> undoLog

  • 主要依靠undo.log日志实现,即在事务失败时执行回滚。
  • undo.log日志会记录事务执行的sql,当事务需要回滚时,通过反向补偿回滚数据库状态

持久性:-> redoLog

  • 主要依靠redo.log日志实现。
  • 首先,mysql持久化通过缓存来提高效率,即在select时先查缓存,再查磁盘;在update时先更新缓冲,再更新磁盘。
  • 但由于缓存断电就没了,所以需要redo.log日志。
  • 在执行修改操作时,sql会先写入到redo.log日志,再写入缓存中。这样即使断电,也能保证数据不丢失,达到持久性

在这里插入图片描述

隔离性:-> 锁 + mvcc

  • 多线程时多事务之间互相产生了影响,要避免这个影响,那就加锁。
  • mysql的锁有表锁,行锁,间隙锁写。写操作通过加锁实现隔离性,渎操作通过MVCC实现

一致性:-> 上面三个为的就是保证一致性

  • 就是事务再执行的前和后数据库的状态都是正常的,表现为没有违反数据完整性,参照完整性和用户自定义完整性等等。
  • 而上面三种特性就是为了保证数据库的有一致性

二:MySQL事务机制综述

1:手动管理事务

在MySQL中,提供了一系列事务相关的命令,如下:

  • start transaction | begin | begin work:开启一个事务
  • commit:提交一个事务
  • rollback:回滚一个事务
-- 开启一个事务
start transaction;

-- 第一条SQL语句
-- 第二条SQL语句
-- 第三条SQL语句

-- 提交或回滚事务
commit || rollback;

事务是基于当前数据库连接而言的,而不是基于表,一个事务可以由操作不同表的多条SQL组成,这句话什么意思呢?

在这里插入图片描述
上面画出了两个数据库连接,假设连接A中开启了一个事务,那后续过来的所有SQL都会被加入到一个事务中

也就是说图中连接A,后面的SQL2、SQL3、SQL4、SQL5这四条都会被加入到一个事务中,只要在未曾收到commit/rollback命令之前,这个连接来的所有SQL都会加入到同一个事务中

因此开启事务后一定要做提交或回滚处理。

在连接A中开启事务,是不会影响连接B的:事务是基于当前数据库连接的,每个连接之间的事务是具备隔离性的

🎉 可视化工具中,新建一个查询时,本质上它就是给你建立了一个数据库连接,每一个新查询都是一个新的连接

在这里插入图片描述
单个语句会被认为是单个事务,并且开启了默认的自动提交事务,所以一条条语句修改了,其他连接立即可见

-- 查看 自动提交事务 是否开启
SHOW VARIABLES LIKE 'autocommit';

-- 关闭或开启自动提交
-- [0/ON]是相同的意思,表示开启自动提交,[1/OFF]则表示关闭自动提交。
SET autocommit = 0|1|ON|OFF;

2:事务回滚点

假设目前有一个事务,由很多条SQL组成,我想让其中一部分执行成功后,就算后续SQL执行失败也照样提交,这样可以做到吗?

从前面的理论上来看,一个事务要么全部执行成功,要么全部执行失败,似乎做不到啊,但实际上是可以做到的,这里需要利用事务的回滚点机制。

事务回滚点的概念:

在某些SQL执行成功后,但后续的操作有可能成功也有可能失败

不管成功亦或失败,你都想让前面已经成功的操作生效时,此时就可在当前成功的位置设置一个回滚点。

当后续操作执行失败时,就会回滚到该位置,而不是回滚整个事务中的所有操作,这个机制则称之为事务回滚点。
在这里插入图片描述
在MySQL中提供了两个关于事务回滚点的命令:

  • savepoint point_name:添加一个事务回滚点
  • rollback to point_name:回滚到指定的事务回滚点
-- 先查询一次用户表
SELECT * FROM zz_users;

-- 开启事务
start transaction;

-- sql1: 修改 ID=4 的姓名为:黑熊
update zz_users set user_name = "张三" where user_id = 4;

-- 添加一个事务回滚点:update_name
savepoint update_name;

-- sql2: 删除 ID=1 的行数据
delete from zz_users where user_id = 1;

-- 如果sql2失败了,回滚到 update_name 这个事务点
rollback to update_name;
-- 再次查询一次数据
SELECT * FROM zz_users;
-- 提交事务
COMMIT;

⚠️ 回滚到事务点后不代表着事务结束了,只是事务内发生了一次回滚,如果要结束当前这个事务,还依旧需要通过commit|rollback;命令处理。

3:事务问题和隔离机制(面试)

上面说,一个连接的事务并不会影响其他连接,而这个是通过事务隔离机制实现的

在MySQL中,事务隔离机制分为了四个级别:

  • Read uncommitted/RU:读未提交
  • Read committed/RC:读已提交
  • Repeatable read/RR:可重复读
  • Serializable:序列化/串行化

上述四个级别,越靠后并发控制度越高,也就是在多线程并发操作的情况下,出现问题的几率越小,但对应的也性能越差

MySQL的事务隔离级别,默认为第三级别:Repeatable read(RR)可重复读

想要真正理解这几个隔离级别,得先明白几个因为并发操作造成的问题。

事务是基于数据库连接的,数据库连接本身会有一条工作线程来维护,也就是说事务的执行本质上就是工作线程在执行

因此所谓的并发事务也就是指多条线程并发执行。

3.1:事务问题
3.1.1:脏读问题

脏读的意思是指一个事务读到了其他事务还未提交的数据(未提交的数据->脏数据)

也就是当前事务读到的数据,由于还未提交,因此有可能会回滚

在这里插入图片描述
在个图中,事务A先扣减了库存,然后事务回滚时又加了回去,但连接2已经将扣减后的库存数量读回去操作了,这个过程就被称为数据库脏读问题。

这个问题很严重,会导致整个业务系统出现问题,数据最终错乱。

3.1.2:不可重复读

不可重复读问题是指在一个事务中,多次读取同一数据,先后读取到的数据信息不一致
在这里插入图片描述

3.1.3:幻读问题

幻读是指:另外一个事务在第一个事务要处理的目标数据范围之内新增了数据,然后先于第一个事务提交造成的问题

在这里插入图片描述

3.1.4:脏写问题

多个事务一起操作同一条数据,例如两个事务同时向表中添加一条ID=88的数据,此时就会造成数据覆盖,或者主键冲突的问题

这个问题也被称之为更新丢失问题。

在这里插入图片描述

3.2:四大隔离级别

既然有上面的问题,那这些问题该怎么解决呢?

其实四个事务隔离级别,解决的实际问题就是这三个,因此一起来看看各级别分别解决了什么问题:

事务隔离级别脏读不可重复读(修改)幻读(增删)
读未提交read-uncommitted RU会造成会造成会造成
读已提交read-committed RC不会造成会造成会造成
可重复读repeatable-read RR不会造成不会造成会造成
串行化serializable不会造成不会造成不会造成

MySQL InnoDB存储引擎默认的事务隔离级别是可重复读RR

MySQL MyISAM存储引擎默认的事务隔离级别是读已提交RC

MySQL默认是处于第三级别的,可以通过如下命令查看目前数据库的隔离级别:

-- 查询方式①
SELECT @@transaction_isolation;
-- 查询方式②
show variables like '%transaction_isolation%';

在这里插入图片描述
其实数据库不同的事务隔离级别,是基于不同类型、不同粒度的锁实现的,因此想要真正搞懂隔离机制,还需要弄明白MySQL的锁机制[后面说]

3.2.1:读未提交级别(RU)

设计思路:写互斥锁

这种隔离级别是基于写互斥锁实现的,当一个事务开始写某一个数据时,另外一个事务也来操作同一个数据

此时为了防止出现问题则需要先获取锁资源,只有获取到锁的事务,才允许对数据进行写操作

同时获取到锁的事务具备排他性/互斥性,也就是其他线程无法再操作这个数据。

可以解决的问题:只能解决脏写问题

这个级别中,写同一数据时会互斥,但读操作却并不是互斥的

也就是当一个事务在写某个数据时,就算没有提交事务,其他事务来读取该数据时,也可以读到未提交的数据

因此就会导致脏读、不可重复读、幻读一系列问题出现。

由于在这个隔离级别中加了「写互斥锁」,因此不会存在多个事务同时操作同一数据的情况,因此这个级别中解决了脏写问题

3.2.2:读已提交(RC)

设计思路:写互斥锁 + MVCC

在这个隔离级别中,对于写操作同样会使用「写互斥锁」,也就是两个事务操作同一数据时,会出现排他性

对于读操作则使用了一种名为MVCC多版本并发控制的技术处理

也就是有事务中的SQL需要读取当前事务正在操作的数据时,MVCC机制不会让另一个事务读取正在修改的数据,而是读取上一次提交的数据(老数据)

可以解决的问题:可以解决脏写和脏读

在这个隔离级别中,基于同一条数据而言,对于写操作会具备排他性,对于读操作则只能读已提交事务的数据,不会读取正在操作但还未提交的事务数据

所以RC可以解决脏写和脏读

为了理解还是简单的说一下其过程

同样有两个事务A、B。事务A的主要工作是负责更新ID = 1的这条数据,事务B中则是读取ID = 1的这条数据。

此时当A正在更新数据但还未提交时,事务B开始读取数据

此时MVCC机制则会基于表数据的快照创建一个ReadView,然后读取原本表中上一次提交的老数据。

然后等事务A提交之后,事务B再次读取数据,此时MVCC机制又会创建一个新的ReadView,然后读取到最新的已提交的数据

此时事务B中两次读到的数据并不一致,因此出现了不可重复读问题。

在这里插入图片描述
🎉 MVCC后面会详细说

3.2.3:可重复读级别(RR)

设计思路:写互斥锁 + MVCC[这个和RC的MVCC有不同之处]

在这个隔离级别中,主要就是解决上一个级别中遗留的不可重复读问题

但MySQL依旧是利用MVCC机制来解决这个问题的,只不过在这个级别的MVCC机制会稍微有些不同。

  • 在RC中,一个事务中每次查询数据时,都会创建一个新的ReadView,然后读取最近已提交的事务数据,因此就会造成不可重复读的问题
  • 在RR中,则不会每次查询时都创建新的ReadView,而是在一个事务中,只有第一次执行查询会创建一个ReadView

在这个事务的生命周期内,所有的查询都会从这一个ReadView中读取数据

从而确保了一个事务中多次读取相同数据是一致的,也就是解决了不可重复读问题。

然而,RR依旧解决不了幻读的问题,后面说Next-key + 行锁的时候会解释RR如何解决幻读问题
在这里插入图片描述

3.2.4: 可序列化/串行化

“给老子一个个的执行,什么并发问题就都不存在了”

这个隔离级别是最高的级别,处于该隔离级别的MySQL绝不会产生任何问题

因为从它的名字上就可以得知:序列化意思是将所有的事务按序排队后串行化处理

也就是操作同一张表的事务只能一个一个执行,事务在执行前需要先获取表级别的锁资源,拿到锁资源的事务才能执行

其余事务则陷入阻塞,等待当前事务释放锁。

在这里插入图片描述
但这种隔离级别会导致数据库的性能直线下降,毕竟相当于一张表上只能允许单条线程执行了,虽然安全等级最高,可以解决脏写、脏读、不可重复读、幻读等一系列问题,但也是代价最高的,一般线上很少使用。

3.3:事务隔离机制的命令
-- 方式①:查询当前数据库的隔离级别
SELECT @@transaction_isolation;
-- 方式②:查询当前数据库的隔离级别
show variables like '%transaction_isolation';

-- 设置隔离级别为RU级别(当前连接生效)
set transaction isolation level read uncommitted;
-- 设置隔离级别为RC级别(全局生效)
set global transaction isolation level read committed;

-- 设置隔离级别为RR级别(当前连接生效)
-- 这里和上述的那条命令作用相同,是第二种设置的方式
set transaction_isolation = 'repeatable-read';
-- 设置隔离级别为最高的serializable级别(全局生效)
set global.transaction_isolation = 'serializable'; 

三:事务实现原理

1:一条SQL的事务机制

MySQL默认开启事务的自动提交(autocommit = on),并且将一条SQL视为一个事务。

那MySQL在何种情况下会将事务自动提交呢?什么情况下又会自动回滚呢?

想要弄明白这个问题,首先得回顾一下三个日志:undo-logredo-logbin-log

  • undo-log:主要记录SQL的撤销日志,比如目前是insert语句,就记录一条delete日志。
  • redo-log:记录当前SQL归属事务的状态,以及记录修改内容和修改页的位置。
  • bin-log:记录每条SQL操作日志,主要是用于数据的主从复制与数据恢复/备份。

在写SQL执行记录的三个日志中,bin-log暂且不需要关心,这个跟事务机制没关系

重点是undo-logredo-log这两个日志,其中最重要的是redo-log这个日志。

redo-log是一种WAL(Write-ahead logging)预写式日志

在数据发生更改之前会先记录日志,也就是在SQL执行前会先记录一条prepare状态的日志,然后再执行数据的写操作

MySQL是基于磁盘的,但磁盘慢,因此MySQL-InnoDB引擎中不会直接将数据写入到磁盘文件中,而是会先写到BufferPool缓冲区

当SQL被成功写入到缓冲区后,紧接着会将redo-log日志中相应的记录改为commit状态,然后再由MySQL刷盘机制去做具体的落盘操作。

因为默认情况下,一条SQL会被当成一个事务,数据写入到缓冲区后,就代表执行成功,会自动修改日志记录为commit状态,后续由后台线程执行刷盘动作。

在这里插入图片描述

2:多条SQL的事务机制

多条SQL就要手动的声明事务和提交操作了

-- 开启事务
start transaction;
-- 修改 ID=4 的姓名为:张三(原本user_name = 1111)
update zz_users set user_name = "张三" where user_id = 4;
-- 删除 ID=1 的行数据
delete from zz_users where user_id = 1;
-- 提交事务
commit;
  1. 当MySQL执行时,碰到start transaction;的命令时,会将后续所有写操作全部先关闭自动提交机制,也就是后续的所有写操作,不管有没有成功都不会将日志记录修改为commit状态。
  2. 先在redo-log中为第一条SQL语句,记录一条prepare状态的日志,然后再生成对应的撤销日志并记录到undo-log中,然后执行SQL,将要写入的数据先更新到缓冲区。
  3. 再对第二条SQL语句做相同处理,如果有更多条SQL则逐条依次做相同处理…

在这里插入图片描述

3:事务的恢复机制

bufferPool刷入磁盘的时候宕机了怎么办

对于这个问题呢实际上并不需要担心,因为前面聊到过redo-log是一种预写式日志,会先记录日志再去更新缓冲区中的数据

所以就算缓冲区的数据未被刷写到磁盘,在MySQL重启时,依旧可以通过redo-log日志重新恢复未落盘的数据,从而确保数据的持久化特性。

在这里插入图片描述

记录日志的时候宕机了怎么办?

先明确一点:只要进入bufferPool,就说明已经执行成功了,redo-log就有记录,此时会返回写入成功的提示,即使这时候数据库宕机了也没事,此时只是没有落盘而言,所以MySQL重启后只需要再次落盘即可。

如果在记录日志的时候MySQL宕机了,这代表着SQL都没执行成功,SQL没执行成功的话,MySQL也不会向客户端返回任何信息

因为MySQL一直没返回执行结果,因此会导致客户端连接超时,而一般客户端都会有超时补偿机制的,比如会超时后重试

如果MySQL做了热备/灾备,这个重试的时间足够MySQL重启完成了,因此用户的操作依旧不会丢失(对于补偿机制,在各大数据库连接池中是有实现的)。

没做热备/灾备,这时候记录日志的时候宕机了怎么办?

如果是这样的情况,那就只能说是寄了!!!毕竟MySQL挂了一直不重启,不仅仅当前的SQL会丢失,后续平台上所有的用户操作都会无响应

这属于系统崩溃级别的灾难了,因此只能靠完善系统架构来解决。

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

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

相关文章

NX/UG二次开发—CAM—快速查找程序参数名称

使用UF_PARAM_XXX读取或设置参数时,会发现程序中有一个INT类型参数param_index,这个就是对应程序中的参数,比如读取程序余量,则param_index = UF_PARAM_STOCK_PART,读取程序的加工坐标系则param_index = UF_PARAM_MCS等等。 你需要读取什么参数,只要只能在uf_param_indic…

X86路由搭配rtl8367s交换机

x86软路由,买双网口就好。或者单网口主板,外加一个pcie千兆。 华硕h81主板戴尔i350-T2双千兆,做bridge下载,速度忽高忽低。 今天交换机到货,poe供电,还是网管,支持Qvlan及IGMP Snooping&#xf…

【LLM-agent】(task1)简单客服和阅卷智能体

note 一个完整的agent有模型 (Model)、工具 (Tools)、编排层 (Orchestration Layer)一个好的结构化 Prompt 模板,某种意义上是构建了一个好的全局思维链。 如 LangGPT 中展示的模板设计时就考虑了如下思维链:Role (角色) -> Profile(角色…

ZZNUOJ(C/C++)基础练习1021——1030(详解版)

目录 1021 : 三数求大值 C语言版 C版 代码逻辑解释 1022 : 三整数排序 C语言版 C版 代码逻辑解释 补充 (C语言版,三目运算)C类似 代码逻辑解释 1023 : 大小写转换 C语言版 C版 1024 : 计算字母序号 C语言版 C版 代码逻辑总结…

2025 年,链上固定收益领域迈向新时代

“基于期限的债券市场崛起与 Secured Finance 的坚定承诺” 2025年,传统资产——尤其是股票和债券——大规模涌入区块链的浪潮将创造历史。BlackRock 首席执行官 Larry Fink 近期在彭博直播中表示,代币化股票和债券将逐步融入链上生态,将进一…

基于互联网+智慧水务信息化整体解决方案

智慧水务的概述与发展背景 智慧水务是基于互联网、云计算、大数据、物联网等先进技术,对水务行业的工程建设、生产管理、管网运营、营销服务及企业综合管理等业务进行全面智慧化管理的创新模式。它旨在解决水务企业分散经营、管理水平不高、投资不足等问题。 水务…

FIDL:Flutter与原生通讯的新姿势,不局限于基础数据类型

void initUser(User user); } 2、执行命令./gradlew assembleDebug,生成IUserServiceStub类和fidl.json文件 3、打开通道,向Flutter公开方法 FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { Override void…

文件读写操作

写入文本文件 #include <iostream> #include <fstream>//ofstream类需要包含的头文件 using namespace std;void test01() {//1、包含头文件 fstream//2、创建流对象ofstream fout;/*3、指定打开方式&#xff1a;1.ios::out、ios::trunc 清除文件内容后打开2.ios:…

cf1000(div.2)

Minimal Coprime最小公倍数 输入&#xff1a; 6 1 2 1 10 49 49 69 420 1 1 9982 44353 输出&#xff1a; 1 9 0 351 1 34371 代码

【2025年数学建模美赛E题】(农业生态系统)完整解析+模型代码+论文

生态共生与数值模拟&#xff1a;生态系统模型的物种种群动态研究 摘要1Introduction1.1Problem Background1.2Restatement of the Problem1.3Our Work 2 Assumptions and Justifications3 Notations4 模型的建立与求解4.1 农业生态系统模型的建立与求解4.1.1 模型建立4.1.2求解…

jhat命令详解

jhat 命令通常与 jmap 搭配使用&#xff0c;用来分析 jmap 生成的 dump 文件&#xff0c;jhat 内置了一个微型的HTTP/HTML服务器&#xff0c;生成 dump 的分析结果后&#xff0c;可以在浏览器中查看。 命令的使用格式如下。&#xff08;其中heap-dump-file为必填项&#xff09…

FFmpeg(7.1版本)的基本组成

1. 前言 FFmpeg 是一个非常流行的开源项目,它提供了处理音频、视频以及其他多媒体内容的强大工具。FFmpeg 包含了大量的库,可以用来解码、编码、转码、处理和播放几乎所有类型的多媒体文件。它广泛用于视频和音频的录制、转换、流媒体传输等领域。 2. FFmpeg的组成 1. FFmp…

DDD - 领域驱动设计分层架构:构建可演化的微服务架构

文章目录 引言1. 什么是DDD分层架构&#xff1f;1.1 DDD分层架构的演变1.2 四层架构的起源与问题1.3 依赖倒置和五层架构 2. DDD分层架构的核心层次2.1 用户接口层&#xff08;User Interface Layer&#xff09;2.2 应用层&#xff08;Application Layer&#xff09;2.3 领域层…

主流的AEB标准有哪些?

目录 1、AEB的技术构成与工作原理 2、典型应用场景举例 3、AEB的功能分类 4、AEB系统性能评估的关键因素 5、全球AEB技术标准概览 5.1、联合国欧洲经济委员会&#xff08;UN ECE&#xff09; 5.2、美国NHTSA法规 5.3、中国标准 5.4、印度AIS 185 5.5、澳大利亚ADR法规…

开源智慧园区管理系统如何重塑企业管理模式与运营效率

内容概要 在如今快速发展的商业环境中&#xff0c;企业面临着日益复杂的管理挑战。开源智慧园区管理系统应运而生&#xff0c;旨在通过技术创新来应对这些挑战。它不仅是一个简单的软件工具&#xff0c;而是一个全面整合大数据、物联网和智能化功能的综合平台&#xff0c;为企…

decison tree 决策树

熵 信息增益 信息增益描述的是在分叉过程中获得的熵减&#xff0c;信息增益即熵减。 熵减可以用来决定什么时候停止分叉&#xff0c;当熵减很小的时候你只是在不必要的增加树的深度&#xff0c;并且冒着过拟合的风险 决策树训练(构建)过程 离散值特征处理&#xff1a;One-Hot…

【AI论文】VideoAuteur:迈向长叙事视频

摘要&#xff1a;近期的视频生成模型在制作持续数秒的高质量视频片段方面已展现出令人鼓舞的成果。然而&#xff0c;这些模型在生成能传达清晰且富有信息量的长序列时面临挑战&#xff0c;限制了它们支持连贯叙事的能力。在本文中&#xff0c;我们提出了一个大规模烹饪视频数据…

循环神经网络(RNN)+pytorch实现情感分析

目录 一、背景引入 二、网络介绍 2.1 输入层 2.2 循环层 2.3 输出层 2.4 举例 2.5 深层网络 三、网络的训练 3.1 训练过程举例 1&#xff09;输出层 2&#xff09;循环层 3.2 BPTT 算法 1&#xff09;输出层 2&#xff09;循环层 3&#xff09;算法流程 四、循…

Linux网络 | 网络层IP报文解析、认识网段划分与IP地址

前言&#xff1a;本节内容为网络层。 主要讲解IP协议报文字段以及分离有效载荷。 另外&#xff0c; 本节也会带领友友认识一下IP地址的划分。 那么现在废话不多说&#xff0c; 开始我们的学习吧&#xff01;&#xff01; ps&#xff1a;本节正式进入网络层喽&#xff0c; 友友们…

2025年大年初一篇,C#调用GPU并行计算推荐

C#调用GPU库的主要目的是利用GPU的并行计算能力&#xff0c;加速计算密集型任务&#xff0c;提高程序性能&#xff0c;支持大规模数据处理&#xff0c;优化资源利用&#xff0c;满足特定应用场景的需求&#xff0c;并提升用户体验。在需要处理大量并行数据或进行复杂计算的场景…