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

相关推荐
小白勇闯网安圈4 小时前
Django 响应对象、文件上传与类视图
数据库·django·sqlite
努力的小雨4 小时前
同一份 SQL 跑多套环境:Ksql 变量怎么用才安全
数据库
嘻哈∠※5 小时前
0061基于 SpringBoot 的投稿与稿件处理系统设计与实现
java·spring boot·后端
科技绘图5 小时前
快鲸GEO vs 传统AI搜索优化:全链路自动化与高效内容生产在转化闭环上的对比
数据库·人工智能·自动化
ltl5 小时前
数据库作为 LLM 记忆体:语义缓存、RAG 与一致性
数据库
ltl5 小时前
WAL 与崩溃恢复:ARIES 协议怎么跑通
数据库
ltl5 小时前
TEE 数据库:EnclaveDB、Oblivious 原语与机密 SQL
数据库
白狐_7986 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2586 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
ShineWinsu6 小时前
对于C++:C++11中lambda、function、bind的解析
c++·面试·笔试·开发·lambda·bind·function