MySQL中InnoDB的事务隔离

2023-10-28


前言

我们在实际开发中,执行某个业务,肯定不是简单的操作某一句SQL语句,而是多条SQL语句。那么这多条SQL语句必须是全部成功执行,或者全部失败。才能保证业务逻辑的正确。因此这便引出了事务。接下来我们对MySQL的事务进行深入的了解学习


一、事务介绍

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在MySQL中,事务支持是在引擎层实现的,并且也不是所有的引擎都支持事务的,比如MyISAM引擎就不支持事务,这也是MyISAM被InnoDB取代的重要原因之一。
本文将会以InnoDB为例,将对MySQL对事务的支持进行深入学习。

对MySQL有基本了解的,提到事务,肯定会想到ACID(Atomicity、Consistency、lsolation、Durability,即原子性、一致性、隔离性、持久性)。
接下来我们先对这四大特性进行一个简单了解

二、事务的四大特性

  1. 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。
  2. 一致性:是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。 比如,用户A和用户B在银行分别有800元和600元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。
  3. 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。
  4. 持久性:事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失。

了解了事务这四大特性之后,那么这四大特性分别是怎么实现的呢,这里简答介绍下:
以InnoDB引擎为例,来介绍如何保证事务的这四个特性的

  • 持久性是通过redo log(重做日志)来保证的
  • 原子性是通过undo log(回滚日志)来保证的
  • 隔离性是通过MVCC(多版本并发控制)或锁机制来保证的
  • 一致性则是通过持久性+原子性+隔离性来保证

而这其中最重要,也是经常被考查的就是隔离性。接下来重点学习下事务的隔离性

三、事务的隔离性

当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

在介绍隔离级别之前,我们先介绍下这三个可能出现的问题:

  • 脏读:如果一个事务读到了另一个未提交事务修改过的数据,就意味着发生了脏读现象;(毕竟未提交过的事务都有随时可能发生回滚操作)
  • 不可重复读:在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了不可重复读现象(可能是其他事务修改了这条数据,并进行了提交)前后读取的数据不一致
  • 幻读:在一个事务内多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了幻读现象(可能是其他事务增添或者删除了记录并进行了提交)前后读取的记录数量不一致

为了解决这些可能出现的问题,便设计处了事务的隔离级别,其中包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable)。

在介绍这几个隔离级别之前,不知道读者是否有个疑问,既然知道了事务存在这些可能发生的问题,我们直接设计出一个隔离级别,一起把这三个可能出现的问题解决了不就行了吗,为什么还要设计出这么多个隔离级别啊。

为此,我们先来解答一下这个问题。那是因为隔离级别有高低之分,隔离级别越高,性能越低。因此很多时候,我们都要在二者之间寻找一个平衡点。这几个隔离级别隔离水平高低排序如下:
在这里插入图片描述

解决了这个疑惑之后,接下来为各个隔离级别进行介绍:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到;
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的
  • 串行化:对同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行

为了彻底搞清楚这几个隔离级别,这里用一个例子进行说明。

假设数据表T中只有一列,其中一行的值为1,如下SQL语句:

create table T(c int) engine=InnoDB;
insert into T(c) values(1);

然后按照时间顺序执行下面两个事务:
在这里插入图片描述
接下来,我们看看在不同的隔离级别下,事务A会有哪些不同的返回结果,即V1、V2、V3的返回值分别是什么

  • 若隔离级别是“读未提交”,则V1的值是2。这时候事务B虽然还没有提交,但是结果已经被A看到了。因此,V2、V3也都是2
  • 若隔离级别是“读提交”,则V1是1,V2的值是2.事务B的更新在提交后才能被A看到。所以,V3的值也是2
  • 若隔离级别是“可重复读”,则V1、V2是1,V3是2。之所以V2还是1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的
  • 若隔离级别是 “串行化”,则在事务B执行“将1改成2”的时候,会被锁住(被事务A的读锁锁住)。直到事务A提交后,事务B才可以继续执行。所以从A的角度看,V1、V2值是1,V3的值是2。

通过例子了解了事务的隔离性,那么我们对每个隔离级别可以解决的问题应该也清楚了。
为了清楚记忆,构建下图(针对不同的隔离级别,并发事务可能发生的现象):
在这里插入图片描述
通过上述,可以清楚认识到在不同的隔离级别下,数据库的行为是不同的。既然开发者设计出来,肯定有它的适用场景,根据实际开发的业务判断即可。

如下,举个“可重复读”的场景:数据校对逻辑的案例
假设在管理一个个人银行账号表。一个表存了每个月月底的余额,一个表存了账单明细。这时候要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。这时肯定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。
这时候使用“可重复读”隔离级别就很方便

了解了事务的隔离性,接下来我们再深入学习以下,看看这事务的隔离性是怎么实现的

四、事务隔离的实现

这里先总体说下实现。在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个SQL语句开始执行的时候创建的。“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

总体了解了之后,我们再来学习下事务隔离具体的实现,这里以“可重复读”为具体说明:

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

假设一个值从1被按顺序改成2、 3、 4,则在回滚日志里就会有类型下面的记录:
在这里插入图片描述
从上图,我们看见了一个新的东西:Read View。没接触过的肯定不知道。接下来我们先介绍下它。

如下图所示:
在这里插入图片描述
Read View的四个重要字段:

  • m_ids:指的是在创建Read View时,当前数据库中活跃事务的事务id列表,注意是一个列表,“活跃事务”指的就是,启动了但还没提交的事务
  • min_trx_id:指的是在创建Read View时,当前数据库中活跃事务中事务id最小的事务,也就是m_ids的最小值
  • max_trx_id:这个并不是m_ids的最大值,而是创建Read View时当前数据库中应该给下一个事务的id值,也就是全局事务中最大的事务id值+1
  • creator_trx_id:指的是创建该Read View的事务的事务id。

知道了Read View的字段,还要知道聚簇索引记录中的两个隐藏列。
如下图所示,每个行记录中包括的两个隐藏列:
在这里插入图片描述
对于InnoDB存储引擎的数据库表,聚簇索引记录包含的隐藏列解释如下:

  • trx_id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务id记录在trx_id隐藏列里;
  • roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到undo日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录

在创建Read View后,可以将记录中的trx_id划分为这三种情况:
在这里插入图片描述

了解了这之后,接着看开头把表数据的值从1改成2,然后3、 4。那么当前值是4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的read-view。从上面的数据更新图可以看出,在视图A、B、C里面,这一个记录的值分别是1、2、 4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于上面数据更新图里,对于read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。

这是不知道你发现问题了没有。设想,如果我们数据更新的比较频繁,那么回滚日志中的数据不能一直都保留吧,那么什么时候才会删除记录呢?

系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的read-view的时候。

此时在使用时有个建议:
尽量不要使用长事务,因为长事务就意味着系统里面会存在很老的事务视图。并且这些事务随时可能访问数据库里面任何数据,所以这个事务提交之前,数据库里面它可能用到回滚记录都必须保留,这就会导致大量占用存储空间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL中InnoDB的事务隔离 的相关文章

  • MySql 查询在选择中将 NULL 替换为空字符串

    如何用空字符串替换 select 中的 NULL 值 输出 NULL 值看起来不太专业 这是非常不寻常的 根据我的语法 我希望它能够工作 我希望能得到一个解释 为什么没有 select CASE prereq WHEN prereq IS
  • 自动将所有mysql表转储到单独的文件中?

    我想将每个 mysql 表转储到单独的文件中 手册指出其语法是 mysqldump options db name tbl name 这表明您事先知道表名称 我现在可以设置知道每个表名称的脚本 但是假设我在路上添加了一个新表并且忘记更新转储
  • MySQL 子查询返回多行

    我正在执行这个查询 SELECT voterfile county Name voterfile precienct PREC ID voterfile precienct Name COUNT SELECT voterfile voter
  • MySQL 中的断言

    我有一个针对大型数据库运行的 SQL 脚本 我想在开始时提出几个简单的查询 作为健全性检查 有没有办法在MySQL中写断言 或者任何类型的 选择 如果它与该值不匹配 则中止整个脚本 一些疯狂的代码 要点是 SET可能会引发 mysql 变量
  • 在mysql中搜索“SanF”时获取旧金山的记录

    当我搜索 SanF 时获得 San Francisco 记录 SELECT FROM table WHERE col LIKE san Works SELECT FROM table WHERE col LIKE san F Works S
  • SQL Server PIVOT 函数

    我有一个检索所有代理及其模块的查询 结果集将每个模块返回 1 行 SELECT am agentID AS agentid pa agentDisplayName agentdisplayname m ModuleName ModuleNa
  • 从数据库生成 XML 时出现 PHP 编码错误 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在尝试获取一个简单的 PHP 服
  • ejabberd 16.06 + mysql 5.5.50,消息历史记录不保存

    我使用ejabberd 16 06 mysql 5 5 50 消息历史记录没有保存 我的 ejabberd yml MySQL server odbc type mysql odbc server freldo odbc port 3306
  • MySQL“选择更新”行为

    根据 MySql 文档 MySql 支持多粒度锁定 MGL case 1 开放航站楼 1 连接到mysql mysql gt start transaction Query OK 0 rows affected 0 00 sec mysql
  • 在 MySQL 中将行转置为列

    如何在 MySQL 查询中将行转换为列 您可以将行变成a列与GROUP CONCAT 但您无法以任何自动方式转置整个结果集 您可以编写手动生成每一列的查询 也可以在应用程序中执行此操作 以下是有关编写复杂查询来模拟转置的教程 http ww
  • PreparedStatement setnull方法中Types.INTEGER和Types.NULL的区别

    下面的说法有什么区别 PreparedStatement setNull 1 java sql Types NULL and PreparedStatement setNull 1 java sql Types INTEGER 第一个指示驱
  • SQL Server 查询结果集的大小

    SQL Server 中是否有确定结果集中 Mgmt Studio 查询中返回的数据大小 以 MEGS 为单位 您可以打开客户端统计信息 查询菜单 包括客户端统计信息 它给出执行查询时从服务器返回的字节数
  • Sql:计算随时间的增长

    我几周前发布了这个问题 但我认为我没有清楚地提出这个问题 因为我得到的答案不是我想要的 我认为最好重新开始 我正在尝试查询数据库以检索一段时间内唯一条目的数量 数据看起来像这样 Day UserID 1 A 1 B 2 B 3 A 4 B
  • 我可以在一个查询中更新/选择表吗?

    我需要在查看页面时选择数据并更新 视图 列 有没有一种方法可以在一个查询中执行此操作 或者我是否必须使用不同的查询 如果您不想 不需要使用事务 则可以创建一个存储过程 该过程首先更新视图计数 然后选择值并将其返回给用户
  • 在 PHP 中将十进制/双精度/浮点值与 PDO 绑定的最佳方法是什么?

    看来类常量只涵盖PDO PARAM BOOL PDO PARAM INT and PDO PARAM STR用于绑定 您只是将十进制 浮点 双精度值绑定为字符串还是有更好的方法来处理它们 MySQLi 允许使用 d 类型表示 double
  • 如何通过循环变量在 dbt 中多次运行 SQL 模型?

    我有一个 dbt 模型 测试模型 接受地理变量 zip state region 在配置中 我想通过循环变量来运行模型三次 每次使用不同的变量运行它 问题是 我有一个如下所示的宏 它将变量附加到输出表名称的末尾 即运行测试模型 with z
  • 从数据库 MYSQL 和 Codeigniter 获取信息

    如果你们需要其他信息 上一个问题就在这里 从数据库中获取信息 https stackoverflow com questions 13336744 fetching information from the database 另一个更新 尽
  • Oracle:如果表存在

    我正在为 Oracle 数据库编写一些迁移脚本 并希望 Oracle 有类似于 MySQL 的东西IF EXISTS构造 具体来说 每当我想删除 MySQL 中的表时 我都会执行类似的操作 DROP TABLE IF EXISTS tabl
  • 案例陈述以确定我是否应该结合

    我目前想做某种条件联合 给出以下示例 SELECT age name FROM users UNION SELECT 25 AS age Betty AS name 假设我只想在 用户 计数 gt 2 时合并第二个语句 否则不合并两者 总之
  • 通过将行旋转为动态数量的列来在 MySQL 中创建摘要视图

    我在 MySQL 中有一个表 其中包含以下字段 id company name year state 同一客户和年份有多行 以下是数据示例 id company name year state 1 companyA 2008 1 2 com

随机推荐

  • Kafka 面试套路居然这样多!读完大神的 Kafka 核心手册,秒杀面试官!全网最强!!

    在热招的 Java 架构师岗位面试中 Kafka 面试题被面试官选中的几率非常大 也是 HR 的杀手锏和狠招 一般来讲 面试题有以下几种 Kafka 为什么这么快 如何对 Kafka 集群进行调优 Kafka 的高性能网络架构是如何设计的
  • 【JavaScript】详解JavaScript中的replace()函数

    replace 1 方法简介 2 replace 使用 2 1 replace 字符串 字符串 2 2 replace 正则表达式 字符串 2 3 replace 正则表达式 function 2 3 1 简单用法 正则表达式不使用分组 2
  • QString 和char *和QByteArray的转换总结

    参考博客 https www cnblogs com findumars p 5107700 html 以下内容摘抄以上大神博客 1 char转换为QString char a b QString str str QString a 2 Q
  • 为 openEuler 安装 桌面环境图形化界面【ukui】

    三 UKUI 图形化界面 3 1 安装图形化界面 ukui 由于openEuler只有命令行操作界面 所以我们可以给openEuler安装一个桌面图形环境 最近华为发布了一个适用于openEuler的图形化界面 叫作UKUI 3 1 1 首
  • C++调用python脚本

    C 调用python的脚本 一 为什么 缘由 用python写了机器学习的模型 项目工程代码是C 写得 所以在调用时 想通过C 调用python脚本 用c 获取返回值 然后就是一个接着一个的坑 二 环境 环境win10 64 VS2008
  • 虚拟机中如何挂载物理磁盘(VMware操作)

    虚拟机中如何挂载物理磁盘 VMware操作 来源 本站转载 作者 佚名 时间 2012 04 16 11 05 04 测试的时候难免会遇到 从真是机器拷贝东西到虚拟机中 虽说安装了VMware tools Vm Install VMware
  • 关于Mybatis中的条件查询。createCriteria example里面的条件

    之前用Mybatis框架反向的实体 还有实体里面的Example 之前只是知道Example里面放的是条件查询的方法 可以一直不知道怎么用 到今天才开始知道怎么简单的用 在我们前台查询的时候会有许多的条件传过来 先看个例子 public L
  • linux内核分析编译体验

    一 资源 linux 2 6 22 6 下载地址 https mirrors edge kernel org pub linux kernel v2 6 linux 2 6 22 6 jz2440 patch下载地址 https downl
  • cesium简介

    文章目 1 什么是Cesium 2 Cesium能做什么 3 Cesium的依赖性 4 Cesium学习参考 Cesium实战系列文章总目录 传送门 1 什么是Cesium Cesium是AGI公司计算机图形开发小组与2011年研发的三维地
  • ant-design-vue customRequst

    关于a upload中customRequest customRequest回调参数 onProgress event percent number void onError event Error body Object void onS
  • python输出高斯消元法求解行列式的过程

    前言 有人可能好奇 为啥有那么多工具还要自己写一个 emmm 比如我求 Ax b 增广矩阵 比如说我想要 A I A I A I gt
  • 没有工业软件 谈什么智能制造转型

    没有工业软件 谈什么智能制造转型 时间 2017 02 04 1 赞 0 评论 关键字 工业软件 智能制造 PI推出全新LinkSwitch TN2开关电源IC LinkSwitch LP ADC原理及应用 AWR1243查看技术文档 Li
  • 程序员成长的四个简单技巧,你 get 了吗?

    最近拜读了 阿里工程师的自我修养 手册 12 位技术专家分享生涯感悟来帮助我们这些菜鸡更好的成长 度过中年危机 我收获颇多 其中有不少的方法技巧和我正在使用的 这让我觉得我做的这些事情是对的 我走在了一条正确的道路上 我们程序员这个行业不像
  • vue+element表格的左右布局及动态添加行

    一般我们实现表格 直接使用element中的table组件 现在有个原型图是 类似表头左边 本来在纠结要写元生的表格了 最后突然想到把它当成form表单 直接样式实现表格效果 效果如图 我这里还有一个动态的添加行 我只做了这一个的动态添加的
  • [ MySQL] — 事务管理

    什么是事务 事务就是一组DML语句组成 这些语句在逻辑上存在相关性 这一组DML语句要么全部成功 要么全部失败 是一个整体 MySQL提供一种机制 保证我们达到这样的效果 事务还规定不同的客户端看到的数据是不相同的 事务就是要做的或所做的事
  • springcloud3 分布式事务解决方案seata之TCC模式6

    一 TCC模式 1 1 TCC的逻辑 TCC模式与AT模式非常相似 每阶段都是独立事务 不同的是TCC需要人工干预编写代码 需要实现三个方法 Try 资源的检测和预留 Confirm 完成资源操作业务 要求 Try 成功 Confirm 一
  • springboot多环境配置

    1 新建一个springboot项目 2 新建多个环境的配置文件 在springboot中多环境配置文件名需要满足 application profile yml的格式 其中 profile 是对应的环境标识 比如 开发环境 applica
  • ES6详解七:循环的秘密 - iterator 和 yield

    如果学过设计模式或者java之类的肯定知道 iterator 是什么 在 Symbol iterator 出现后 JS中也可以自己定义一个迭代器 只要一个对象实现了正确的 Symbol iterator 方法 那么它就可以被 for in
  • 【问题解决】javaWeb项目中报:java.io.FileNotFoundException: src\druid.properties (系统找不到指定的路径。)错误

    在登录界面填写账号密码提交后 报java io FileNotFoundException src druid properties 系统找不到指定的路径 错误 解决方法 把下面的相对路径改为绝对路径 改正后 可正常运行
  • MySQL中InnoDB的事务隔离

    文章目录 前言 一 事务介绍 二 事务的四大特性 三 事务的隔离性 四 事务隔离的实现 前言 我们在实际开发中 执行某个业务 肯定不是简单的操作某一句SQL语句 而是多条SQL语句 那么这多条SQL语句必须是全部成功执行 或者全部失败 才能