postgresql并发控制:事务隔离级别

"postgresql事务隔离级别"

 

前言

数据库事务有四大特性:原子性(A),一致性(C),隔离性(I),持久性(D)。其中的隔离性,即是本文要讲的重点。

所谓隔离性,指的是不同事务在并发运行时,不会互相干扰。隔离性允许事务行为隔离于其他事务并发运行。

 

四种隔离级别

数据库事务四种异常现象:

  • 脏读:一个事务读取了另一个并行未提交事务写入的数据。
  • 不可重复读:一个事务重新读取之前读取过的数据,发现该数据已经被另一个事务(在初始读之后提交)修改。
  • 幻读:一个事务重新执行一个返回符合一个搜索条件的行集合的查询, 发现满足条件的行集合因为另一个最近提交的事务而发生了改变。
  • 序列化异常:成功提交一组事务的结果与一次运行这些事务的所有可能顺序不一致。

 

通过避免以上四种异常现象的多少,数据库事务将隔离级别划分为了四种,以安全级别从低到高排序为:

  • 读未提交(read uncommitted)
  • 读提交(read committed)
  • 可重复读(repeatable read)
  • 可串行化(serializable)

PG默认隔离级别为读提交(read committed)。

 

SQL标准定义的隔离级别:

隔离级别藏独不可重复读幻读序列化异常
读未提交(read uncommitted)可能;在PG中不可能可能可能可能
读提交(read committed)不可能可能可能可能
可重复读(repeatable read)不可能不可能可能可能
可串行化(serializable)不可能不可能不可能不可能

事实上,在postgresql中,读未提交(read uncommitted)和读提交(read committed)的实现是一致的,所以实质上PG只实现了三种隔离级别。

读提交(read committed)

提交读只可读取其它已提交事务的结果。要注意的是,在这个级别,同一事务多次select得到的结果可能不一样(因为每次读的都是已提交事务的结果,如果在两次select之间有事务已经提交,那得到的结果就会不一样)。

 

可重复读(repeatable read)

相对于提交读,重复读要求在同一事务中,前后两次带条件查询所得到的结果集相同。实际中,PostgreSQL的实现更严格,不紧要求可重复读,还不允许出现幻读。它是通过只读取在当前事务开启之前已经提交的数据实现的。

经常见到的报错ERROR: could not serialize access due to concurrent update,就是在这个级别产生的,是因为UPDATE、DELETE、SELECT FOR UPDATE和SELECT FOR SHARE命令在搜索目标行时的行为和SELECT一样: 它们将只找到在事务开始时已经被提交的行。 不过,在被找到时,这样的目标行可能已经被其它并发事务更新(或删除或锁住),这个时候就会报上面的错。

 

可串行化(serializable)

最严格的级别,要求所有已提交的事务是串行执行,而不是并行执行。跟可重复读一样,也可能报ERROR: could not serialize access due to concurrent update

 

相关SQL命令

  1. 查看当前会话的隔离级别

  2. 临时修改会话的事务隔离级别

  3. 永久修改事务隔离级别,需要修改postgresql.conf文件

  4. 修改默认隔离级别

     

各个隔离级别的实现机制

待补充

 

参考

  1. https://www.postgresql.org/docs/9.5/transaction-iso.html
  2. SQL优化(六) MVCC PostgreSQL实现事务和多版本并发控制的精华