【MySQL篇】事务管理,事务的特性及深入理解隔离级别

news2025/4/2 18:09:43

目录

一,什么是事务

二,事务的版本支持

 三,事务的提交方式

四,事务常见操作方式 

五,隔离级别

1,理解隔离性

 2,查看与设置隔离级别

3,读未提交(read uncommitted)

4,读提交(read committed)

5,可重复读(repeatable read)

 ​编辑

6,串行化(serializable)

7,总结 

六,深入理解隔离级别

MVCC的实现原理 

3个隐藏记录字段

undo日志 

模拟MVCC

Read View 

总结 

RR与RC的本质区别


一,什么是事务

先思考一个场景,在一个火车票购票系统中,西安到郑州的火车票只剩下一张,两个用户进行买票,可以想象成这两个用户对表中数据进行CURD,而如果这些CURD不加控制,会不会出现问题?

当客户端A检查到还有一张票时,将票卖掉,还没有 执行更新数据库的语句时,客户端B检查了票数,发现大于0,于是又买了一次票。然后 A将票数更新回数据库。这就出现了同一张票被买了2次的问题。

为了解决上述的问题,就需要CURD操作满足 如下的属性:

1,买票的过程需要是原子的

2,买票之间不能互相影响

3,买完票,对数据进行修改后,数据应该永久有效

4,买前和买后的状态都是确定的


 事务的概念:事务就是一组DML语句组成,这些语句在逻辑上 存在相关性,这一组DML语句要么全部成功 ,要么全部失败,是一个整体。

通俗的讲,事务就是一些SQL语句的组合。在业务层面上,为了完成某个业务,比如完成一次转账,要将一个用户的数据减100,另一个用户的数据加100,为了完成这个业务,在MySQL层面上,就需要多条SQL语句来完成。

单独的一条SQL语句之间是没有关系的,但是站在使用者的角度,这些SQL语句是 存在逻辑关系的,共同完成某个任务。这些SQL语句要么全部成功,要么全部失败。就比如转账的例子,不能使一个用户减100,后面的操作失败,导致另一个 用户没有加100.


事务就是要做或者所做的事情 ,主要用于处理操纵量大,复杂度高的数据。一个MySQL数据库,可不止一个事务在运行。在同一时刻,可能有大量的请求被包装成事务,向MySQL服务器发起事务处理请求。而每条事务至少一条SQL语句,也可能有很多条SQL语句,这样如果大家都访问同样的表数据,在不加保护的情况下,一定会存在问题。也会存在,一个事务中,一些SQL语句执行完成了,另一些SQL执行出错或者不再执行的情况,那么这种情况下该怎么办?

所以,一个完整的事务,绝对不是简单的SQL语句的集合,还需要满足如下属性:

  • 原子性:一个事务(transaction)中的所有操作,要么全部完成 ,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像 这个事务从来没有执行过。
  • 一致性:在事务开始之前和事务结束之后,数据库的完整性没有被破坏。这表示事务按照我们的预期执行的。
  • 隔离性:数据库允许多个并发事务同时对其数据进行读和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据不一致的问题。事务隔离分为不同的级别:读未提交(read uncommited),读提交(read commited),可重复读(repeatable read )和串行化(serializable)。
  • 持久性:事务结束后,对数据的修改是永久的,即便系统 故障也不会丢失。

上面的四个属性,可以简称为ACID:

原子性(Atomicity)

一致性(Consistency)

隔离性(Isolation)

持久性(Durability)

二,事务的版本支持

在MySQL中只有使用了Innodb存储引擎的数据库或表才支持事务,MyISAM不支持。

查看数据库引擎

show engines;   --表格显示

show engines\G  --行显示

 三,事务的提交方式

事务的提交方式有两种:自动提交和手动提交

查看事务提交方式

show variables like 'autocommit'

用set来改变MySQL的自动提交模式:

四,事务常见操作方式 

创建测试表 

 mysql> create table account(

mysql> id int  primary key,

mysql> name varchar(20) not null default '',

mysql> blance decimal(10,2) not null default 0.0);

演示:事务的开始与回滚 

 begin 开始一个事务,commit结束这个事务。

如果事务执行到一半,未commit,客户端崩掉,MySQL会自动回滚到开始。

如果事务执行完了,commit了,客户端崩掉,MySQL数据不受影响,已经持久化。

begin开始一个事务,需要手动commit,和MySQL是否开启自动提交无关。

单条SQL与事务的关系:单条SQL也是一个事务,被MySQL封装成一个事务。


总结:

  • 只要输入begn或者start transaction,事务便必须要通过commit提交,才会持久化,与是否支持自动提交无关(autocommit)。
  • 事务可以手动回滚,同时,当操作异常,MySQL会自动回滚。
  • 对于Innodb,每一条SQL语句,都会封装成事务 ,自动提交。(select有特殊情况 ,因为MySQL有MVCC)。

五,隔离级别

1,理解隔离性

mysql可能会被多个客户端进程(线程)进行访问,访问的方式以事务的方式进行

一个事务 可能由多条SQL语句构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶段。而所谓的原子性,就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,可以随时回滚。所以单个事务,对用户表现出来的就是原子性的。

那么在多个事务执行自己的SQL时,就有可能出现相互影响的情况。比如:多个事务访问同一张表,甚至同一行数据。

数据库中,为了保证事务执行过程中尽量不受干扰,就有了隔离性的概念。

数据库中,允许事务受不同程度的干扰,就有了隔离级别的概念。


隔离级别:

读未提交(read uncommitted):在该隔离级别下,所有事务都可以看到其他事务还没有提交的执行结果,(实际生产中不可能使用这种级别),相当于没有隔离性,会有很多并发问题,如脏读,幻读,不可重复读等问题。

读提交(read committed):该隔离级别是大多数数据库的默认隔离级别(不是MySQL的)。它满足了隔离的简单定义:一个事务只能看到其他已提交的事务所作的改变。这种隔离级别会引起不可重复读的问题,即一个事务执行时,如果多次select,会得到不同的结果。

可重复读(repeatable read):这是MySQL的默认级别,它确保同一个事务,在执行中,多次读取数据时,读到的是同一份数据。但是会有幻读的问题。

串行化(serializable):这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题,它在每个读的数据行上面加上共享锁。但是可能会导致超时和锁竞争。(这种隔离级别太极端,实际生产一般不使用)

接下来是对这4中隔离级别的现象演示。

 2,查看与设置隔离级别

查看

mysql> select @@global.transaction_isolation; --查看全局隔离级别

   

 mysql> select @@transaction_isolation;  --查看当前会话的隔离级别

会话启动时,当前会话的隔离级别会从全局隔离级别中拷贝一份。 

设置

语法:

set [session | global] transatcion isolation level [隔离级别];

设置当前会话的隔离级别

mysql> set session transaction isolation level serializable; -- 串行化

设置全局隔离级别: 

mysql> set global transaction isolation level read uncommitted;

//transaction 事务

//isolation 隔离

//level 级别

//read uncommitted 读未提交

  全局隔离级别设置好以后,需要重启终端才可以生效。 

mysql> select @@transaction_isolation;

 

3,读未提交(read uncommitted)

 现象演示:(先将当前隔离级别设置为read uncommitted)

一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读。

4,读提交(read committed)

终端Acommit之后 :

 但是,终端B事务还未结束,那么就造成了,同一个事务内,同样的读取,在不同的时间段,读到了不同的数据,这种现象,叫做不可重复读。

5,可重复读(repeatable read)

 

 两个终端A和B,如果终端A在对应事务中insert数据,在终端B的事务周期中 ,没什么影响,也符合可重复的特点。但是,一般的数据库在可重复读的时候,无法屏蔽其他事务的insert操作(为什么?因为隔离性是对数据加锁完成的,而insert待插入的数据并不存在,那么一般加锁无法屏蔽insert数据)。会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读的情况下被读出来,导致多次查找时,会多查找除出的记录,就如同产生了幻觉,这种现象叫做幻读。但是,MySQL在repeatable read隔离级别下解决了该问题。

6,串行化(serializable)

对于所有操作全部加锁,进行串行化,不会出现问题。但是效率很低,几乎不会被采用。

 

7,总结 

隔离级别越高,安全性越高,但数据库并发性越低,往往需要在两者之间找一个平衡点。

不可重复读的重点是修改和删除:同样的条件,不同的时间,发现读取到的数据不一致。

幻读的终点在于新增:同样的条件,第一次和第二次读出来的数据记录不同。

六,深入理解隔离级别

数据库并发的场景有三种:

读-读:不存在任何问题,也不需要并发控制

写-写:有线程安全问题,可能会存在更新丢失问题。

读-写:有线程安全问题,可能会造成事务隔离性问题,如脏读,幻读,不可重复读等问题。

多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发机制。

首先抛出概念:

MVCC的全称是Multi-Version Concurrency Control,即多版本并发控制。它的主要目的是在读取数据时不加锁,从而提高并发性能。传统的锁机制可能会导致读阻塞写或者写阻塞读,而MVCC通过保存数据的历史版本来让读写操作可以同时进行,避免阻塞。

首先,每个事务都有一个唯一的事务ID,这个ID是在事务开始时分配的,按照时间顺序递增,就像每一个进程都有唯一一个PID。


MVCC的实现原理 

 理解MVCC的三个前置知识:

  • 3个隐藏记录字段
  • undo日志
  • Read View

3个隐藏记录字段

  1. DB_TRX_ID:6byte,最近修改(修改/插入)的事务ID,记录创建这条记录或者修改这条记录的的事务ID。
  2. DB_ROLL_PTR:7byte,回滚指针,这想这条记录的上一个版本。(这些数据一般在undo log中)。
  3. DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,Innodb会自动以DB_ROW_ID产生一个聚簇索引。
  4. 补充:实际还有一个删除flag隐藏字段,既记录被删除并不代表真的被删除,而是flag由0置1,或由1置0。

索引特性传送门:【MySQL篇】索引特性,索引的工作原理以及索引的创建与管理_mysql索引b+树特性-CSDN博客

undo日志 

首先,要理解一个事情,MySQL将来是以服务进程的方式,在内存中运行。索引,事务,隔离性等机制,都是在内存中完成的。即在MySQL内部的相关缓冲区中,保存相关数据,完成各种判断操作,然后再合适的时候数据刷新到磁盘上。

所以,undo日志可见简单的理解成,是一段MySQL内部的缓冲区,用来保存日志数据的。

重点会在下面模拟的时候讲解。

模拟MVCC

假设现在有一个事务,事务ID为11,对上面表的数据进行修改,将name(张三)改成name(李四)。大致过程如下:

  • 事务11,因为要修改,先对这条记录加行锁。
  • 修改前,先将该行记录拷贝到undo log中,所以undo log中就有了一行数据。
  • 现在修改MySQL中的记录,将原始记录name(张三)修改为name(李四)。并且修改原始记录的隐藏字段,DB_TRX_ID为当前事务的ID11,回滚指针列DB_ROLL_PTR,里面写入放入undo log中修改前数据的地址。从而指向上一条记录,即指向上一个版本。
  • 事务11提交,释放锁。

 

假设又有一个事务,事务ID为12,要将age改为30,同样按照上述过程。

总结:当一行数据被修改时,Innodb会将当前行的当前版本复制到undo log日志中,并通过DB_ROLL_PTR形成一个版本链。这样,每次修改都会都会形成一个新版本,旧版本保留在undo log中,直到不再需要为止。

上面是以更新(update)为例,如果是删除(delete)操作呢?

其实是一样的,因为上面提到过,还有一个隐藏字段flag,删除一段记录,设置flag即可。因此也可以生成版本链。


如果是insert插入数据呢?

因为insert是插入数据,之前没有数据,那么insert也就没有历史版本。但是,一般为了回滚操作,insert后的数据也是要被放到undo log日志中的。

如果当前事务commit了,那么这个undo log的历史insert记录就可以被清空了。因为一个事务跑起来了,插入数据,这个数据之前是没有的,就注定了和该事务并发运行的事务是不会用这个数据的,所以insert形成的版本链就可以清空了。

而update和delete就不一定要被清空了,因为这条事务提交了,可能会有其他事务也要访问这条记录。


如果是select操作呢? 

select是不会对任何数据进行修改的。所以,维护select多版本是没有意义的。


select读取,应该读取到的是最新版本,还是历史版本?

当前读:读取最新的记录。增删改,都是当前读。

快照读:读取历史版本。

在多个事务进行当前读的时候,同时多个select,是需要加锁的,这就是串行化。

而对于读提交和可重复读的隔离级别,是在进行快照读,读取历史版本。

但如果是快照读,读取历史版本的话,是不受加锁限制的。也就是可以并发执行!换言之,调高了效率,这就是MVCC的意义。


但是,什么决定了select是当前读,还是快照读呢?

隔离级别。

事务是有执行前,执行中和执行后三个阶段的。begin-CURD-commit。

那么多个事务执行中,CURD是会交织子在一起的。那么,为了保证事务有先有后,就该让不同的事务看到该看到的内容,这就是隔离级别要完成的任务。这个工作就由接下来的read view实现。

Read View 

概念:

Read View 就是事务进行快照读操作时产生的读视图,在该事务执行快照读的那一刻,记录并维护当前活跃事务的ID,这个ID是自增的,所以最新的事务,ID值越大。

Read View作用:事务进行读操作的时候,确定那些数据版本对该事务可见。


 事务和Read View的关系,可以理解成进程PCB和进程地址空间的关系。让该事务看到应该看到的数据版本。

Read View在MySQL源码中,就是一个类,本质是用来进行可见性判断的。即当我们某个事务进行快照读的时候,读该记录创建一个Read View读视图,用它来判断当前哪个事务可以看见哪个版本的数据,可能是当前最新的数据,也就能是该记录的undo log日志里面的某个版本数据。


Read View类中几个重要的字段:

 m_ids;           //一张列表,用来维护Read View生成时刻,系统中活跃的事务ID

up_limit_id;    //记录m_ids列表中事务ID最小的ID

low_limit_id;  //Read View生成时刻,系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1

creator_trx_id;   //创建该事务的事务ID

 对于undo log中的数据版本链,我们在读取的时候,是能读取到每一个版本的事务ID的,即DB_TRX_ID。

所以,现在我们手里有两样东西:当前事务的Read View和数据版本链中的某一条记录的DB_TRX_ID。

所以,现在的问题就是当前事务应不应该看到当前版本记录。

总结 

MVCC实现原理:

核心组件作用
事务ID每个事务启动时,分配一个事务ID,ID是自增的
undo 日志存储数据的历史版本,形成版本链。
Read View事务进行读操作时生成,确定那些版本对该事务可见
隐藏字段每个记录包含3个隐藏字段

MVCC的优缺点:

优点缺点
非阻塞读,提高并发性能需要维护undo日志,增加存储开销
减少锁冲突,降低死锁概率旧版本数据不及时清理可能影响性能
支持快照图,实现一致性视图复杂事务可能导致版本链过长

RR与RC的本质区别

有了MVCC的认识,接下来看看mysql是如何实现RC和RR两种不同的隔离级别的。

注意:read view是事务可见性的一个类,不是事务创建出来,就会有 read view,而是当事务第一次进行快照读的时候,mysql才会生成read view。

隔离级别read view 生成时机效果
读已提交(RC)

每次查询时生成新的read view,

可能 一直在变化。

能看到其他事务提交后的修改。
可重复读(RR)

事务首次查询生成read view,

之后就不会变化了。

始终看到事务开始时的数据快照,避免不可重复读的问题。

 

RR 级别下,快照读生成 Read View 时, Read View 会记录此时所有其他活动事务的快照,这些事
务的修改对于当前事务都是不可见的。而早于 Read View 创建的事务所做的修改均是可见的。
而在 RC 级别下的,事务中,每次快照读都会新生成一个快照读 Read View, 这就是我们在 RC 级别下 的事务中可以看到别的事务提交的更新的原因。

正式因为RC每次快照读,都会形成新的read view,所以,RC才会有不可重复读的问题。

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

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

相关文章

项目实战-角色列表

抄上一次写过的代码: import React, { useState, useEffect } from "react"; import axios from axios; import { Button, Table, Modal } from antd; import { BarsOutlined, DeleteOutlined, ExclamationCircleOutlined } from ant-design/icons;const…

26_ajax

目录 了解 接口 前后端交互 一、安装服务器环境 nodejs ajax发起请求 渲染响应结果 get方式传递参数 post方式传递参数 封装ajax_上 封装ajax下 了解 清楚前后端交互就可以写一些后端代码了。小项目 现在写项目开发的时候都是前后端分离 之前都没有前端这个东西&a…

Kafka中的消息是如何存储的?

大家好,我是锋哥。今天分享关于【Kafka中的消息是如何存储的?】面试题。希望对大家有帮助; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中,消息是通过 日志(Log) 的方式进行存储的。…

Altium Designer——同时更改多个元素的属性(名称、网络标签、字符串标识)

右键要更改的其中一个对象,选择查找相似… 进入到筛选界面,就是选择你要多选的对象的共同特点(名字、大小等等),我这里要更改的是网络标签,所以我选择Text设置为一样。 点击应用就是应用该筛选调节&#…

当模板方法模式遇上工厂模式:一道优雅的烹饪架构设计

当模板方法模式遇上工厂模式:一道优雅的烹饪架构设计 模式交响曲的实现模板方法模式搭建烹饪骨架(抽象类)具体菜品(子类) 工厂模式 模式协作的优势呈现扩展性演示运行时流程控制 完整代码 如果在学习 设计模式的过程中…

企业级知识库建设:自建与开源产品集成的全景解析 —— 产品经理、CTO 与 CDO 的深度对话

文章目录 一、引言二、主流产品与方案对比表三、自建方案 vs. 开源产品集成:技术路径对比3.1 自建方案3.2 开源产品集成方案 四、结论与个人观点 一、引言 在当今数据驱动的商业环境中,构建高质量的知识库已成为企业数字化转型的关键一环。本博客分别从…

vue3项目配置别名

vue3项目配置别名 src别名的配置TypeScript 编译配置如果出现/别名引入报找不到的问题 src别名的配置 在开发项目的时候文件与文件关系可能很复杂,因此我们需要给src文件夹配置一个别名!!! // vite.config.ts import {defineCon…

[ C语言 ] | 从0到1?

目录 认识计算机语言 C语言 工欲善其事必先利其器 第一个C语言代码 这一些列 [ C语言 ] ,就来分享一下 C语言 相关的知识点~ 认识计算机语言 我们说到计算机语言,语言,就是用来沟通的工具,计算机语言呢?就是我们…

[Mac]利用Hexo+Github Pages搭建个人博客

由于我这台Mac基本没啥环境,因此需要从零开始配置,供各位参考。 注意⚠️:MacBook (M4)使用/bin/zsh作为默认Shell,其对应的配置文件为~/.zshrc 参考文档: HEXO系列教程 | 使用GitHub部署静态博客HEXO | 小白向教程 文…

Qt在IMX6ULL嵌入式系统中图片加载问题排查与解决

Qt在IMX6ULL嵌入式系统中图片加载问题排查与解决(保姆级教学!) 在使用Qt开发IMX6ULL嵌入式系统的过程中,我遇到了图片加载的常见问题。本文将分享问题排查的详细过程和解决方案,希望能帮助遇到类似困难的开发者。 问题…

界面控件Telerik和Kendo UI 2025 Q1亮点——AI集成与数据可视化

Telerik DevCraft包含一个完整的产品栈来构建您下一个Web、移动和桌面应用程序。它使用HTML和每个.NET平台的UI库,加快开发速度。Telerik DevCraft提供完整的工具箱,用于构建现代和面向未来的业务应用程序,目前提供UI for ASP.NET MVC、Kendo…

pycharm终端操作远程服务器

pycharm项目已经连接了远程服务器,但是打开终端,却依旧显示的是本地的那个环境,也就是说没有操作远程的那个环境。只能再使用Xshell去操作远程环境,很麻烦,找了下教程。 来源:https://blog.csdn.net/maolim…

接口测试中数据库验证,怎么解决?

在接口测试中,通常需要在接口调用前后查询数据库,以验证接口操作是否正确影响了数据库状态。​这可以通过数据库断言来实现,PyMySQL库常用于连接和操作MySQL数据库。​通过该库,可以在测试中执行SQL语句,查询或修改数据…

Playwright从入门到实战:比Selenium更快的数据爬取案例实战

摘要 Playwright 是微软开源的下一代浏览器自动化工具,凭借其高性能、跨浏览器支持和现代化设计,迅速成为 Web 自动化领域的热门选择。本文将从 安装配置 开始,通过 实战演练 展示其核心功能,并与 Selenium 深度对比,…

day1_Flink基础

文章目录 Flink基础今日课程内容目标为什么要学Flink技术更新迭代市场需求 流式计算批量计算概念特点 批量计算的优势和弊端流式计算生活中流场景流式计算的概念 Flink简介Flink历史Flink介绍 Flink架构体系已学过的框架技术Flink架构 Flink集群搭建Flink的集群模式Standalone模…

使用FastExcel时的单个和批量插入的问题

在我们用excel表进行插入导出的时候,通常使用easyexcel或者FastExcel,而fastexcel是easy的升级版本,今天我们就对使用FastExcel时往数据库插入数据的业务场景做出一个详细的剖析 场景1 现在我们数据库有一张组织表,组织表的字段…

交换技术综合实验

一、实验拓扑 二、实验要求 内网IP地址使用172.16.0.0/16分配。 SW1和SW2之间互为备份。 VRRP/STP/VLAN/Eth-trunk均使用。 所有PC通过DHCP获取IP地址。 ISP只能配置IP地址。 所有电脑可以正常访问ISP路由器。 三、实验步骤 基于172.16.0.0/16进行划分 172.16.2.0/24&…

记录Jmeter 利用BeanShell 脚本解析JSON字符串

下载org.json包(文档说明) #下载地址 https://www.json.org/ # github 地址 https://github.com/stleary/JSON-java # api 文档说明 https://resources.arcgis.com/en/help/arcobjects-java/api/arcobjects/com/esri/arcgis/server/json/JSONObject.htmlBeanShell脚本 import…

深入解析音频:格式、同步及封装容器

物理音频和数字音频 物理音频 定义:物理音频就是声音在自然界中的物理表现形式,本质上是一种机械波,通过空气或其他介质传播。例如,当我们说话、乐器演奏或物体碰撞时,都会产生振动,这些振动会引起周围介…

RPCGC阅读

24年的MM 创新 现有点云压缩工作主要集中在保真度优化上。 而在实际应用中,压缩的目的是促进机器分析。例如,在自动驾驶中,有损压缩会显着丢失户外场景的详细信息。在三维重建中,压缩过程也会导致场景数据中语义信息(Contour)的…