MySQL 事务 | ACID、四种隔离级别、并发带来的隔离问题、事务的使用与实现

文章目录

  • 事务
  • ACID
  • 并发带来的隔离问题
    • 幻读(虚读)
    • 不可重复读
    • 脏读
    • 丢失更新
  • 隔离级别
    • Read Uncommitted (读未提交)
    • Read Committed (读已提交)
    • Repeatable Read (可重复读)
    • Serializable (可串行化)
  • 事务的使用
  • 事务的实现
    • Redo
    • undo


事务

事务指逻辑上的一组操作。

我们在 MySQL 存储引擎 | MyISAM 与 InnoDB 中提到,MyISAM引擎 并不支持事务,所以本文内容主要与 InnoDB引擎 相关。

这篇博客写事务也写得很好:我以为我对Mysql事务很熟,直到我遇到了阿里面试官


ACID

谈到事务,那肯定少不了ACID的特性,ACID是以下几个单词的缩写:

原子性(atomicity):

事务是一个不可分割的工作单位,对数据的修改要么全部执行成功,要么全部失败。

举个例子:事务A中要进行转账,那么转出的账号要扣钱,转入的账号要加钱,这两个操作都必须同时执行成功,从而确保数据的一致性。

一致性(consistency):

事务操作前与操作后数据库的状态始终一致。

如何理解呢?就好比我们此时有用户A和用户B,他们的余额分别为300元和700元,此时两人总金额为1000元。此时若是用户B向用户A转账200元,则两者的此时都有500元,总金额还是1000元。

也就是说,无论我们两个怎么转账,总金额它只会是1000,既不会多,也不会少。这就是事务操作前后的状态始终一致。倘若钱多了或者少了,都代表着事务将数据库从一种状态变为了另外一种状态,此时就不再符合一致性了。

隔离性(isolation):

隔离性指的是每个读写事务的对象之间相互隔离,即该事务提交前对其他事务都不可见。

持久性(durability):

持久性指的是事务一旦提交,这个事务的状态会被持久化到数据库中。 即使发生了服务器宕机的事故,数据库也能成功的将数据给恢复。

但是需要注意的是,只能保证数据库本身发生的问题后可以恢复,但并不是事务提交后所有变化都是永久的,倘若是由于外部原因如:RAID卡损坏、天灾人祸导致数据库发生问题,那么即使事务提交了,也可能会丢失。

基于上述原因,持久性只能保证事务系统的高可靠性,而无法保证其高可用性。

总结:

原子性、隔离性、持久性都是为了保障一致性而存在的,一致性也是最终的目的。


并发带来的隔离问题

幻读(虚读)

幻读指 在同一事务中,用同样的操作读取两次,得到的记录数却不一样(针对 insert 操作)。 举个例子:

  • 第一个事务对表中的所有数据行进行修改;
  • 同时,第二个事务向表中插入了一行。这样也就导致了操作第一个事务的用户发现表中还有没修改的数据行,像发生了幻觉一样。

在这里插入图片描述
明明在 会话A 的第一次查询中,大于 2 的数只有行只有一行,而由于 会话B 插入了新行后,对于 会话A 而言就凭空多出来了一行,像出现了幻觉一样。


不可重复读

不可重复读指的是在一个事务中多次读取同一行数据,但是多次读取的数据却不一样(针对 update 操作)。导致这一问题的主要原因就是一个事务读取到了其他事务已提交的数据

例如:

  1. 账户1中有300元,账户2中有500元
  2. 事务A读取账户B的内容,里面显示有500元
  3. 事务B将账户1的300元全部转给账户2,并提交事务
  4. 事务A再次读取账户B,此时里面有800元。

由于其他事务的干扰,对于事务A来说,两次读取的金额都不一样。

因为不可重复读读到的是已经提交的数据,由于其本身并不会带来很大的问题,所以大部分数据库厂商都会允许这种情况的发生。

在这里插入图片描述


脏读

脏读即一个事务读取到了另外一个事务中未提交的数据,也就是可能因为其他事务对数据进行修改或者回滚导致的问题。

在这里插入图片描述
会话B 在第一次查看时表中只有一条数据,但是在第五阶段中 会话A 向表中插入了另一条数据(但还未commit【提交】),这就导致了 会话B 在读取的时候得到的结果就不再一样,因为它读取到了脏数据。

脏读的现象并不会经常发生,因为脏读发生的条件是需要事务的隔离级别为 READ UNCOMMITTED(读未提交),而大部分数据库的默认隔离级别都为 READ COMMITTED(读已提交)


丢失更新

丢失更新就是一个事务的更新操作会被另外一个事务的更新操作所覆盖,从而导致数据的不一致。例如以下案例:

  1. 事务A行记录r 更新为 1,但是 事务A 并未提交;
  2. 同时,事务B行记录r 更新为 2事务B 也未提交;
  3. 事务A提交;
  4. 事务B提交。

此时由于 BA 的修改覆盖,导致 A 虽然提交,但是更新却丢失了,只剩下了 B 的更新。

但是在当前数据库的任何隔离级别下,都不会导致理论意义上的丢失更新问题,即使是隔离级别最低的 Read Uncommitted,也由于加锁保护,所以 事务B 的修改操作会被阻塞,直到 事务A 提交。


隔离级别

为了解决上述问题,MySQL中实现了以下四种隔离级别,隔离级别由低到高依次是:

  • 读未提交(READ UNCOMMITTED)
  • 读已提交 (READ COMMITTED)
  • 可重复读 (REPEATABLE READ)
  • 串行化 (SERIALIZABLE)

隔离级别越高,事务请求的锁也就越多,保持锁的时间也就越长。所以隔离性越强,并发的效率也就越低。


Read Uncommitted (读未提交)

在该隔离级别下,所有事务都可以看到其他未提交事务的执行结果,容易产生脏读问题。

在该级别下,虽然并发的效率最高,但是安全性完全没有得到保护,所以很少用于实际应用。


Read Committed (读已提交)

该隔离级别是大部分数据库默认的隔离级别,如 OracleSQL Server 等。该隔离级别下,一个事务只能看见提交了的事务所做的改变,容易产生不可重复读的问题。

虽然它还有,但不可重复读本身并不是一个大问题,所以为了兼顾到性能,大部分数据库都会容许这种问题的产生。


Repeatable Read (可重复读)

这是 MySQLInnoDB 默认的隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,容易产生幻读的问题。

InnoDB 可以借助 MVCC 中的 Next-Key Locking 的加锁方式来解决这个问题,详见本文。


Serializable (可串行化)

这是最高的隔离级别,通过强制事务进行排序,使事务之间不可能互相冲突,从而解决了其他隔离级别无法解决的幻读问题

由于其在每个读的数据行上加了共享锁,所以在该隔离级别下可能会导致大量的超时现象以及锁竞争


这四种隔离级别分别可能发生的问题如下图所示:
在这里插入图片描述


事务的使用

开启事务

START TRANSACTION
或者
BEGIN
(由于MySQL的数据分析器会自动将BEGIN识别为BEGIN...END,所以在存储过程中只能使用START TRANSACTION来开启事务)

提交事务

COMMIT

回滚事务

//回滚整个事务
ROLLBACK//回滚至某个保存点
ROLLBACK TO SAVEPOINT [保存点ID]

设置保存点

SAVEPOINT 保存点ID

删除保存点

RELEASE SAVEPOINT 保存点ID

查看隔离级别

// 可以看到 MySQL 的 InnoDB存储引擎 的默认隔离级别为可重复读
mysql> SELECT @@TRANSACTION_ISOLATION;
+-------------------------+
| @@TRANSACTION_ISOLATION |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

设置隔离级别

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
// GLOBAL 也可以换成 SESSION,前者表示全局的,后者表示当前会话,也就是当前窗口有效。

PS:当设置完隔离级别后对于之前打开的会话,是无效的,要重新打开一个窗口设置隔离级别才生效。
在这里插入图片描述


事务的实现

  • 事务的持久性主要依靠 Redo log(重做日志)来完成。
  • 原子性、一致性则通过 Undo log(撤销日志)来完成。
  • 隔离性,则通过锁来完成。

先简单说说 Redo 和 Undo :

Redo/Undo机制: 将所有对数据的更新操作都写到日志中。

  • Redo log 用来记录某数据块被修改后的值,可以用来恢复未写入 data file 的已成功事务更新的数据。Redo 通常是物理日志,记录的是页的物理修改操作。
  • Undo log 是用来记录数据更新前的值,保证数据更新失败能够回滚。Undo 是逻辑日志,根据每行记录进行记录。

举个例子:

假如某个时刻数据库崩溃,当数据库重启进行 crash-recovery 时,就会通过 Redo log 将已经提交事务的更改写到数据文件,而还没有提交的就通过 Undo log 进行 回滚roll back


Redo

Redo log 由两部分组成:

  • 一是存在于内存中的 重做日志缓冲(redo log buff),由于存在内存中,所以其具有易失性。
  • 二是 重做日志文件(redo log file),其存在于硬盘中,所以是持久的。

Redo 主要通过 Force Log at Commit机制 来实现事务的持久性。

步骤如下:

在这里插入图片描述
为了确保日志写入文件中,每次将日志缓冲写入日志文件后,都会发起一次 异步操作(fsync)

为什么需要这个异步调用呢?

因为重做日志文件打开时并没有使用 O_DIRECT 选项,所以重做日志缓冲会先写入文件系统缓冲,为了保证其能够成功写入磁盘,必须发起一次异步调用。由于异步调用的效率取决于磁盘的性能,因此磁盘的性能决定了事务提交的性能,即数据库性能。


undo

undo 是撤销日志,其中保留了数据库各个版本的状态,我们可以借助 undo 逻辑地将数据库恢复到原来地样子。除了进行回滚之外, undo 的另一个作用就是实现 MVCC

首先看看 undo log 的生成流程:
在这里插入图片描述

在这里插入图片描述
每当事务发生变更的时候,都会伴随着 undo log 的产生,并且为了防止其丢失,undo log 会比数据持久化到硬盘上。

由于 undo log 是逻辑日志,所以其中记录的都是对于数据库的操作指令。而事务的回滚,其实也就是根据这个操作来进行一个逆向操作。如下面几种:

  • 当执行一个 insert 指令时,其逆向指令为 delete
  • 当执行一个 delete 指令时,其逆向指令为 insert
  • 当执行一个 update 指令时,其逆向指令为 update

在这里插入图片描述
原子性就是借助以上机制实现,倘若事务中的某一个步骤未能成功完成,则借助 undo log 中存储的记录来回滚到事务的最原始状态,即一个失败全体失败。

而至于一致性,则主要依靠上述的其他三种特性来实现,也就是说一致性是目的,而原子性、隔离性、持久性则是数据库实现一致性的手段,只有满足这三个性质,才能够保证一致性。

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

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

相关文章

MySQL 备份与主从复制

文章目录备份主从复制主从复制的作用备份 根据备份方法的不同,备份可划分为以下几种类型: 热备(Hot Backup) : 热备指的是在数据库运行的时候直接备份,并且对正在运行的数据库毫无影响,这种方法在 MySQL 官方手册中又…

C++ 流的操作 | 初识IO类、文件流、string流的使用

文章目录前言IO头文件iostreamfstreamsstream流的使用不能拷贝或对 IO对象 赋值条件状态与 iostate 类型输出缓冲区文件流fstream类型文件模式文件光标函数tellg() / tellp()seekg() / seekp()向文件存储内容/读取文件内容string流istringstreamostringstream前言 我们在使用 …

C++ 右值引用 | 左值、右值、move、移动语义、引用限定符

文章目录C11为什么引入右值?区分左值引用、右值引用move移动语义移动构造函数移动赋值运算符合成的移动操作小结引用限定符规定this是左值or右值引用限定符与重载C11为什么引入右值? C11引入了一个扩展内存的方法——移动而非拷贝,移动较之拷…

且谈关于最近软件测试的面试

前段时间有新的产品需要招人,安排和参加了好几次面试,下面就谈谈具体的面试问题,在面试他人的同时也面试自己。 面试问题是参与面试同事各自设计的,我也不清楚其他同事的题目,就谈谈自己设计的其中2道题。 过去面试总是…

C++ 多态 | 虚函数、抽象类、虚函数表

文章目录多态虚函数重写重定义(参数不同)协变(返回值不同)析构函数重写(函数名不同)final和override重载、重写、重定义抽象类多态的原理虚函数常见问题解析虚函数表多态 一种事物,多种形态。换…

C++ 运算符重载(一) | 输入/输出,相等/不等,复合赋值,下标,自增/自减,成员访问运算符

文章目录输出运算符<<输入运算符>>相等/不等运算符复合赋值运算符下标运算符自增/自减运算符成员访问运算符输出运算符<< 通常情况下&#xff0c;输出运算符的第一个形参是一个 非常量ostream对象的引用 。之所以 ostream 是非常量是因为向流写入内容会改变…

C++ 重载函数调用运算符 | 再探lambda,函数对象,可调用对象

文章目录重载函数调用运算符lambdalambda等价于函数对象lambda等价于类标准库函数对象可调用对象与function可调用对象function函数重载与function重载函数调用运算符 函数调用运算符必须是成员函数。 一个类可以定义多个不同版本的调用运算符&#xff0c;互相之间应该在参数数…

C++ 运算符重载(二) | 类型转换运算符,二义性问题

文章目录类型转换运算符概念避免过度使用类型转换函数解决上述问题的方法转换为 bool显式的类型转换运算符类型转换二义性重载函数与类型转换结合导致的二义性重载运算符与类型转换结合导致的二义性类型转换运算符 概念 类型转换运算符&#xff08;conversion operator&#…

分布式理论:CAP、BASE | 分布式存储与一致性哈希

文章目录分布式理论CAP定理BASE理论分布式存储与一致性哈希简单哈希一致性哈希虚拟节点分布式理论 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系统中的所有数据副本&#xff0c;在同一时刻是否一致&#xff08;所有节点访问同一份最新的数据副…

分布式系统概念 | 分布式事务:2PC、3PC、本地消息表

文章目录分布式事务2PC&#xff08;二阶段提交协议&#xff09;执行流程优缺点3PC&#xff08;三阶段提交协议&#xff09;执行流程优缺点本地消息表&#xff08;异步确保&#xff09;分布式事务 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分…

数据结构算法 | 单调栈

文章目录算法概述题目下一个更大的元素 I思路代码下一个更大元素 II思路代码132 模式思路代码接雨水思路算法概述 当题目出现 「找到最近一个比其大的元素」 的字眼时&#xff0c;自然会想到 「单调栈」 。——三叶姐 单调栈以严格递增or递减的规则将无序的数列进行选择性排序…

最长下降子序列

文章目录题目解法DP暴搜思路代码实现贪心二分思路代码实现题目 给出一组数据 nums&#xff0c;求出其最长下降子序列&#xff08;子序列允许不连续&#xff09;的长度。&#xff08;类似于lc的最长递增子序列&#xff09; 示例&#xff1a; 输入&#xff1a; 6 // 数组元素个…

Linux 服务器程序规范、服务器日志、用户、进程间的关系

文章目录服务器程序规范日志rsyslogd 守护进程syslog函数openlog函数setlogmask函数closelog函数用户进程间的关系进程组会话系统资源限制改变工作目录和根目录服务器程序后台化服务器程序规范 Linux 服务器程序一般以后台进程&#xff08;守护进程[daemon]&#xff09;形式运…

IO模型 :阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO

文章目录IO模型阻塞IO非阻塞IO信号驱动IO多路复用IO异步IOIO模型 根据各自的特性不同&#xff0c;IO模型被分为阻塞IO、非阻塞IO、信号驱动IO、异步IO、多路复用IO五类。 最主要的两个区别就是阻塞与非阻塞&#xff0c;同步与异步。 阻塞与非阻塞 阻塞与非阻塞最主要的区别就…

Tomcat服务器集群与负载均衡实现

一、前言 在单一的服务器上执行WEB应用程序有一些重大的问题&#xff0c;当网站成功建成并开始接受大量请求时&#xff0c;单一服务器终究无法满足需要处理的负荷量&#xff0c;所以就有点显得有点力不从心了。另外一个常见的问题是会产生单点故障&#xff0c;如果该服务器坏掉…

Linux服务器 | 事件处理模式:Reactor模式、Proactor模式

文章目录Reactor模式Proactor模式同步I/O模型模拟Proactor模式两者的优缺点ReactorProactor同步I/O模型通常用于实现 Reactor 模式&#xff0c;异步I/O模型通常用于实现 Proactor 模式。&#xff08;不是绝对的&#xff0c;同步I/O也可模拟出 Proactor 模式&#xff09; React…

Linux服务器 | 服务器模型与三个模块、两种并发模式:半同步/半异步、领导者/追随者

文章目录两种服务器模型及三个模块C/S模型P2P模型I/O处理单元、逻辑单元、存储单元并发同步与异步半同步/半异步模式变体&#xff1a;半同步/半反应堆模式改进&#xff1a;高效的半同步/半异步模式领导者/追随者模式组件 &#xff1a;句柄集、线程集、事件处理器工作流程两种服…

字符串匹配之KMP(KnuthMorrisPratt)算法(图解)

文章目录最长相等前后缀next数组概念代码实现图解GetNext中的回溯改进代码实现代码复杂度分析最长相等前后缀 给出一个字符串 ababa 前缀集合&#xff1a;{a, ab, aba, abab} 后缀集合&#xff1a;{a, ba, aba, baba} 相等前后缀 即上面用同样颜色标识出来的集合元素&#…

Android入门(一) | Android Studio的配置与使用

文章目录安装配置Android Studio使用Android Studio模拟器更改Android SDK的路径Hello World&#xff01;安装配置Android Studio 从这一步开始&#xff1a; 一直点 next 即可&#xff0c;直到存储路径的选择上&#xff0c;可以放到非 C 盘&#xff0c;这里我放到 D 盘了&am…

Android 入门(四) | Intent 实现 Activity 切换

文章目录Intent显式 Intent定义两个 xml 文件android:orientationmatch_parent 和 wrap_contentIntent函数定义两个 Activity隐式 Intent更多隐式 Intent 的用法用隐式 Intent 打开系统浏览器自建 Activity 以响应打开网页的 Intent向下一个活动传递数据返回数据给上一个活动In…