深入理解MySQL事务的4种隔离级别
作者:一年春又来 发布时间:2024-01-21 09:52:40
1 简介
事务的4种隔离级别分别是读未提交(Read Uncommitted)、读已提交(Read Committed)、 可重复读(Repeatable Read)和串行化(Serializable)。
首先,在了解这4种隔离级别前就必须先要了解其前提,也就是事务,本文简单介绍一下关于事务。
之后,我们也要理解这4种隔离级别产生的原因和场景展现以及4种隔离级别是如何解决问题的。
2 什么是数据库事务?
事务由一个有限的数据库操作序列组成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
例如一个银行转账场景:
A转账B 100元,A的账号扣除100元,B的账号加上100块。假如中间出现任何异常,例如,在A的账号扣100元时,银行瘫痪,B的账号余额没有发生变化。这时候就需要事务来保证将A的钱还回去。
2.1 事务的四大特性(ACID)
原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。
一致性:指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。
隔离性:多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。
持久性:表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。
3 并发事务会导致的问题
脏读:事务 A 读取了事务 B 更新的数据,然后 B 进行回滚操作,那么A读取的数据就是脏数据
不可重复读:事务A多次读取同一数据,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次夺取同一数据时,结果不一致。
幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
💡 不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。
3.1 本文会使用到的 SQL 语句
3.1.1 示例表结构
CREATE TABLE `account` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`balance` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `un_name_idx` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.1.2 查询事务的默认隔离级别
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.01 sec)
3.1.3 设置当前会话的事务隔离级别
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
4 事务的4种隔离级别和示例演示
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed)又叫读已提交 | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
4.1 读未提交
事务A:
事务B:
💡 读未提交是隔离级别最低的,会造成脏读。
4.2 读已提交
为了避免脏读,数据库有了比读未提交更高的隔离级别,即读已提交。
对于提交:当前事务只能读取其它事务已提交的数据,未提交事务的数据读取不到。
事务A:
事务B:
由此可以得出结论,隔离级别设置为已提交读(READ COMMITTED)
时,已经不会出现脏读问题了,当前事务只能读取到其他事务提交的数据。但是,站在事务A的角度想想,存在其他问题吗?
提交读的隔离级别会有什么问题呢?
在同一个事务A里,相同的查询sql,读取同一条记录(id=1),读到的结果是不一样的,即不可重复读。所以,隔离级别设置为read committed
的时候,还会存在不可重复读的并发问题。
4.3 可重复读
为了避免不可重复读的并发问题,我们将隔离级别设置为可重复读(REPEATABLEE READ),重复一下之前的操作。
事务A:
事务B:
到了这一步,可以发现事务隔离级别设置为可重复读,可以解决幻读问题。
那么可重复读真的是否已经解决了幻读问题呢?毕竟还剩个事务隔离级别呢。
RR隔离级别下,手动启动一个事务,进行select操作,他会生成一个快照,可以理解为将当前数据库的数据复制一份,在当前事务中,之后不管进行多少次select查询,都是在模板中去取数据,所以不管数据库中是否对数据进行了改变,都不会影响当前事务数据的读取,从而避免了幻读。这种普通的 select 操作,称为快照读。
但是如果在当前事务中使用了下图语句进行当前读:
select * from account for update;
for update
是进行当前读的操作,他会重新从数据库去加载当前的最新的数据,每执行一次加载一次,如果在此时,另外一个事务为数据库添加了一个事务,再进行查询,会发现查询的数据与之前相比多了或者少了,这也就是幻读现象。
如果你阅读到这里,去实操一下,会发现和我说的不一样,有一种上当的感觉。
其实不是的,这是因为上述都是在标准的可重复读下的情况,在innodb存储引擎中对可重复读进行了改造,为当前读加上了 Next-key Lock
,也就是间隙锁和行锁的统称,行锁防止了别的事务修改或者删除,间隙锁防止了别的事务新增。也就是在进行上面的for update
事务中,其他的事务不能对数据进行增删操作,执行会报错或者长时间处于等待状态。
💡 注意:如果A事务如果进行了快照读,然后通过B事务对数据就行增删,然后紧接着A事务进行当前读操作,两次读取数据不一致,不能算作幻读,因为幻读定义是同一个select语句,快照读和当前读的查询语句是不一样的.
小结
数据库的并发问题有:脏读、不可重复读和幻读;
事务隔离级别依次为:读未提交、读已提交、可重复读和串行化;
在标准的RR下并没有彻底解决幻读,但是在Mysql的innodb引擎中彻底解决了;
innodb通过 Next-Key lock解决的幻读问题,其实也就是阻塞串行化了;
不能把快照读和当前读在一个事务中进行比较是否出现幻读,两者不是同一个select,不满足幻读的官方定义。
4.4 串行化
略,这部分我懒得放图了,因为结果和上面没啥差别。
文献引用
一文彻底读懂MySQL事务的四大隔离级别 - Jay_huaxiao - 博客园
来源:https://blog.csdn.net/Little_fxc/article/details/124196928


猜你喜欢
- importlib 模块的作用模块,是一个一个单独的py文件 包,里面包含多个模块(py文件)动态导入模块,这样就不用写那么多的import
- 前言本实验中所用数据库创建SQL语句以及插入数据到数据库中的SQL语句链接:链接: https://pan.baidu.com/s/1BnF
- 一. torch.cat()函数解析1. 函数说明1.1 官网:torch.cat(),函数定义及参数说明如下图所示:1.2 函数功能函数将
- Linux+apache+mysql+python+mod_python+Django说明:系统rhel 5.3,默认安装httpd、mys
- 目录abspathbasenamedirnameexistsgetatimegetctimegetsizeisabsisdirisfilei
- 控制台打印时显示的2位小数:pd.set_option('precision', 2)实际修改数据精度:官例:http://
- 1.在浏览器搜索python.org,如下图选择第一个2.进入python官网,选择dowload然后选择windows如下图:3.选择py
- 在.net 1.1中我们要实现压缩这一功能,一般都是用open source的SharpZipLib 或者调用
- 在许多人看来,HTML应该是WEB制作所有语言中最简单的语言,因为它不需要编译、封闭等,甚至只需要一个记事本就可以让其在浏览器中呈现出来。所
- 本文实例讲述了Django框架用户注销功能实现方法。分享给大家供大家参考,具体如下:HttpResponse()里有个delete_cook
- 简介:使用python的过程中肯定少不了读取文件的操作,传统的形式是使用 直接打开、然后在操作、然后再关闭,这样代码量稍微大些不说,一旦在操
- mysql的in会让索引失效吗?不会! 看结果:mysql> desc select * from tb_province where
- 本文实例讲述了JS实现单击输入框弹出选择框效果的方法。分享给大家供大家参考,具体如下:运行效果截图如下:完整实例代码如下:<!DOCT
- 导航栏是一个很好的功能,是Bootstrap 网站的一个突出特点。导航栏是响应式元组件就,作为应用程序或网站的导航标题。导航栏在移动设备的视
- 模块 定义计算机在开发过程中,代码越写越多,也就越难以维护,所以为了编写可维护的代码,我们会把函数进行分组,放在不同的文件里。在python
- 直方图是用于展示数据的分组分布状态的一种图形,用矩形的宽度和高度表示频数分布,通过直方图,用户可以很直观的看出数据分布的形状、中心位置以及数
- 基于上一篇文章,这篇文章是关于使用coverage来实现代码覆盖的操作实例,源代码在上一篇已经给出相应链接。本篇文章字用来实现代码覆盖的源代
- 文章参考自:link.前言最近学习python切片,时而醍醐灌顶时而云里雾里,今天终于弄清楚了 python切片 里面自己经常疑惑的地方,记
- 元组是不可变的Python对象序列。元组的序列就像列表。唯一的区别是,元组不能被改变,即元组是不可被修改。元组使用小括号,而列表
- 在定义类的过程中,无论是显式创建类的构造方法,还是向类中添加实例方法,都要求将 self 参数作为方法的第一个参数。例如,定义一个 Pers