MySQL之InnoDB存储引擎:浅谈隔离级别

谈事务的隔离性Isolation,就不得不谈谈所谓的隔离级别

abstract.png

读写冲突

Dirty Write脏写

所谓脏写是指,当一个事务 修改了 另一个未提交事务修改过的数据。如下图两个会话Session各自开启了两个事务。在Session 2的事务其将id为1的name字段更新为Aaron,随后在Session 1的事务又将修改为Bob了。此时如果Session 2事务中发生了回滚。这个时候对于Session 1而言,会发现即使该事务提交了但其所做的修改却没有生效。即出现了所谓的脏写

figure 1.jpg

Dirty Read脏读

如果一个事务 读到了 另一个未提交事务修改过的数据,即会出现脏读。如下图两个会话Session各自开启了两个事务。在Session 2的事务其将id为1的name字段更新为Aaron,然后Session 1的事务中会读到id为1的记录其name字段为Aaron。如果此时Session 2的事务发生了回滚,显然其所做的修改也将被还原。这个时候对于Session 1而言,其相当于读到了一个不存在的数据

figure 2.jpg

Non Repeatable Read不可重复读

假设一个事务 只能读到 其他已经提交事务修改过的数据,则其他事务每次对该数据的修改并提交后,如果该事务都可以读到最新的值,则即是所谓的不可重复读。如下图所示,在Session 2中通过隐式事务提交对数据的修改。而在Session 1的事务中则是每次可以读到其他事务修改后提交的最新的值。这即是所谓的不可重复读

figure 3.jpg

Phantom Read幻读

所谓幻读,指的是在一个事务中,根据查询条件获取到若干条记录,记为R1、R2、…、Rn。随后其他事务又插入了新记录Rm并提交,那么当原来的事务继续使用之前的查询条件继续查询,发现不仅获取到了R1、R2、…、Rn记录,还查询到了其他事务插入、提交的新记录Rm

如下图所示,我们在Session 1中的事务中,通过条件查询获取到一条记录结果。随后Session 2通过隐式事务插入一条id为996的新记录。然后Session 1的事务又执行了相同查询条件的查询,可以看到其结果与第一次查询结果相比,多了一条name字段为Aaron的记录

figure 4.jpg

四种隔离级别

这里对上文所述各类型的读写冲突按问题的严重程度进行排序:脏写 > 脏读 > 不可重复读 > 幻读。可以看到,脏写是最严重的,幻读是最不严重的。为此,SQL标准中提出了下面四种隔离级别。可以看到不同的隔离级别下,其可以避免不同的读写冲突问题。由于脏写的问题过于严重,故在任何隔离级别中均不允许发生

figure 5.jpg

对于上述读写冲突与隔离级别的对应关系,可通过如下口诀进行记忆——“鞋脏不换(写脏不幻),UCRS

  • 写 → 脏写,U → RU未提交读
  • 脏 → 脏读,C → RC已提交读
  • 不 → 不可重复读,R → RR可重复读
  • 幻 → 幻读,S → Serializable串行化

对于任何一隔离级别而言,其亦可解决口诀中之前所出现的问题。例如对于R(即RR)而言,其不仅可以解决不可重复读,还可以解决脏写、脏读的问题

上面介绍的四种隔离级别只是SQL标准定义的,而具体到各数据库厂商来说的话,其所支持的程度是不一样的。就我们这里MySQL数据库的InnoDB引擎而言,其对上述四种隔离级别均是支持的。值得一提的是,对于MySQL的InnoDB引擎而言,在REPEATABLE READ可重复读隔离级别下是不可能出现幻读的

配置隔离级别

修改

下面我们来介绍下如何在MySQL中修改隔离级别,SQL语法如下所示

1
set {global|session} transaction isolation level {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};

其中,对于上述SQL语法中的 {global|session} 部分。用户可以选择使用global关键字,也可以使用session关键字,还可以二者均不使用。下面简单介绍这三种不同用法的作用范围

1. 使用global关键字

其只对后续新建立的Session会话有效。对于当前已经存在的会话Session均无效

2. 使用session关键字

首先其作用范围仅限于当前会话中。其次即使是在当前会话中,也只对所有后续的事务才有效。换句话说,如果说在一个事务中执行该命令,则其对当前事务并无效

3. 二者(global、session)均不使用

其只对当前会话中的下一个即将开启的新事务生效。一旦这个新事务结束,则当前会话的隔离级别就会被恢复到之前的隔离级别。换言之,这个隔离级别的设置是一次性的,仅对后续一次新的事务过程生效。值得注意的是,不能在一个已经开启的事务当中执行该用法的SQL命令

查看

如果期望查看MySQL的隔离级别,则可通过系统变量transaction_isolation进行查看。具体地,SQL命令如下所示

1
2
3
4
-- 查看当前会话的隔离级别
show [session] variables like 'transaction_isolation';
-- 查看全局的隔离级别
show global variables like 'transaction_isolation';

参考文献

  1. MySQL是怎样运行的
  2. 数据密集型应用系统设计(DDIA) Martin Kleppmann著
0%