你真的了解mysql事务隔离级别、脏读、不可重复读、幻读吗?

1,063 阅读4分钟

相信大部分人都听说过mysql的事务隔离级别,但有很多人并没有真正了解4种隔离级别的区别到底在哪里。4种隔离级别如下表所示:

ä½ çœŸçš„äº†è§£mysql事务隔离级别、脏读、不可重复读、幻读吗?

下面举一个简单的查询更新某行数据的示例来说明。其中ID字段为主键,事务A和事务B并行按顺序执行,在4种不同隔离级别下会有不同的执行效果,具体流程图如下。

未提交读

read uncommitted,这是事务最低的隔离级别,它允许令外一个事务可以看到这个事务未提交的数据。

流程图01如下:

上图中,事务A第5步更新了age为35后,事务A还没提交前,事务B立即能看到事务A的更新结果。

已提交读

read committed,只有事务修改的数据提交后,才能被另外一个事务读取到。

流程图02如下:

上图中,事务A第5步更新了age为35后,事务A还没提交前,事务B是看不到事务A的更新结果的。只有当事务A提交事务后,事务B才能读取到数据。

可重复读

repeatable read,指的是在同一事务内,不同的时刻读到的同一批数据都是一样的。

流程图03如下:

上图中,事务A第8步提交事务后,更新了age为35到数据库中。事务B再次读取数据age,还是30。也就是说,事务B一开始获取到的数据是30,那在整个事务中,获取到的数据都是一样,不受其它事务影响。这就是“可重复读”了。

可串行化

serializable,在可串行化级别上,执行是一阶段申请锁,一阶段释放锁。读写都要加锁。

流程图04如下:

下面针对不同隔离级别出现的不同问题做下说明,包含:“脏读”、“不可重复读”以及“幻读”

脏读

“未提交读(read-uncommitted)”这种隔离级别是数据一致性最差的一种,会产生“脏读”、“不可重复读”、“幻读”。

以上面“未提交读”中的示例图01进一步说明什么是脏读:

如上图所示,当事务B获取到事务A的更新结果,该结果参与事务B的业务计算后,事务B提交了事务。但是事务A并没有提交事务,而是回滚事务。这种情况下事务B读到的数据就是“脏数据”。

不可重复读

“已提交读(read committed)”这种隔离级别虽然解决了“脏数据”问题。但仍然存在“不可重复读”和“幻读”。从上图示例02中便可理解什么是“不可重复读”。事务A提交事务后,事务B中是可以读到事务A的更新数据的。

幻读

虽然“ 可重复读( repeatable read)”级别,解决了“脏数据”和“不可重复读”的问题。但是它依然存在“幻读”现象,下面以“可重复读”级别下说明什么是幻读:

上图中,事务A插入数据并提交事务后,对事务B来说是查询不到ID为3的数据的。但是当事务B要插入一条ID为3的数据时,会提示“Duplicate entry '3' for key 'PRIMARY'”。这就是幻读的例子。

注意:这边的ID字段为主键

相关示例代码

--示例表
CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--查看当前事务级别
SELECT @@tx_isolation;
--开启事务
start transaction;
--当前连接会话中设置隔离级别语句
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;

update test set age = 35 where id = 1;
insert into test values (3,33);

提示:示例是在Navicat工具的命令列窗口中执行演示的,不是查询窗口哦!

数据库的事务隔离级别可以不同程度的解决事务并发时可能产生的问题,不同的事务隔离级别适应不同业务逻辑需求。等级越高越能保证数据的一致性,但也越降低并发性能。