| 万叶集 |
|---|
| 🎉 隐约雷鸣,阴霾天空。 🎉 |
| 🎉 但盼风雨来,能留你在此。 🎉 |
前言:
✌ 作者简介:渴望力量的哈士奇 ✌,大家可以叫我 🐶哈士奇🐶 ,一位致力于 TFS - 全栈 赋能的博主 ✌
🏆 CSDN博客专家认证、新星计划第三季全栈赛道 MVP 、华为云享专家、阿里云专家博主 🏆
📫 如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
💬 人生格言:优于别人,并不高贵,真正的高贵应该是优于过去的自己。💬
🔥 如果感觉博主的文章还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主哦
专栏系列(点击解锁) 学习路线(点击解锁) 知识定位 🔥Python全栈白皮书🔥 零基础入门篇 以浅显易懂的方式轻松入门,让你彻底爱上Python的魅力。 语法进阶篇 主要围绕多线程编程、正则表达式学习、含贴近实战的项目练习 。 自动化办公篇 实现日常办公软件的自动化操作,节省时间、提高办公效率。 自动化测试实战篇 从实战的角度出发,先人一步,快速转型测试开发工程师。 数据库开发实战篇 掌握关系型与非关系数据库知识,提升数据库实战开发能力。 爬虫入门与实战 更新中 数据分析篇 更新中 前端入门+flask 全栈篇 更新中 django+vue全栈篇 更新中 拓展-人工智能入门 更新中 网络安全之路 踩坑篇 记录学习及演练过程中遇到的坑,便于后来居上者 网安知识扫盲篇 三天打鱼,不深入了解原理,只会让你成为脚本小子。 vulhub靶场漏洞复现 让漏洞复现变得简单,让安全研究者更加专注于漏洞原理本身。 shell编程篇 不涉及linux基础,最终案例会偏向于安全加固方向。 [待完结] WEB漏洞攻防篇 2021年9月3日停止更新,转战先知社区等安全社区及小密圈 渗透工具使用集锦 2021年9月3日停止更新,转战先知社区等安全社区及小密圈 点点点工程师 测试神器 - Charles 软件测试数据包抓包分析神器 测试神器 - Fiddler 一文学会 fiddle ,学不会倒立吃翔,稀得! 测试神器 - Jmeter 不仅是性能测试神器,更可用于搭建轻量级接口自动化测试框架。 RobotFrameWork Python实现的自动化测试利器,该篇章仅介绍UI自动化部分。 Java实现UI自动化 文档写于2016年,Java实现的UI自动化,仍有借鉴意义。 MonkeyRunner 该工具目前的应用场景已不多,文档已删,为了排版好看才留着。

上一章节我们宏观上的了解了 “事务的ACID” 属性,并通过 “undo” 与 “redo” 日志文件分析了 “事务的ACID” 是如何实现的。接下来我们就从新的视角来学习一下事务机制,那就是 “事务的并发性”。数据库的事务都是并发执行的,因为隔离性的问题会给一些业务场景带来问题。该章节我们主要学习的目的就是学习事务并发执行的条件下,如何修改事务的隔离级别来满足业务的要求。
默认情况下, MySQL 是不允许事务之间相互读取临时数据的,但是在某些特殊的场合,需要让事物之间能够读取到一些临时数据,如此就必须要去修改事务的隔离级别。MySQL 一共有四个事务的隔离级别,如下:
序列 隔离级别 实现功能 1 read uncommitted 读取未提交的数据 2 read commited 读取已提交数据 3 repeatable read 重复读取 4 serializable 序列化
- 四个事务的隔离级别,必须要结合具体的业务场景才能够理解,接下来我们就结合业务场景来逐一分析这四个事务隔离级别的应用。
- 刚刚为大家简单的介绍了 “事务的四种隔离级别” ,其实这四个级别没有一个是万能的。所以我们不能够挑选一个隔离级别将其当做是一个通用的隔离级别,这个是绝对的不可以的。必须要结合具体的业务场景,然后挑选一个最适合这个业务的隔离级别才可以。
- 以下面的真实生活中的抢购高铁票为例。
车次 行程 车厢 座位 余票 状态 G1 北京 - 上海 6 2A 1 可购买 G1 北京 - 上海 6 2B 1 可购买
- 分析:现在有两个事务 “事务A” 与 “事务B” 都想要购买 G1 车次 6号 车厢的 2A 座位,但是现在只有一张余票。
- 分析:“事务A” 先行购买了 2A 坐席的票,这里的可购买状态会变更为 “已售出” ,但是由于 “事务A” 并没有提交事务,所以修改状态的操作只记载在了 “redo” 日志里面,真实的 “数据文件” 并没有发生改变。
- 分析:"事务B"看到了 2A 坐席的票是 “可购买” 状态, “事务B” 也购买了 2A 的坐席票并很快的提交了事务,于是 “数据文件” 中的 2A 坐席的票务状态就变更为了 “已售出”。
- 分析:这个时候的 “事务A” 再去提交事务的时候,发现 2A 坐席的票务状态就变更为了 “已售出”,于是就引发了 “回滚” 的操作。虽说没有产生数据上的歧义,但是购票失败的这种购票体验肯定是相当的差劲。
- 所以再这个场景中,我们就应该允许当前的事务去读取其他事务的临时状态。就比如 “事务B” 发现了 “事务A” 的临时数据购买了 2A 的坐席,就会去购买其他的坐席(比如 2B 坐席),这样就不会引发数据的冲突了。
- 所以在这个场景中,我们就应该使用
"read uncommitted"这种隔离级别,允许读取其他事务未提交的数据。"read uncommitted"语法如下:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- SET SESSION : 设置当前会话的事务隔离级别,仅针对当前会话窗口有效,不是对全局生效。(所谓的会话,就是我们输入 SQL 语句的窗口)
- 1
- 2
- 3
![]()
- 现在第一个会话窗口,开启一个事务,但是并不提交。(以
"修改员工表全体员工的月薪为例。")START TRANSACTION; UPDATE t_emp SET sal=100 ; -- 不提交事务
- 1
- 2
- 3
- 4
- 5
![]()
- 在第二个会话窗口,也开启一个事务,查询 “t_emp” 表中的信息。
START TRANSACTION; SELECT empno, ename, sal FROM t_emp; COMMIT;
- 1
- 2
- 3
- 4
- 5
![]()
- 此时,我们再尝试在第二个会话窗口,将 “事务的隔离级别” 临时修改为 “read uncommitted”
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; START TRANSACTION; SELECT empno, ename, sal FROM t_emp; COMMIT;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
![]()
从上图可以看到,通过在会话窗口临时修改了 “事务的隔离级别” ,我们可以读取到了第一个会话窗口修改的 “sal” 记录。
需要注意的是,该场景下的 “read uncommitted” 隔离级别,仅适用于我们当前买票的场景,而不适用于其他场景。所以当我们想要使用 “read uncommitted” 这个事务的隔离级别的时候,一定要根据具体的业务去分析。
如果符合条件,使用这个隔离级别是没有问题的;如果不如何条件,就需要选择其他的 “事务隔离级别” 了。
- 刚刚我们讲解了 “read uncommitted” 的事务隔离级别,但是在接下来要演示的案例就不能够使用该事务级别了。
- 以银行转账的场景为例,只能让当前事务去读取其他事务提交之后的数据,绝对不能读取其他事务没有提交的临时数据。
比如说当前银行账户有余额 5000元,"事务A" 向账户 "转入 1000元" ,"事务B" 从账户 "支出500元"
分开来看,只有当 "事务A" 与 "事务B" 分别提交事务之后,当前账户的余额读取他们事务提交之后的数据,余额才会变更为 "5500元"如果我们不读取 "事务A" 与 "事务B" 事务提交之后的数据,而是读取提交之前的数据会怎么样?虽然仍然可以读取,但是如果 "事务A" 与 "事务B" 的事务最终没有提交,而是回滚的状态,对于当前账户的余额来说则是很大的一笔损失。所以在使用 "事务的隔离级别" 的时候,一定要结合着实际的业务场景来进行合理的应用。
"read committed"事务级别代表着只能读取其他事务提交的数据"read committed"语法如下:SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
- 1
- 依然以上文我们所使用的 第一个会话窗口 的事务为例。
START TRANSACTION; UPDATE t_emp SET sal=100 ; -- 不提交事务 -- 因为我们并没有提交事务,所以数据表中的真实记录仍然是未修改的记录
- 1
- 2
- 3
- 4
- 5
- 6
- 接下来我们在第二会话窗口临时使用
"read uncommitted"的事务隔离级别,然后读取员工的sal记录SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; START TRANSACTION; SELECT empno, ename, sal FROM t_emp; COMMIT; -- 因为第一个会话窗口,我们并没有提交事务 -- 所以当我们在使用 "read uncommitted" 的 "事务隔离级别"之后,读取到的仍然是之前没有修改的 "员工" 的 "sal" 记录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
![]()
![]()
所以在这里我们就深刻的体会到了
"read uncommitted" 事务隔离级别的重要性,像前面提到的 “转账” 这个例子,就应该使用这种 “事务隔离级别” 。
- 接下来我们看一下 “repeatable read” 事务隔离级别的业务应用案例。
- 在我们网购的过程中可能会出现这样的场景,当我们在下订单之后、支付金额之前,这个商品涨价了。此时我们应该是按照涨价之前的价格去支付呢?还是应该按照涨价之后的价格去支付呢?
- 在这个场景中,当然是按照涨价之前的价格去支付金额了。
所以再这个业务场景中,我们希望当前的事务场景的执行不要受到其他事务的影响,甚至其他事务的结果提交之后也不应影响到我们当前的事务。这个事务的隔离级别就是 " repeatable read " ,也就是 "可重复读",这也就意味着当前事务能读取到的数据记录是事务开始之前的数据,事务开始之后其他事务提交的数据是读取不到的。- 这个技术点其实很容易实现,在事务执行之前,会将数据拷贝到 “undo” 日志里面,在 “repeatable read” 这个事务隔离级别之下。当前事务只去读取 “undo” 日志里的数据记录,根本不会受到其他事务的影响。所以我们所描述的这个网购的过程中,商品涨价了的场景,就应该使用 “repeatable read” 这个事务级别。
- “repeatable read” 代表事务在执行中不会受到其他的事务的影响,在当前的事务中反复的读取数据,得到的结果前后是一致的。
- “repeatable read” 语法如下:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
- 1
- 依然以上文我们所使用的 第一个会话窗口 的事务为例。
START TRANSACTION; UPDATE t_emp SET sal=100 ; COMMIT; -- 这里的事务暂时先不提交 -- 因为我们并没有提交事务,所以数据表中的真实记录仍然是未修改的记录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 接下来我们在第二会话窗口临时使用
"repeatable read"的事务隔离级别,然后读取员工的sal记录SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT empno, ename, sal FROM t_emp; -- 因为第一个会话窗口,并没有提交事务。 -- 所以当我们在使用 "repeatable read" 的 "事务隔离级别"之后,读取到的仍然是 "undo" 日志的数据记录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
![]()
![]()
- 此时我们返回第一个会话窗口,将当前会话的 “UPDATE” 事务提交
START TRANSACTION; UPDATE t_emp SET sal=100 ; COMMIT; -- 这里的启动事务与 "UPDATE" 语句,其实已经执行过了 -- 所以我们直接执行 "COMMIT" ,提交事务即可 -- 因为我们提交事务了事务,所以数据表中的真实记录已经全部修改为了 100
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 这个时候,再次返回至第二个会话窗口,执行我们的查询记录的事务
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT empno, ename, sal FROM t_emp;
- 1
- 2
- 3
- 4
- 5
- 6
![]()
- “serializable” 的事务隔离级别,一但设置了这种隔离级别。当前会话里的事务,就必须等待其他事务结束之后,才可以继续执行。
- 也就是不再会出现并发型的事务了,虽然说 “序列化” 执行事务能够避免一些业务场景上的问题,但是随之而来的事数据库的并发性上的下降。
- 基于这种情况,这种 “事务隔离级别” 是很少使用的。
- “serializable” 的语法如下:
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- 1
- 现在第二个会话窗口临时使用
"serializable "的事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 执行该 "serializable " 的 "事务隔离级别" 后,当前的 "SET SESSION" 就是序列化执行事务的状态了
- 1
- 2
- 3
- 在 第一个会话窗口 的事务为例。
START TRANSACTION; UPDATE t_emp SET sal=100 ; COMMIT; -- 先执行 "手动启动事务" 与 "UPDATE" 语句 -- 这里的 "COMMIT;" 先不执行,事务暂时先不提交
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 接下来我们在第二会话窗口临时使用
"serializable "的事务隔离级别SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; START TRANSACTION; SELECT empno, ename, sal FROM t_emp; COMMIT; -- 这里的设置 "REPEATABLE READ" 事务不用再执行,因为之前我们已经执行过了 -- 直接执行 "手动启动事务",再执行 "SELECT" 语句
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
![]()
- 当我们执行了 第一个会话窗口的事务 “提交”、或 “回滚” 的时候,第二个会话窗口的 “SELECT” 语句才可以查到记录。
![]()
该章节为大家讲了一下在不同的业务场景,我们应该选择什么样的 "事务隔离级别" 。相信大家也通过这篇文章搞明白了 "事务隔离的特性" ,因为我通过查询资料来看,发现很多的文章也好、教材也好,在讲述到 "隔离性" 的时候引入了类似 "脏读、幻读、不可重复读" 的知识点。说实话,除了把人搞得头晕眼花之外,收效甚微。
所以在这一章节,我们就回避了 "脏读、幻读、不可重复读" 的知识点,通过业务分析、案例练习的方式为大家讲解在什么场景下应该使用什么样的 "事务隔离级别",相信大家通过研读与练习也都能学明白,奥利给!