MySQL面试必备三之事务
本文首发于公众号:Hunter后端
原文链接:MySQL面试必备三之事务
这一篇笔记介绍一下 MySQL 的事务,面试中常被问到关于事务的几个问题如下:
- 事务是什么
- 为什么需要事务,事务有什么作用
- 事务的特点
- 事务可能带来哪些问题
- 事务有哪些隔离级别,这些隔离级别都可以解决哪些问题
- 可重复读隔离级别下能否解决幻读问题
- 如何解决幻读问题
以下是本篇笔记目录:
- 什么是事务
- 事务的特性
- 事务执行的示例
- 并发事务可能带来的问题
- 事务的隔离级别
- 可重复读隔离级别为什么不可以解决幻读的问题
1、什么是事务
所谓事务,就是一系列的 SQL 组合,这些 SQL 操作要么全部执行,要么都不执行,是一个不可分割的工作单位。
比如我们在支付系统中想要完成一个转账功能,比如从 A 账户转账一百元给 B 账户,那么从 A 账户的总额中减去 100,然后在 B 账户上加上 100,这两个就需要全部执行才算是这个转账操作的实现。
这个过程就包含了 A 账户的减少 100,B 账户的加上 100,这两个操作加起来就是一个完整的事务。
2、事务的特性
事务的特性有四个,为ACID,分别是 A(Atomicity)、C(Consistency)、I(Isolation) 和 D(Duration),分别表示原子性、一致性、隔离性和持久性。
1. 原子性
事务的原子性指的是一个事务中的所有操作要么全部完成,要么全部失败,如果在执行事务的过程中,某个 SQL 执行失败,那么这个事务中之前执行操作全部回滚,恢复到执行事务之前的状态。
2. 一致性
一致性指的是事务执行前后,数据库的状态应该保持一致,即数据库的完整性不会被破坏。
这个一致性的理解为在执行事务前后数据库应该符合事务的约束条件,从而保证数据的正确性。
比如我们设置了某个字段的属性应当大于或等于 0,但在某个操作过程中如果更新该字段的值小于 0,那么则属于破坏了数据的一致性,事务会回滚到执行前,从而保证数据库状态的一致性。
3. 隔离性
隔离性指的是多个事务并发执行时,每个事务都应该独立于其他事务,互不干扰,从而避免数据并发访问引起的问题。
事务的隔离分为多个级别,这个在后面再介绍。
4. 持久性
事务的持久性指的是事务执行完毕之后,对数据的修改就是永久的,即便是系统故障,修改的数据也不会丢失。
3、事务执行的示例
事务的执行过程会包含几个步骤,事务的开始、SQL 操作、提交或者回滚。
1. 事务执行示例
比如我们想要给 id=1 的账号减去一百元,然后给 id=2 的账号加上一百元,使用事务来操作的示例如下:
START TRANSACTION;
UPDATE user_account set money = money - 100 WHERE id = 1;
UPDATE user_account set money = money + 100 WHERE id = 2;
COMMIT;
这里,我们通过 START TRANSACTION 开启一个事务,中间执行 SQL 操作,以 COMMIT 提交事务为结束。
2. 回滚操作
如果我们想执行回滚操作,可以直接使用 ROLLBACK:
START TRANSACTION;
UPDATE user_account set money = money - 100 WHERE id = 1;
UPDATE user_account set money = money + 100 WHERE id = 2;
ROLLBACK;
这里的回滚操作会回滚到事务执行前。
3. 保存点
如果我们的事务包含的 SQL 很长,我们并不想直接回滚到事务开启前,而是事务中间的某个步骤,我们可以使用保存点来进行回滚操作:
-- 开始事务
START TRANSACTION;
-- 执行 SQL 操作
UPDATE accounts SET balance = balance - 100 WHERE account_id = 123;
SAVEPOINT before_insert;
INSERT INTO transaction_log (account_id, amount, type) VALUES (123, 100, 'debit');
-- 检查条件
IF some_condition THEN
-- 回滚到保存点
ROLLBACK TO before_insert;
ELSE
-- 提交事务
COMMIT;
END IF;
在这里,我们通过 SAVEPOINT 来创建保存点,并在后面的代码里通过 IF 条件进行判断,选择性的回到该保存点。
一个事务是可以包含多个保存点的。
4. 单条 SQL 的事务
前面几条介绍的都是使用 START TRANSACTION 显式地开始一个事务,而至于单条 SQL 语句,比如 INSERT 或者 UPDATE 这种,在默认情况下是自动提交的,所以不用手动进行 COMMIT 操作,它们也属于单独的事务。
4、并发事务可能带来的问题
在我们访问数据库时,可能同一时刻有多个事务在访问操作数据库,那么这样可能会导致一些问题。
1. 脏读
所谓脏读就是在在某个事务的执行过程中可以读到其他事务未提交的数据,这个现象就是脏读。
因为一个事务的执行是可能包含多个 SQL 的,在某种事务隔离级别下就可能存在 A 事务执行了 SQL 但是还未提交,这时候 B 事务执行过程中就读取到了 A 事务更改的数据。
2. 不可重复读
不可重复读的现象指的是在同一个事务中,有两个读取数据的 SQL,这两次读取的数据内容都不一样,这种现象就称为不可重复读。
这个现象产生的原因在于这两次读取 SQL 的过程中,有其他事务更新了这条数据并提交了。
不可重复读偏重的点在于对数据的修改。
3. 幻读
幻读的现象指的是在同一个事务中,两次查询数据返回的结果的条数不一样,它产生的原因同样是两次查询期间有其他事务提交了,但它的侧重点是其他事务是对数据的插入或者删除。
5、事务的隔离级别
事务的隔离级别分别是读未提交(Read Uncommited)、读已提交(Read Commited)、可重复读(Repeatable Read)、串行化(Serializable)。
1. 读未提交
读未提交指的是一个事务可以读取到其他事务未提交的内容。
在这个隔离级别下,如果有一个事务 A,包含多条 SQL 操作,执行到其中某条 SQL,但是还没有执行 COMMIT 操作,这个时候另一个事务 B 读取事务 A 操作过的 SQL 数据,就可以读取到对应内容,这个过程就是读未提交。
读未提交这个隔离级别可能会造成数据的脏读问题。
2. 读已提交
读已提交指的是事务可以读取到其他事务已经提交的数据,这个隔离级别可以解决脏读问题,但是不可以解决不可重复读和幻读的问题。
比如一个事务 A,在其执行过程中先读取了某条数据,这个时候另一个事务 B 开启一个事务并提交,事务 B 修改了事务 A 前面读取的数据内容,这个时候事务 A 在后面的操作又读取了这条数据,会发现和第一次读的时候数据不一致,这个就是读已提交可能造成的问题。
3. 可重复读
可重复读则是在事务开始的时候会先获取一个当前时刻数据的快照,并且在整个事务的过程中都会从这个快照中读取数据,这个就是可重复读。
可重复读可以解决不可重复读的问题,因为不可重复读针对的是某条数据本身,而可重复读会对数据本身做快照处理,所以可以解决不可重复读的问题。
而为什么不可以解决幻读的问题呢?
这个我们后面再介绍。
4. 串行化
串行化是最高的隔离级别,在这个隔离级别下,会将分别对读操作和写操作加锁,当一个事务正在执行,其他事务必须等前一个事务执行完毕之后才能执行。
在这个隔离级别下,可以解决前面并发事务带来的所有问题,包括不可重复读和幻读,但同时,这种方式也会降低数据库的并发性能,因为事务需要按照其他事务释放锁才能执行。
注意:MySQL 默认的隔离级别是可重复读。
6、可重复读隔离级别为什么不可以解决幻读的问题
接着来说一说为什么可重复读隔离级别不可以解决幻读的问题。
在说明这个问题的原因之前,先来介绍一下快照读和当前读。
1. 快照读和当前读
1) 快照读
快照读指的是在事务开始的时候,事务会创建一个数据的快照,在接下来这个事务的整个过程中,都会使用这个快照来读取数据。
2) 当前读
当前读则是指在读取数据时,直接读取库里最新的数据,而不使用事务开启时创建的快照数据。
2. 使用快照读和当前读的场景
在 MySQL 中,除了普通的 SELECT 查询语句是快照读,UPDATE、INSERT、DELETE 操作都是当前读,也就是对数据进行更新、插入和删除的时候都是会查询到数据库最新的数据然后进行操作。
除此之外,对 SELECT 操作进行加锁操作也是当前读,比如共享锁 select ... lock in share mode 和排他锁 select ... for update,这个我们后面再介绍。
3. 可重复读隔离级别下的幻读操作
我们可以通过下面一个例子来进行阐述,在可重复读隔离级别下幻读操作是如何产生的。
比如有两个事务,分别是事务 A 和事务 B,A 事务开启后,查询数据库中的数据,这个时候事务 B 开启,并且使用 INSERT 插入一条数据并提交,在这之后,A 事务对数据库中的数据进行一个 UPDATE 全量数据的操作,之后再进行一个 SELECT 的操作。
下面的代码示例,我们用前面的 t 序号作为执行的时间:
create table users (
id int not null auto_increment primary key,
name varchar(20) not null
);
INSERT INTO users (id, name) values(1, "张三");
-- t1 开启事务A
START TRANSACTION;
-- t2 查询数据
SELECT * from users;
-- t6 查询数据
SELECT * from users;
-- t7 更新全部数据
UPDATE users SET name = "王五" WHERE id >= 1;
-- t8 查询数据
SELECT * FROM users;
-- t9 提交事务A
COMMIT;
-- t3 开启事务B
START TRANSACTION;
-- t4 事务B插入一条数据
INSERT INTO users (id, name) values(2, "李四");
-- t5 提交事务B
COMMIT;
对于上面的代码,我们分别开启两个 MySQL 终端然后按照时间顺序执行,可以看到以下输出:
事务 A 的整体操作如下:

事务 B 的整体操作如下:

可以看到事务 A 在第二次进行查询的时候数据就会新增一条,和第一次查询的时候数据不一致了,这个过程就产生了幻读。
4. 如何解决幻读问题
前面介绍了即便是可重复读隔离级别下,也还是会可能产生幻读问题,那么如何解决幻读问题呢,本质上还是加锁,比如在查询数据的时候使用 select for update 操作对查询的数据加上间隙锁,这样就可以避免其他事务插入新的数据,关于锁的概念和使用,我们在后面再详细介绍。
如果想获取更多相关文章,可扫码关注阅读:

MySQL面试必备三之事务的更多相关文章
- [转帖]MySQL的又一神器-锁,MySQL面试必备
MySQL的又一神器-锁,MySQL面试必备 https://segmentfault.com/a/1190000020762791 lock 低一级的是 latch 原文链接:blog.ouya ...
- MySQL 系列(三)事务
MySQL 系列(三)事务 一组要么同时执行成功,要么同时执行失败的 SQL 语句.是数据库操作的一个执行单元! 事务开始于: 连接到数据库上,并执行条 DML 语句(INSERT. UPDATE 或 ...
- MySQL 面试必备:又一神器“锁”,不会的在面试都挂了
1 什么是锁 1.1 锁的概述 在生活中锁的例子多的不能再多了,从古老的简单的门锁,到密码锁,再到现在的指纹解锁,人脸识别锁,这都是锁的鲜明的例子,所以,我们理解锁应该是非常简单的. 再到MySQL中 ...
- MySQL 学习(三)事务学习
事务隔离级别 SQL标准的事务隔离级别包括:读未提交(read uncommitted).读提交(read committed).可重复读(repeatable read)和串行化(seria ...
- MySQL数据库之大厂面试必备技能v8.0.27
概述 **本人博客网站 **IT小神 www.itxiaoshen.com 定义 MySQL官方地址 https://www.mysql.com/ MySQL 8系列最新版本为8.0.27,5系列的最 ...
- 可能是全网最好的MySQL重要知识点 | 面试必备
可能是全网最好的MySQL重要知识点 | 面试必备 mp.weixin.qq.com 点击蓝色“程序猿DD”关注我 回复“资源”获取独家整理的学习资料! 标题有点标题党的意思,但希望你在看了文章之后 ...
- 面试必备的10道MySQL题
MySQL 事务,是我们去面试中高级开发经常会被问到的问题,很多人虽然经常使用 MySQL,SQL 语句也写得很溜,但是面试的时候,被问到这些问题,总是不知从何说起.下面我们先来了解一下什么是 MyS ...
- Spring事务专题(三)事务的基本概念,Mysql事务处理原理
前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为二者毕竟没有什么实际关系,实际上如果你对MySQL的事务原理不感兴趣也可 ...
- MySQL 系列(三)你不知道的 视图、触发器、存储过程、函数、事务、索引、语句
第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...
- MySQL学习(三)MySQL锁与事务
本章我们着重讨论MySQL锁机制的特点,常见的锁问题,以及解决MySQL锁问题的一些方法或建议. 一.MySQL锁概述 相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支 ...
随机推荐
- PriorityQueue和PriorityBlockingQueue
目录 简介 PriorityQueue PriorityBlockingQueue PriorityQueue和PriorityBlockingQueue 简介 Queue一般来说都是FIFO的,当然 ...
- JS 取Json中对象特定属性值
解析JSON JSON 数据 var str = '[{"a": "1","b": "2"}, {"a&quo ...
- 在 macOS Big Sur 上安装 MySQL 8.x Compressed TAR Archive
因个人能力有限,文章难免存在错误,望斧正,感谢. 版本: macOS 11.6 Intel版本 MySQL 8.0.26 Compressed TAR Archive 一.准备所需文件 前往 MySQ ...
- Canvas图形编辑器-数据结构与History(undo/redo)
Canvas图形编辑器-数据结构与History(undo/redo) 这是作为 社区老给我推Canvas,于是我也学习Canvas做了个简历编辑器 的后续内容,主要是介绍了对数据结构的设计以及His ...
- 剑指offer29(Java)-顺时针打印矩阵(简单)
题目: 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字. 示例 1: 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5 ...
- 基于 MaxCompute 的实时数据处理实践
简介: MaxCompute 通过流式数据高性能写入和秒级别查询能力(查询加速),提供EB级云原生数仓近实时分析能力:高效的实现对变化中的数据进行快速分析及决策辅助.当前Demo基于近实时交互式BI ...
- [GPT] Vue 的 methods 中使用了 addEventListener,如何在 addEventListener 的匿名函数参数中访问 Vue data 变量
在 Vue 的 methods 方法中使用 addEventListener时,你可以使用 箭头函数 来访问 Vue 实例的数据. 箭头函数不会创建自己的作用域,而是继承父级作用域的上下文.以下是 ...
- dotnet 使用 TaskTupleAwaiter 同时等待多个任务简化代码写法
在某些业务逻辑下,需要同时等待多个任务执行完成,才能继续往下执行后续逻辑.等待任务执行的逻辑,大部分情况下需要使用到 Task.WhenAll 方法,代码行数不少.另外,在需要获取多个异步任务的返回值 ...
- vue使用vant的van-tabs+tag在选项卡展示该内容有几条的提示
1.直接写用v-if判断下标展示,会滚动.pass! 2.定位,各种定位,相对各种父元素各种定位,还是会滚,因为tab内容一定滚动,pass 3.手写选项卡+v-if判断,这肯定可行,但本着能用ui组 ...
- webapi授权认证
webapi授权认证 一.需要类包 Microsoft.AspNetCore.Authentication.JwtBearer 二.相关名词 Authentication(认证):标识用户的身份,一般 ...