MySQL之InnoDB存储引擎:浅谈隔离级别
共 2527字,需浏览 6分钟
·
2021-09-22 12:01
谈事务的隔离性Isolation,就不得不谈谈所谓的隔离级别
读写冲突
Dirty Write脏写
所谓脏写是指,当一个事务 修改了 另一个未提交事务修改过的数据。如下图两个会话Session各自开启了两个事务。在Session 2的事务其将id为1的name字段更新为Aaron,随后在Session 1的事务又将修改为Bob了。此时如果Session 2事务中发生了回滚。这个时候对于Session 1而言,会发现即使该事务提交了但其所做的修改却没有生效。即出现了所谓的脏写
Dirty Read脏读
如果一个事务 读到了 另一个未提交事务修改过的数据,即会出现脏读。如下图两个会话Session各自开启了两个事务。在Session 2的事务其将id为1的name字段更新为Aaron,然后Session 1的事务中会读到id为1的记录其name字段为Aaron。如果此时Session 2的事务发生了回滚,显然其所做的修改也将被还原。这个时候对于Session 1而言,其相当于读到了一个不存在的数据
Non Repeatable Read不可重复读
假设一个事务 只能读到 其他已经提交事务修改过的数据,则其他事务每次对该数据的修改并提交后,如果该事务都可以读到最新的值,则即是所谓的不可重复读。如下图所示,在Session 2中通过隐式事务提交对数据的修改。而在Session 1的事务中则是每次可以读到其他事务修改后提交的最新的值。这即是所谓的不可重复读
Phantom Read幻读
所谓幻读,指的是在一个事务中,根据查询条件获取到若干条记录,记为R1、R2、...、Rn。随后其他事务又插入了新记录Rm并提交,那么当原来的事务继续使用之前的查询条件继续查询,发现不仅获取到了R1、R2、...、Rn记录,还查询到了其他事务插入、提交的新记录Rm
如下图所示,我们在Session 1中的事务中,通过条件查询获取到一条记录结果。随后Session 2通过隐式事务插入一条id为996的新记录。然后Session 1的事务又执行了相同查询条件的查询,可以看到其结果与第一次查询结果相比,多了一条name字段为Aaron的记录
四种隔离级别
这里对上文所述各类型的读写冲突按问题的严重程度进行排序:脏写 > 脏读 > 不可重复读 > 幻读。可以看到,脏写是最严重的,幻读是最不严重的。为此,SQL标准中提出了下面四种隔离级别。可以看到不同的隔离级别下,其可以避免不同的读写冲突问题。由于脏写的问题过于严重,故在任何隔离级别中均不允许发生
对于上述读写冲突与隔离级别的对应关系,可通过如下口诀进行记忆——“鞋脏不换(写脏不幻),UCRS”
写 → 脏写,U → RU未提交读 脏 → 脏读,C → RC已提交读 不 → 不可重复读,R → RR可重复读 幻 → 幻读,S → Serializable串行化
对于任何一隔离级别而言,其亦可解决口诀中之前所出现的问题。例如对于R(即RR)而言,其不仅可以解决不可重复读,还可以解决脏写、脏读的问题
上面介绍的四种隔离级别只是SQL标准定义的,而具体到各数据库厂商来说的话,其所支持的程度是不一样的。就我们这里MySQL数据库的InnoDB引擎而言,其对上述四种隔离级别均是支持的。值得一提的是,对于MySQL的InnoDB引擎而言,在REPEATABLE READ可重复读隔离级别下是不可能出现幻读的
配置隔离级别
修改
下面我们来介绍下如何在MySQL中修改隔离级别,SQL语法如下所示
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命令如下所示
-- 查看当前会话的隔离级别
show [session] variables like 'transaction_isolation';
-- 查看全局的隔离级别
show global variables like 'transaction_isolation';
参考文献
MySQL是怎样运行的 数据密集型应用系统设计(DDIA) Martin Kleppmann著