事务隔离级别(Isolation Level)分类如下:

  • READ UNCOMMITTED,读未提交
  • READ COMMITTED,读已提交
  • REPEATABLE READ,可重复读
  • SERIALIZABLE,序列化

一、并发事务交互的现象

并发事务之间的交互会产生如下现象,这些现象不会在每一个级别都发生:

  • dirty read,脏读。一个事务可以读取到另一个并发事务写入但是未提交的数据。
  • nonrepeatable read,不可重复读。事务重新读取之前读取的数据行,但是发现该数据行已经被另一个事务(在第一个事务重新读取之前事务提交)修改。
  • phantom read,幻读。事务重新执行满足一定搜索条件的查询,发现查询结果集因为另一个提交的事务而发生了更改(插入或删除了满足查询条件的行),第一次查询和第二次查询之间出现了“幻影”行。
  • serialization anomaly,序列化异常。成功提交一组事务的结果与将事务一个一个地一次运行一个事务的顺序不一致。

二、隔离级别

1、READ UNCOMMITTED

READ UNCOMMITTED,读未提交。这是最低的隔离级别,允许一个事务读取另一个未提交事务的数据,此时如果事务回滚,第二个事务将检索到无效的行。

读未提交隔离级别时,脏读、不可重复读和幻读都可能发生。

TIPS:PostgreSQL 不支持这个隔离级别,它不允许读取未提交的数据。

2、READ COMMITTED

READ COMMITTED,读已提交。在这个级别下,一个事务只能看到在它开始之前已经提交的数据,不能读取其他事务尚未提交的数据,从而避免了脏读。但是,如果同一个事务在不同时间点对同一数据进行多次读取,可能会因为其他事务的提交而看到不同的结果,这被称为不可重复读。此外,由于其他事务可能插入新的行,导致第一次查询的结果集与第二次查询不一致,这称为幻读。

TIPS:这是 PostgreSQL 的默认隔离级别。

3、REPEATABLE READ

REPEATABLE READ,可重复读。在这个隔离级别下,一个事务在整个事务期间看到的数据是一致的,即它能够重复读取同一数据而不受其他事务的影响。可以防止脏读和不可重复读。可能会发生幻读。

TIPS

  • PostgreSQL 实现这一级别的方法是在事务开始时锁定所有将要读取的数据,直到事务结束。这可以防止其他事务修改这些数据,从而避免了不可重复读和幻读。但这种方法可能导致更多的锁竞争,影响并发性能。
  • PostgreSQL 在此级别时,不可重复读和幻读都是不会发生的,SQL 标准的定义此时可能发生幻读。

4、SERIALIZABLE

SERIALIZABLE,串行化。这是最高的隔离级别,确保事务完全按照某种顺序执行,就像它们没有并发执行一样。在这个级别下,事务之间不会发生干扰,从而避免了所有类型的并发问题,如脏读、不可重复读和幻读。虽然此隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。

TIPS:PostgreSQL 使用快照隔离技术来实现这一级别,这通常比实际串行执行事务更高效。然而,当检测到违反序列化的情况时,PostgreSQL 可能会回滚事务,并且应用程序需要准备处理这种情况,重新尝试事务。

二、隔离级别的实现

SQL 规范定义了四种隔离级别,但是并没有给出实现,不同数据库的实现方式和使用方式也不相同。SQL 隔离级别的标准是依据于锁的实现方式来指定的。

1、传统隔离级别的实现

1.1 锁

  • 排他锁(Exclusive Locks,X 锁),事务对一条记录进行写操作时,需要先获取该记录的排他锁
  • 共享锁(Share Locks,S 锁),事务对一条记录进行读操作时,需要先获取该记录的共享锁

加了共享锁的记录,其他事务也可以获取该记录的共享锁,但是无法获取该记录的排他锁;加了排他锁的记录,其他事务是即无法获取该记录的共享锁,也无法获取排他锁。(S 锁和 S 锁是兼容的,S 锁和 X 锁是不兼容的,X 锁和 X 锁是不兼容的)。

  • 当一个事务需要修改数据时,它会尝试获取排他锁来阻止其他事务同时修改该数据。其他事务需要在锁释放后才能访问该数据。
  • 当一个事务只需要读取数据而不修改时,它会尝试获取共享锁,在不互斥的情况下允许多个事务同时读取该数据

锁的粒度可以是表级锁、行级锁或更细粒度的锁。

在对一条记录进行读取时,需要先获取该记录的共享锁,但是有时候需要在读取记录时阻止其他事务访问该记录,这是就要获取该记录的排他锁,可以通过 select ... for update 来实现。

1.2 基于锁实现隔离级别

读未提交

读操作不加锁,读读、读写、写读并行;写操作加 X 锁且直到事务提交后才释放。

读已提交

读操作加 S 锁,写操作加 X 锁且直到事务提交后才释放。读操作不会阻碍其他事务读写,写操作会阻碍其他事务读写,因此可以防止脏读问题。

可重复读

读操作加 S 锁且直到事务提交后才释放,写操作加 X 锁且直到事务提交后才释放。读操作不会阻塞其他事务读但是会阻塞其他事务写,写操作会阻塞其他事务读写,因此可以防止脏读和不可重复读。

串行化

读操作和写操作都加 X 锁且直到事务提交后才释放,粒度为表级锁。

注意

  • 如果锁获取后直到事务提交之后才释放,这种锁称为长锁;如果锁在操作完成后就被释放,这种锁被称为短锁。
  • 事务获取锁之后直到事务提交后才能释放,这种把获取锁和释放锁分为两个不同阶段的协议称为两阶段锁协议(Two Phase Locking,2PL)。该协议规定,在加锁阶段一个事务可以获得锁但是不能释放锁;在解锁阶段只能释放锁但不能获得新的锁。两阶段锁协议能够保证事务串行化执行,解决事务并发问题,但也会导致死锁。

2、MVCC

相关链接

OB tags

#数据库 #事务 #未完待续