26.事务之隔离级别

86 阅读4分钟

SQL标准的4个隔离级别

提到事务,你肯定会想到ACID:Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性。

原子性:语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;实现主要基于undo log

持久性:保证事务提交后不会因为宕机等原因导致数据丢失;实现主要基于redo log。

隔离性:保证事务执行尽可能不受其他事务影响;InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制(包含next-key lock)、MVCC(包括数据的隐藏列、基于undo log的版本链、ReadView)。

一致性:事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障。

这个四个属性中,最重要的是一致性,也就是说其他的三个属性都是为了保证一致性而存在。

总之,ACID只是个概念,事务最终目的是要保障数据的可靠性,一致性。

今天我们就来说说隔离性。

我们上边介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题也有轻重缓急之分,我们给这些问题按照严重性来排一下序:

脏写 > 脏读 > 不可重复读 > 幻读

我们上边所说的舍弃一部分隔离性来换取一部分性能在这里就体现在:设立一些隔离级别,隔离级别越低,越严重的问题就越可能发生。

有一帮人制定了一个所谓的SQL标准,在标准中设立了4个隔离级别:

  • READ UNCOMMITTED:未提交读。
  • READ COMMITTED:已提交读。
  • REPEATABLE READ:可重复读。
  • SERIALIZABLE:可串行化。

SQL标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:

隔离级别脏读不可重复读幻读
READ UNCOMMITTEDPossiblePossiblePossible
READ COMMITTEDNot PossiblePossiblePossible
REPEATABLE READNot PossibleNot PossiblePossible
SERIALIZABLENot PossibleNot PossibleNot Possible
  • READ UNCOMMITTED隔离级别下,可能发生脏读、不可重复读和幻读问题。
  • READ COMMITTED隔离级别下,可能发生不可重复读和幻读问题,但是不可以发生脏读问题。
  • REPEATABLE READ隔离级别下,可能发生幻读问题,但是不可以发生脏读和不可重复读的问题。
  • SERIALIZABLE隔离级别下,各种问题都不可以发生。

脏写是怎么回事儿?怎么里边都没写呢?这是因为脏写这个问题太严重了,不论是哪种隔离级别,都不允许脏写的情况发生。

脏写在mysql中已经使用了update 加锁的方式解决了。

MySQL的4种事务隔离级别

在MySQL中也是支持那4种隔离级别的,基本的语义都是差不多的

但是要注意的一点是,MySQL默认设置的事务隔离级别,都是RR级别的,而且MySQL的RR级别是可以避免幻读发生的。

只是在一定场景下避免了幻读。

这点是MySQL的RR级别的语义跟SQL标准的RR级别不同的,毕竟SQL标准里规定RR级别是可以发生幻读的,但是MySQL的RR级别避免了!

也就是说,MySQL里执行的事务,默认RR级别下不会发生脏写、脏读、不可重复读和幻读的问题,事务的执行都是并行的,大家互相不会影响,我不会读到你没提交事务修改的值,即使你修改了值还提交了,我也不会读到的,即使你插入了一行值还提交了,我也不会读到的,总之,事务之间互相都完全不影响!

我们可以通过下边的语句修改事务的隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

其中的level可选值有4个:

level: {
     REPEATABLE READ
   | READ COMMITTED
   | READ UNCOMMITTED
   | SERIALIZABLE
}

使用GLOBAL关键字(在全局范围影响)。

  • 只对执行完该语句之后产生的会话起作用。
  • 当前已经存在的会话无效。

使用SESSION关键字(在会话范围影响)。

  • 对当前会话的所有后续的事务有效
  • 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务。
  • 如果在事务之间执行,则对后续的事务有效。

上述两个关键字都不用:只对执行语句后的下一个事务产生影响。

想要查看当前会话默认的隔离级别可以通过查看系统变量transaction_isolation的值来确定:

mysql> SHOW VARIABLES LIKE 'transaction_isolation';

或者使用更简便的写法:

mysql> SELECT @@transaction_isolation;