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

发布时间:2026/5/23 2:59:37

20.MySQL事务隔离级别示例详解(脏读、不可重复读、幻读) 目录一、上节课复习1. 事务相关概念回顾2. 事务隔离级别与问题对应关系二、视图~~三、隔离级别示例结合操作步骤截图1. 准备表与数据2. 查看/设置全局隔离级别3. 脏读Read Uncommitted客户端1操作客户端2操作4. 读已提交Read Committed客户端1操作客户端2操作5. 不可重复读Repeatable Read步骤1客户端2开启事务并第一次查询​步骤2客户端1修改数据并提交​步骤3客户端2在同一事务内再次查询​6. 解决不可重复读调整隔离级别为可重复读客户端2操作7. 幻读Phantom Read步骤1客户端2开启事务并第一次查询​步骤2客户端1插入新数据并提交​步骤3客户端2在同一事务内再次查询​8. 串行化Serializable客户端1操作客户端2操作四、总结结合所有逻辑一、上节课复习1. 事务相关概念回顾事务是什么事务的基本特性ACID事务的使用保存点~~事务的隔离级别重点难点2. 事务隔离级别与问题对应关系隔离级别可能出现的问题读未提交脏读、不可重复读、幻读读已提交不可重复读、幻读可重复读幻读串行化无但性能低上节课操作过程中修改的隔离级别看起来没有生效~~和 navicat 有关~~ 修改的隔离级别需要把 navicat 的连接断开重新连接才能生效。已经和 mysql 服务器建立好的连接没有受到影响二、视图~~针对查询语句封装~~三、隔离级别示例结合操作步骤截图1. 准备表与数据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. 查看/设置全局隔离级别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操作start TRANSACTION; update bank_account set balance 10000 where name 张三; select * from bank_account; -- 不去提交数据看另一个客户端的事务是否能够读取到。 -- commit; 先不提交客户端2操作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操作start TRANSACTION; update bank_account set balance 10000 where name 张三; select * from bank_account; -- 不提交看客户端2读取结果客户端2操作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开启事务并第一次查询​start TRANSACTION; select * from bank_account; -- 第一次读张三余额1000 select * FROM bank_account; -- 再读一次还是1000 commit;步骤2客户端1修改数据并提交​start TRANSACTION; update bank_account set balance 10000 where name 张三; select * from bank_account; -- 看到10000 commit; -- 提交事务步骤3客户端2在同一事务内再次查询​-- 第三步在同一个事务内部再次读取~~ select * FROM bank_account; -- 第二次读张三余额变成10000不可重复读 commit;客户端2中同一个事务之内两次读取到的结果不相同这就是不可重复读。6. 解决不可重复读调整隔离级别为可重复读把隔离级别调整为REPEATABLE READ后重复上述操作客户端2操作start TRANSACTION; select * from bank_account; -- 第一次读张三1000 select * FROM bank_account; -- 第二次读还是1000不可重复读问题解决 commit;相同的操作步骤下第二个客户端第二次读取结果仍然是 1000不可重复读问题就解决了~~7. 幻读Phantom Read步骤1客户端2开启事务并第一次查询​start TRANSACTION; select * from bank_account; -- 第一次读无赵六步骤2客户端1插入新数据并提交​start TRANSACTION; insert into bank_account values(null, 赵六, 1000); -- 插入新行 select * from bank_account; -- 看到赵六 commit;步骤3客户端2在同一事务内再次查询​select * FROM bank_account; -- 这次查询结果中没有包含赵六未出现幻读可重复读隔离级别本身已经处理掉大部分的幻读情况了~~8. 串行化Serializable把隔离级别提升到SERIALIZABLE后测试幻读客户端1操作start TRANSACTION; insert into bank_account values(20, 赵六, 1000); -- 插入 select * from bank_account; commit;客户端2操作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 UNCOMMITTED→READ COMMITTED→REPEATABLE READ→SERIALIZABLE隔离级别越高并发性能越低但数据一致性越强。

相关新闻