浅谈事务隔离级别

2020-03-31  本文已影响0人  AD刘涛
思考?
  1. 事务并发处理可能存在的三种异常有哪些?什么是脏读、不可重复读和幻读?
  2. 针对可能存在的异常情况,四种事务隔离的级别分别是什么?
  3. 如何使用 MySQL 客户端来模拟脏读、不可重复读和幻读?

四种隔离级别

这四种隔离级别从低到高分别是:

这些隔离级别能解决的异常情况如下表所示:

隔离级别 脏读 不可重复读 幻读
读未提交 允许 允许 允许
读已提交 禁止 允许 允许
可重复读 禁止 禁止 允许
可串行化 禁止 禁止 禁止

事务并发处理可能存在的三种异常有哪些?

在了解数据库隔离级别之前,我们需要了解设定事务的隔离级别都要解决哪些可能存在的问题,也就是事务并发处理时会存在哪些异常情况

这些异常情况级别分别为:

如何理解脏读?

下面我们通过案例进行演示:

  1. 首先我们通过以下命令先来查看下当前会话的隔离级别
mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)

然后你能看到当前的隔离级别REPEATABLE-READ,也就是可重复读

  1. 我们修改当前事务隔离级别,将其改为READ UNCOMMITTED:

在mysql客户端输入以下指令:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
  1. 查看当前事务隔离级别:
mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+------------------+
| Variable_name         | Value            |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set (0.00 sec)
  1. 关闭自动提交并查看是否已更改(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. 我们先在客户端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. 我们用客户端 1 来查看 id=1 的英雄:
mysql> select name from heros_temp where id = 1;
+--------+
| name   |
+--------+
| 张飞   |
+--------+
1 row in set (0.00 sec)
  1. 此时我们在客户端 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. 我们在客户端 1 在进行查看同一条记录:
mysql> select name from heros_temp where id = 1;
+-----------+
| name      |
+-----------+
| 张翼德    |
+-----------+
1 row in set (0.01 sec)

你会发现此时,在同一个事务中同一条查询语句出现的结果竟然不一样。这样的一种行为,我们称作不可重复读

模拟幻读

  1. 在了解幻读之前,我们先恢复当前事务隔离级别为Repeatable Read

  2. 然后我们打开两个mysql客户端:

我们现在分别在客户端1,2开启事务:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
  1. 我们在客户端2搜索一条不存在的记录,比如:
select * from heros_temp where id = 3000;

结果如下:


mysql> select * from heros_temp where id = 3000;
Empty set (0.00 sec)

  1. 此时我们在客户端1插入该条记录:

mysql> INSERT INTO `heros_temp` VALUES (3000, '刘备');
Query OK, 1 row affected (0.00 sec)
  1. 然后我们在去客户端2查询,结果如下:
mysql>  select * from heros_temp where id = 3000;
Empty set (0.00 sec)
  1. 我们此时提交客户端1的数据:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
  1. 然后我们再次去客户端2查询,结果如下:
mysql>  select * from heros_temp where id = 3000;
Empty set (0.00 sec)

客户端1的数据已经提交,但我们在客户端2依旧无法查到。

  1. 见证奇迹的时候到了:我们在客户端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,但我们却更新成功了。

  1. 此时我们再次查询,结果出来了。
mysql>  select * from heros_temp where id = 3000;
+------+-----------+
| id   | name      |
+------+-----------+
| 3000 |  曹植      |
+------+-----------+
1 row in set (0.00 sec)

以上就是幻读,所以幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。

不可重复读 VS 幻读的区别:

不可重复读是同一条记录的内容被修改了,重点在于UPDATEDELETE

幻读是查询某一个范围的数据行变多了或者少了,重点在于INSERT.

所以,SELECT显示不存在,但是INSERT的时候发现已存在,说明符合条件的数据行发生了变化,也就是幻读的情况,而不可重复读指的是同一条记录的内容被修改了。

不可重复读是对于同一条记录内容不可重复读幻读是对于某一范围的数据集,发现查询数据集的行数多了或者少了,从而出现的不一致。所以不可重复读的原因是 对于要查询的那条数据进行了UPDATEDELETE,而幻读是对于要查询的 那个范围的数据集,进行了INSERT

总结:
读未提交:在这个隔离级别下,事务A会读到事务B未提交的数据,在事务B回滚后,事务A读到的数据无意义,是脏数据,称为 脏读

读已提交:在这个隔离级别下,只有在事务B已提交时,事务A才能读到,如果事务A先查询id为1的记录,之后事务B修改这条记录并提交事务A再读取两次结果会不一致,所以不可重复读

可重复读:在这个隔离级别下,就算事务B的修改已经提交,事务A读到的数据依旧是一致的。当事务B插入一条新数据并提交之后,事务A查询不到当前数据,查询不到就以为不存在,但是事务A却可以更新这条数据成功,并且更新后再次查询,数据出现了。一开始查询不到,但能修改,再次查询又出现了,跟幻觉一样,所以称为幻读

幻读

上一篇 下一篇

猜你喜欢

热点阅读