欢迎来到代码驿站!

Mysql

当前位置:首页 > 数据库 > Mysql

深入理解MySQL事务的4种隔离级别

时间:2023-02-24 09:16:07|栏目:Mysql|点击:

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:

Untitled

事务B:

Untitled

? 读未提交是隔离级别最低的,会造成脏读。

4.2 读已提交

为了避免脏读,数据库有了比读未提交更高的隔离级别,即读已提交

对于提交:当前事务只能读取其它事务已提交的数据,未提交事务的数据读取不到。

事务A:

Untitled

事务B:

Untitled

由此可以得出结论,隔离级别设置为已提交读(READ COMMITTED)
时,已经不会出现脏读问题了,当前事务只能读取到其他事务提交的数据。但是,站在事务A的角度想想,存在其他问题吗?

提交读的隔离级别会有什么问题呢?

在同一个事务A里,相同的查询sql,读取同一条记录(id=1),读到的结果是不一样的,即不可重复读。所以,隔离级别设置为read committed的时候,还会存在不可重复读的并发问题。

4.3 可重复读

为了避免不可重复读的并发问题,我们将隔离级别设置为可重复读(REPEATABLEE READ),重复一下之前的操作。

事务A:

Untitled

事务B:

Untitled

到了这一步,可以发现事务隔离级别设置为可重复读,可以解决幻读问题。

那么可重复读真的是否已经解决了幻读问题呢?毕竟还剩个事务隔离级别呢。

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 - 博客园

上一篇:一文搞懂MySQL XA如何实现分布式事务

栏    目:Mysql

下一篇:MySQL 数据库的基础知识

本文标题:深入理解MySQL事务的4种隔离级别

本文地址:http://www.codeinn.net/misctech/226360.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有