案例:
操作步骤及解读:
1 1. 创建数据库: 2 create database ceshi; 3 4 2.创建表: 5 6 CREATE TABLE test_table ( 7 id INT PRIMARY KEY, 8 value VARCHAR(100) 9 ); 10 插入数据 11 INSERT INTO test_table (id, value) VALUES (1, 'yujian'); 12 13 3.查看当前隔离级别 14 当前会话: SELECT @@session.transaction_isolation; 15 全局: SELECT @@global.transaction_isolation; 16 17 4.设置会话就是在当前页面生效,其它页面不生效。我们需要创建两个会话页面。(都是可重复读级别。) 18 19 5.配置读取未提交级别,两个会话都设:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 20 21 脏读: 22 23 脏读指的是读当前事务读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据。 24 6.首先我们在第一张会话里面创建事务并修改数据: 25 开启事务:START TRANSACTION; 26 修改数据:UPDATE test_table SET value = 'llnb' WHERE id = 1; 27 28 7.然后我们在第二张会话里可以查到第一张刚刚修改的数据,但是第一张会话的事务并没有提交 29 30 31 不可重复读: 32 不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况 33 34 8.我们先回滚一下,回到最初的状态:ROLLBACK; 35 9.在两张会话里面都创建事务:START TRANSACTION; 36 37 10.在第一张会话里查一下,会发现id为1的字段里是yujian 38 11.然后在第二张会话里修改字段并提交事务: 39 40 UPDATE test_table SET value = 'llnb' WHERE id = 1; 41 COMMIT; 42 12.对比第一张会话前后两次查询到的数据,会发现两次查询的数据是不一样的。 43 44 45 幻读: 46 47 幻读就是select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入 48 49 13.先回滚一下:ROLLBACK; 50 14.在一二张会话里开启事务:START TRANSACTION; 51 15.在第一张会话里查询所有记录 52 SELECT * FROM test_table; 53 16.然后在第二张会话里新增第二条记录并提交 54 55 INSERT INTO test_table (id, value) VALUES (2, 'llnb'); 56 COMMIT; 57 58 17.在第一张会话里查询所有,会发现原本只有一条记录的表多了一条记录
补充:
在 MySQL 中事务的隔离级别有以下 4 种:
- 读未提交(READ UNCOMMITTED)
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ)
- 序列化(SERIALIZABLE)
MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ),这 4 种隔离级别的说明如下。
1.READ UNCOMMITTED
读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
2.READ COMMITTED
读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
3.REPEATABLE READ
可重复读,是 MySQL 的默认事务隔离级别,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读 (Phantom Read)。
4.SERIALIZABLE
序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。
补充2:
1.事务相关的常用命令
# 查看 MySQL 版本
select version();
# 开启事务
start transaction;
# 提交事务
commit;
# 回滚事务
rollback;
脏读幻读不可重复读详细补充3:
1 # 脏读,不可重复读,幻读 出现的原因是什么? 2 -事务隔离级别不同,而并发开启事务操作数据,就会出现不同的问题 3 4 #脏读: 5 脏读指的是读当前事务读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读 6 7 # 不可重复读: 8 -解释:不可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据出现不一致的情况 9 -导致的原因:事务 A 多次读取同一数据,但事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致 10 11 # 幻读:解决了不可重复读的问题了 12 #幻读错误的理解 13 幻读是 事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到 10 条记录,select 2 得到 15 条记录。 这其实并不是幻读,既然第一次和第二次读取的不一致,那不还是不可重复读吗,所以这是不可重复读的一种。 14 15 #正确的理解 16 幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。 17 18 更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读 19 20 21 # 每个隔离级别解决了什么问题 22 -Read uncommitted(读未提交)-ru:存在脏读,不可重复读,幻读 23 -Read committed(读已提交)-rc:解决了脏读,但是存在不可重复读和幻读 24 -Repeatable read(可重复读取)-rr:解决了脏读,不可重复读问题,存在幻读问题 25 -Serializable(串行化):解决了脏读,不可重复读和幻读问题,牺牲了效率 26 27 28 # mysql 5.7 默认隔离级别 29 -REPEATABLE READ(可重复读)-RR-解决了脏读和不可重复读,存在幻读 30 # Oracle仅支持两种隔离级别: 31 Read Committed:读已提交 32 Serializable:默认基本为RC 存在不可重复读问题