20.MySQL事务隔离级别示例详解(脏读、不可重复读、幻读)

目录

一、上节课复习

[1. 事务相关概念回顾](#1. 事务相关概念回顾)

[2. 事务隔离级别与问题对应关系](#2. 事务隔离级别与问题对应关系)

二、视图(~~)

三、隔离级别示例(结合操作步骤+截图)

[1. 准备表与数据](#1. 准备表与数据)

[2. 查看/设置全局隔离级别](#2. 查看/设置全局隔离级别)

[3. 脏读(Read Uncommitted)](#3. 脏读(Read Uncommitted))

客户端1操作:

客户端2操作:

[4. 读已提交(Read Committed)](#4. 读已提交(Read Committed))

客户端1操作:

客户端2操作:

[5. 不可重复读(Repeatable Read)](#5. 不可重复读(Repeatable Read))

步骤1:客户端2开启事务并第一次查询​

步骤2:客户端1修改数据并提交​

步骤3:客户端2在同一事务内再次查询​

[6. 解决不可重复读(调整隔离级别为可重复读)](#6. 解决不可重复读(调整隔离级别为可重复读))

客户端2操作:

[7. 幻读(Phantom Read)](#7. 幻读(Phantom Read))

步骤1:客户端2开启事务并第一次查询​

步骤2:客户端1插入新数据并提交​

步骤3:客户端2在同一事务内再次查询​

[8. 串行化(Serializable)](#8. 串行化(Serializable))

客户端1操作:

客户端2操作:

四、总结(结合所有逻辑)


一、上节课复习

1. 事务相关概念回顾

  • 事务是什么

  • 事务的基本特性(ACID)

  • 事务的使用

  • 保存点(~~)

  • 事务的隔离级别(重点&难点)

2. 事务隔离级别与问题对应关系

隔离级别 可能出现的问题
读未提交 脏读、不可重复读、幻读
读已提交 不可重复读、幻读
可重复读 幻读
串行化 无(但性能低)

上节课操作过程中,修改的隔离级别,看起来没有生效~~

和 navicat 有关~~ 修改的隔离级别,需要把 navicat 的连接断开,重新连接,才能生效

(已经和 mysql 服务器建立好的连接,没有受到影响)

二、视图(~~)

针对查询语句,封装~~

三、隔离级别示例(结合操作步骤+截图)

1. 准备表与数据

sql 复制代码
use java117_2;
show tables;

select * from bank_account;
delete from bank_account;
desc bank_account;  -- 自己创建一个表(表结构:id int(PK)、name varchar(20)、balance int)

insert into bank_account values(null, '张三', 1000), (null, '李四', 2000), (null, '王五', 3000);

2. 查看/设置全局隔离级别

sql 复制代码
SELECT @@GLOBAL.transaction_isolation;

SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

3. 脏读(Read Uncommitted)

客户端1操作

sql 复制代码
start TRANSACTION;
update bank_account set balance = 10000 where name = '张三';
select * from bank_account;
-- 不去提交数据,看另一个客户端的事务是否能够读取到。
-- commit; (先不提交)

客户端2操作

sql 复制代码
SELECT @@GLOBAL.transaction_isolation;
use java117_2;
start TRANSACTION;
select * from bank_account;  -- 此时能读到客户端1未提交的10000(脏读)
commit;

虽然第一个客户端没有 commit,但是第二个客户端也读取到了 10000 这个结果,此时就是脏读~~

4. 读已提交(Read Committed)

提升隔离级别到 READ COMMITTED后,客户端2再次测试:

客户端1操作

sql 复制代码
start TRANSACTION;
update bank_account set balance = 10000 where name = '张三';
select * from bank_account;
-- 不提交,看客户端2读取结果

客户端2操作

sql 复制代码
SELECT @@GLOBAL.transaction_isolation;  -- 确认是READ COMMITTED
use java117_2;
start TRANSACTION;
select * from bank_account;  -- 此时读不到10000(因为客户端1未提交)
commit;

提升隔离级别到 READ COMMITTED,此时读到的数据是第一个客户端执行事务之前的数据~~

5. 不可重复读(Repeatable Read)

步骤1:客户端2开启事务并第一次查询

sql 复制代码
start TRANSACTION;
select * from bank_account;  -- 第一次读,张三余额1000
select * FROM bank_account;  -- 再读一次,还是1000
commit;

步骤2:客户端1修改数据并提交

sql 复制代码
start TRANSACTION;
update bank_account set balance = 10000 where name = '张三';
select * from bank_account;  -- 看到10000
commit;  -- 提交事务

步骤3:客户端2在同一事务内再次查询

sql 复制代码
-- 第三步,在同一个事务内部,再次读取~~
select * FROM bank_account;  -- 第二次读,张三余额变成10000(不可重复读)
commit;

客户端2中,同一个事务之内,两次读取到的结果不相同 ,这就是不可重复读

6. 解决不可重复读(调整隔离级别为可重复读)

把隔离级别调整为 REPEATABLE READ后,重复上述操作:

客户端2操作

sql 复制代码
start TRANSACTION;
select * from bank_account;  -- 第一次读,张三1000
select * FROM bank_account;  -- 第二次读,还是1000(不可重复读问题解决)
commit;

相同的操作步骤下,第二个客户端,第二次读取结果仍然是 1000,不可重复读问题就解决了~~

7. 幻读(Phantom Read)

步骤1:客户端2开启事务并第一次查询

sql 复制代码
start TRANSACTION;
select * from bank_account;  -- 第一次读,无赵六

步骤2:客户端1插入新数据并提交

sql 复制代码
start TRANSACTION;
insert into bank_account values(null, '赵六', 1000);  -- 插入新行
select * from bank_account;  -- 看到赵六
commit;

步骤3:客户端2在同一事务内再次查询

sql 复制代码
select * FROM bank_account;  -- 这次查询结果中,没有包含赵六(未出现幻读)

可重复读隔离级别,本身已经处理掉大部分的幻读情况了~~

8. 串行化(Serializable)

把隔离级别提升到 SERIALIZABLE后,测试幻读:

客户端1操作

sql 复制代码
start TRANSACTION;
insert into bank_account values(20, '赵六', 1000);  -- 插入
select * from bank_account;
commit;

客户端2操作

sql 复制代码
start TRANSACTION;
select * from bank_account;  -- 第一步查询
-- 第二步,启动一个事务,插入相同id的数据(测试幻读/阻塞)
insert into bank_account values(20, '赵六', 1000);  -- 插入失败(主键冲突)
select * FROM bank_account;
commit;

一个事务没有执行完,第二个事务只能等~~ 过一段时间之后,就会失败~~

客户端1插入失败,因为20主键已经存在(Duplicate entry '20' for key 'PRIMARY'
正常来说,是会直接告诉我们"影响了一行",但串行化下,事务会排队执行,避免并发问题。

四、总结(结合所有逻辑)

  • 脏读:读未提交时,读取到其他事务未提交的数据。

  • 不可重复读:读已提交时,同一事务内两次读取结果不同(其他事务修改了数据)。

  • 幻读:同一事务内,两次查询++结果集行数++不同(其他事务插入/删除数据)。

  • 隔离级别从低到高:READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE,隔离级别越高,并发性能越低,但数据一致性越强。

相关推荐
Dicky-_-zhang8 小时前
KubeEdge边缘部署实践
java·jvm
码银8 小时前
在若依中如何新建一个模块(图文教程)
java·javascript
Yeats_Liao8 小时前
物联网接入层技术剖析(四):当epoll遇见MQTT
java·linux·服务器·网络·物联网·架构
一条大祥脚8 小时前
Codeforces Round 1099 (Div. 2) 构造|贪心|图论|还原数组
java·算法·图论
yaoxin5211238 小时前
414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件
java·windows·python
mCell8 小时前
JavaScript:从事件循环到手写 Promise
javascript·面试·浏览器
huaCodeA8 小时前
Android面试-Flow相关
android·面试·职场和发展
超梦dasgg8 小时前
详细讲解:WebMvcConfigurer 接口
java·开发语言·spring
独泪了无痕8 小时前
MySQL中 JSON 数据类型使用指南
mysql