思考?
- 事务并发处理可能存在的三种异常有哪些?什么是脏读、不可重复读和幻读?
- 针对可能存在的异常情况,四种事务隔离的级别分别是什么?
- 如何使用 MySQL 客户端来模拟脏读、不可重复读和幻读?
四种隔离级别
这四种隔离级别从低到高分别是:
- 读未提交(READ UNCOMMITTED )
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ)
- 可串行化(SERIALIZABLE)
这些隔离级别能解决的异常情况如下表所示:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 允许 | 允许 | 允许 |
读已提交 | 禁止 | 允许 | 允许 |
可重复读 | 禁止 | 禁止 | 允许 |
可串行化 | 禁止 | 禁止 | 禁止 |
事务并发处理可能存在的三种异常有哪些?
在了解数据库隔离级别之前,我们需要了解设定事务的隔离级别都要解决哪些可能存在的问题
,也就是事务并发处理时会存在哪些异常情况
。
这些异常情况级别分别为:
- 脏读(Dirty Read)
- 不可重复读(Nnrepeatable Read)
- 幻读(Phantom Read)
如何理解脏读?
下面我们通过案例进行演示:
- 首先我们通过以下命令先来
查看
下当前会话的隔离级别
:
mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)
然后你能看到当前的隔离级别
是 REPEATABLE-READ
,也就是可重复读
。
- 我们修改当前事务隔离级别,将其改为
READ UNCOMMITTED
:
在mysql客户端输入以下指令:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
- 查看当前事务隔离级别:
mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+------------------+
| Variable_name | Value |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.00 sec)
- 关闭自动提交并查看是否已更改(MySQL 默认是事务自动提交,这里我们还需要将
autocommit
参数设置为0
,命令如下:)
mysql> SET autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.01 sec)
接着我们以同样的操作启动客户端 2,也就是将隔离级别设置为
READ UNCOMMITTED(读未提交)
,autocommit
设置为0
。
模拟脏读:
- 我们先在客户端1开启一个事务,在heros_temp表中写入新增的英雄"赵子龙",注意这个时候
不要提交
。
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `heros_temp` VALUES (4, '赵子龙');
Query OK, 1 row affected (0.00 sec)
我们此时查看客户端2,你会发现此时表中已经存在了此条记录:
mysql> select * from heros_temp;
+----+-----------+
| id | name |
+----+-----------+
| 1 | 张飞 |
| 2 | 关羽 |
| 3 | 刘备 |
| 4 | 赵子龙 |
+----+-----------+
4 rows in set (0.00 sec)
注意我们目前还没有提交,你能发现客户端2中读取了客户端1未提交的新英雄“赵子龙”,实际上客户端 1
可能马上回滚
,从而造成了脏读
。所以,所谓脏读
就是,一个事务读取了另一个事务改写但还未提交
的数据。
模拟不可重复读:
- 我们用客户端 1 来查看 id=1 的英雄:
mysql> select name from heros_temp where id = 1;
+--------+
| name |
+--------+
| 张飞 |
+--------+
1 row in set (0.00 sec)
- 此时我们在客户端 2 对 id=1 的英雄进行修改:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update heros_temp set name="张翼德" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
- 我们在客户端 1 在进行查看同一条记录:
mysql> select name from heros_temp where id = 1;
+-----------+
| name |
+-----------+
| 张翼德 |
+-----------+
1 row in set (0.01 sec)
你会发现此时,在
同一个事务中
,同一条查询语句出现的结果竟然不一样
。这样的一种行为,我们称作不可重复读
。
模拟幻读
-
在了解幻读之前,我们先恢复当前事务隔离级别为
Repeatable Read
。 -
然后我们打开两个mysql客户端:
我们现在分别在客户端1,2开启事务:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
- 我们在客户端2搜索一条
不存在的记录
,比如:
select * from heros_temp where id = 3000;
结果如下:
mysql> select * from heros_temp where id = 3000;
Empty set (0.00 sec)
- 此时我们在客户端1插入该条记录:
mysql> INSERT INTO `heros_temp` VALUES (3000, '刘备');
Query OK, 1 row affected (0.00 sec)
- 然后我们在去客户端2查询,结果如下:
mysql> select * from heros_temp where id = 3000;
Empty set (0.00 sec)
- 我们此时提交客户端1的数据:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
- 然后我们再次去客户端2查询,结果如下:
mysql> select * from heros_temp where id = 3000;
Empty set (0.00 sec)
客户端1的数据已经提交,但我们在客户端2依旧无法查到。
- 见证奇迹的时候到了:我们在客户端2进行数据更新
mysql> update heros_temp set name = '曹植' where id = 3000;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
是不是很惊讶!!我们刚刚在客户端2查询id=3000的记录时,数据显示为
Empty set
,但我们却更新成功了。
- 此时我们再次查询,结果出来了。
mysql> select * from heros_temp where id = 3000;
+------+-----------+
| id | name |
+------+-----------+
| 3000 | 曹植 |
+------+-----------+
1 row in set (0.00 sec)
以上就是幻读
,所以幻读
是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
不可重复读 VS 幻读的区别:
不可重复读
是同一条记录的内容被修改了,重点在于UPDATE
或DELETE
。
幻读
是查询某一个范围的数据行变多了或者少了
,重点在于INSERT
.
所以,SELECT
显示不存在
,但是INSERT
的时候发现已存在
,说明符合条件的数据行发生了变化,也就是幻读的情况,而不可重复读
指的是同一条记录的内容被修改
了。
不可重复读
是对于同一条记录内容
的不可重复读
;幻读
是对于某一范围
的数据集,发现查询数据集的行数多了或者少了
,从而出现的不一致
。所以不可重复读
的原因是 对于要查询的那条数据
进行了UPDATE
或DELETE
,而幻读
是对于要查询
的 那个范围的数据集
,进行了INSERT
。
总结:
读未提交
:在这个隔离级别下,事务A会读到
事务B未提交的数据
,在事务B回滚
后,事务A读到的数据无意义
,是脏数据,称为 脏读
;
读已提交
:在这个隔离级别下,只有在事务B已提交
时,事务A才能读到
,如果事务A先查询id为1
的记录,之后事务B修改这条记录并提交
,事务A再读取
,两次结果会不一致
,所以不可重复读
;
可重复读
:在这个隔离级别下,就算事务B
的修改已经提交
,事务A读到的数据依旧是一致
的。当事务B插入一条新数据并提交之后
,事务A查询不到当前数据
,查询不到就以为不存在,但是事务A却可以更新这条数据成功
,并且更新后再次查询,数据出现了
。一开始查询不到,但能修改,再次查询又出现了,跟幻觉一样,所以称为幻读
。
网友评论