怎么理解SQL的四个事务隔离级别?

最近在读PostgreSQL的官方文档,PostgreSQL支持SQL标准定义的读未提交、读已提交、可重复读、可串行化这4个事务隔离级别。而内部其实只有读已提交,可重复读和可串行化这3种独立的隔离级别,所以文档里只说明了这3种隔离级别。问题是文档里的说明比较生硬,看完似懂非懂。有没有哪位大神帮我通俗的解释一下这4个事务隔离级别啊?
图片描述

阅读 5.9k
3 个回答

我认为要理解事务隔离级别,就必须先理解"脏读","不可重复读"和"幻读"这三个现象。因此我会以下表为例直观说明这三种读的具体现象。

图片描述

  • 脏读

图片描述

这种现象出现在事物间的隔离级别最差的场景下,写事务对一个元组的更新尚未提交时就被另一个事务读到了。如果在一个业务应用中,写事务后面没提交而是回滚了,那么可以预见这个读事务读到的这个未提交的更新在某些业务场景下可能会带来一些困扰。

  • 不可重复读

图片描述

这种现象比上面的脏读好一点,只有当写事务提交后,这个更新才会被读事务读取到。但是考虑到如上图所示,在同一个事务中的不同时间点意图读取同一个元组却读到了不同的数据,在某些业务场景下可能也会带来一些困扰。

划重点: "脏读"和"不可重复读"这两个现象针对的是对于表中的同一个逻辑上的元组而言.引发"脏读"和"不可重复读"这两个现象的写事务的操作通常是UPDATE
  • 幻读

幻读这一现象针对的已不是表中的单一元组而言,而是指读事务在对表中的某个范围多个元组而言的一种现象,引发幻读的写事务对应的操作通常是INSERTDELETE。如下图所示:

图片描述

在同一个读事务中,对于同一个过滤条件查询出了不同的结果集,这在某些业务场景下也有可能带来一定的困扰。

上面这些就是关于"脏读","不可重复读"以及"幻读"这三个现象的介绍。除此之外,这边还需要再强调两个注意点:

  1. "脏读","不可重复读"以及"幻读"这三个现象不是错误,更不是BUG。它们仅仅是事务并发场景下可能出现的现象。
  2. "脏读","不可重复读"以及"幻读"针对的是同一个事务中的读操作而言。为什么要强调这一点,因为有些对数据库理解不深的同学不能很好的理解清楚 会话,事务语句这三个概念。于是对于以下的例子就会误认为是"不可重复读"的现象:

    图片描述

    但事实上,它并非"不可重复读"的例子。因为在会话2中没有开启显式事务,因此两个SELECT语句分属于不同事务。"脏读","不可重复读"以及"幻读"的概念不适用于不同的事务之间的读操作。


理解清楚了以上的这三个"读"的概念后,就可以很容易的理解事务隔离级别了。因为事务隔离级别的设置本质上就意味着让你控制并发事务之间的写事务带来的数据更新的可见性——即,你允许业务中的并发事务之间出现怎样的都现象. 由于"脏读","不可重复读"以及"幻读"的概念是一种层层递进的概念,因此事务隔离级别从"Read Uncommited"到"Serializable"也是一个比一个严格。

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 允许 可能 可能
READ COMMITTED 不允许 可能 可能
REPEATABLE READ 不允许 不允许 可能
SERIALIZABLE 不允许 不允许 不允许

需要注意的是,SQL标准中对于这些隔离级别定义中约束的"不允许"现象是强制要求的。数据库厂商在宣称支持某个隔离级别时,必须将上表中对应隔离级别的"不允许"进行实现。但是对于"可能"项则不代表你必须实现成具备某种都现象。在PostgreSQL中,由于其MVCC的实现,REPEATABLE READ对于读事务的行为实现也和SERIALIZABLE一样是不会出现幻读的,而REPEATABLE READSERIALIZABLE的区别,主要体现在下文所述的对更新操作的约束力度上。

此外,需要特别说明一下SERIALIZABLE这个隔离级别对于并发写事务有所约束:

在其他隔离级别中,如果并发的两个事务同时意图对同一个元组进行更新时, 后更新的事务会等待直到先更新的事务提交后在继续执行其更新操作。 但是在SERIALIZABLE的情况下,由于此时事务隔离级别最强,会对有可能对读一致性带来影响的写操作必须按照事务的串行执行。在PG的实现中,这表现为尝试对于同一元组进行更新的并发事务会在等待完先更新的事务提交后自己报个错:

图片描述

SERIALIZABLE隔离级别下,对于上图中的两个更新事务若都希望成功,需要保证右边会话的更新操作所属事务的START TRANSACTION必须发生在左边会话的更新事务COMMIT之后。

以上,就是关于事务隔离级别的那点儿事,希望我这个回答能够帮题主解惑。

读未提交:事务T在修改数据R之前必须先对其加排它锁,事务结束后释放
读已提交:一级封锁协议+事务T在读取数据R之前必须先对其加共享锁,读取后(事务结束前)释放
可重复读:一级封锁协议+事务T在读取数据R之前必须先对其加共享锁,事务结束后释放

我正在写相关的一系列文章 有空的话你可以去看一下是否有帮助

推荐问题
宣传栏