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

相关推荐
爱写代码的阿森18 分钟前
鸿蒙三方库 | harmony-utils之KvUtil键值型数据库操作详解
数据库·华为·harmonyos·鸿蒙·huawei
马优晨20 分钟前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
Database_Cool_36 分钟前
单机 MySQL 迁移到分布式数据库方便吗?阿里云 PolarDB-X 100% MySQL 协议兼容零改造平滑迁移
数据库·分布式·mysql
醇氧44 分钟前
CountDownLatch / CyclicBarrier / Semaphore 面试高频问答清单
前端·面试·职场和发展
姜太小白1 小时前
【MySQL】 索引优化实战:解决 WHERE 等值 + IS NULL 查询,TEXT 字段报错 1167 的完整指南
数据库·mysql
窝子面2 小时前
手搓最简前后端协作-node
javascript·数据库
吳所畏惧2 小时前
宝塔面板Redis密码修改指南:SSH命令修改 vs 面板UI界面修改,哪个更靠谱?
运维·服务器·数据库·redis·缓存·ssh
DFT计算杂谈2 小时前
无 Root 权限在 Tesla K80 零门槛部署 DeepSeek 大模型
linux·服务器·网络·数据库·机器学习
HPFBoy2 小时前
log4net 数据库存日志正确配置
数据库