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,隔离级别越高,并发性能越低,但数据一致性越强。

相关推荐
实在智能RPA3 分钟前
航空Agent落地效果评估指标:2026年企业级智能自动化价值度量体系拆解
java·网络·人工智能·ai·自动化
程序员二叉9 分钟前
【JUC】AQS底层深度拆解|独占/共享模式|队列原理全详解
java·开发语言·面试·juc
啦啦啦啦啦zzzz12 分钟前
redis的持久化操作和主从复制与集群的关系及其应用
数据库·redis
地铁潜行者14 分钟前
消息堆积后,为什么一扩容消费者,MySQL 先被打崩了?
java·后端
地铁潜行者18 分钟前
订单状态更新成功了,分账消息却没发出去:聊聊本地消息表的一致性坑
java·后端
亦暖筑序18 分钟前
Java 8老系统SQL Agent实战:AI生成候选SQL,安全引擎拦截后再执行
java·人工智能·sql
CodeStats19 分钟前
《源纹天书》卷一:归元初醒(第1-5章)
java
大囚长23 分钟前
大模型服务端如何命中缓存
java·人工智能·缓存·dubbo
别叫我老干部23 分钟前
一键给整个库造测试数据:外键、约束一个都不能少
后端·mysql
摇滚侠23 分钟前
SpringMVC 入门到实战 拦截器 78-82
java·后端·spring·maven·intellij-idea