MySQL事务隔离级别

今天回顾一下MySQL的事务隔离级别,MySQL的事务隔离级别分为以下四种:

  1. read uncommitted : 读未提交,即脏读。一个事务可以读取到另一个事务修改并未提交的数据;
  2. read committed : 读已提交,可以解决脏读。一个事务修改并提交之后才能被另一个事务读取(不可重复读);
  3. repeatable read : 可重复读。在一个事务的两次查询中,被另一个事务修改了数据,两次查询结果一致(出现幻读);
  4. serializable : 串行化,可以解决脏读、不可重复读、幻读。相当于锁表。

接下来使用两个窗口,同一个用户,进入到mysql来验证上面的四种隔离级别。首先创建一个表,添加两条测试数据,如下所示:

MySQL事务隔离级别_第1张图片

设置和查看隔离级别的语句如下:

--设置隔离级别 read uncommitted | read committed | repeatable read | serializable
set session transaction isolation level read uncommitted;
--查看隔离级别
show variables like '%isolation';

开启和结束事务的语句如下

--开启一个事务,如果不写则默认自动提交
begin;
--结束一个事务
commit; | rollback;

一、读未提交

总体流程为下图

MySQL事务隔离级别_第2张图片

可以看到,事务 A 在还没有提交数据的时候,事务 B 就已经能够查询到为提交的数据了。

二、读已提交

首先在 A B 窗口设置隔离界别为读已提交

以同样的步骤可以看出,在 A 没有提交时,在B窗口时查询不到最新的数据的。

MySQL事务隔离级别_第3张图片

开始介绍的时候说过,这种隔离级别会产生不可重复读的情况,也就是说,当 B 在一次事务中 连续查询两次,在这两次查询中间,事务A 对数据进行了更改,那么 在同一个事务 B 中的两次查询结果不一致,若要测试 需在B中也开启事务,总体如下:

MySQL事务隔离级别_第4张图片

可以看到 在同一个 B 事务中的两次查询之间,由于事务A对数据进行修改并提交,导致两次查询的结果不一致。危害自行领悟。

三、可重复读

把 A B 两个窗口的事务隔离级别都设置为 repeatable read 。然后进行测试。

MySQL事务隔离级别_第5张图片

由上图测试可知,隔离级别设置为 可重复读之后。即使在B事务的两次查询之间 A 事务对数据行进修改并提交,B 事务第二次查询的数据和第一次仍然相同。结束本次事务之后,再次查询,方为最新值。

但此种隔离级别会出现幻读的情况,什么是幻读呢,这个不太好解释,直接用测试说明,如下图:

MySQL事务隔离级别_第6张图片

这个测试步骤即过程如下:

  1. B 窗口 开启了一个事务,并查询 是否有 ID 为3 的数据,查询为空,准备添加一条 ID为3 的数据。
  2. 此时 A 窗口开启了一个事务,添加了一条ID为3的数据,并且提交了。
  3. 此时,事务 B 开始添加 ID 为3的数据,发现表中已经有了一条 ID 为3的数据,从而无法添加。
  4. 由于 repeatable read 支持可重复读,所以当事务 B 再次查询 ID 为3的数据的时候,返现还是没有(一个事务中)。

对于事务 B 来说,明天查不到数据,但当添加数据时,却被提示 ID 为3的数据已存在,这就是幻读。相信通过这个例子要比讲一些原理更容易理解和记忆。当然如果想要解决这个问题,可以手动加锁的方式,即在事务 B 首次查询的时候,在查询语句后加上 for update  即可实现加锁的效果,代码如下:

select * from user for update;

四、串行化

串行化可以解决幻读的问题,当然了,前面的问题对于它也就不是问题了,直接通过测试说明:

MySQL事务隔离级别_第7张图片

测试说明:

  1. 首先在B窗口开启 事务B;
  2. 然后在 A 窗口开启事务A;
  3. 事务 B 查询 ID 为5的数据,发现返回结果为空,准备添加第五条数据;
  4. 此时,事务 A 想使坏,想提前添加一条 ID 为5的数据,发现一致处于阻塞状态,无法添加;
  5. 然后事务 B 就有了时间,添加 ID 为5的数据,并成功提交;
  6. 事务 B 刚刚提交,事务 A 中刚刚被阻塞的语句就立马报了一个错,被告知主键已存在。

由此可知,在串行化的隔离级别下,innoDB 会自动加锁,以保证我们数据的安全。MySQL的四种事务隔离级别就到这里。

 

你可能感兴趣的:(MySQL)