二、PostgreSQL的事务隔离
PostgreSQL的事务支持是通过通常所说的多版本并发控制或者MVCC方法实现的,也就是说,每当事务进行处理时,它看到的是自己的数据库快照,而非底层数据的实际状态。 这使得任何给定的事务无法看到其它已经启动但是尚未提交的事务对数据所作的部分修改。这项原则就是所谓的事务隔离。
SQL标准规定了三种属性以用来确定一个事务处于四级隔离级别的哪一级,这些属性如下所示:
l 脏读:一个事务读取了另一个未提交的并行事务写的数据
l 不可重复读:当一个事务重新读取前面读取过的数据时,发现该数据已经被另一个已提交的事务修改过
l 幻读:一个事务重新执行一个查询时,返回一套符合查询条件的行,发现这些行因为其他最近提交的事务而发生了改变
这三种情况确定了一个事务的隔离级别,所有四种水平如表1所示。
表1 SQL标准事务隔离级别
PostgreSQL允许您请求四种可能的事务隔离级别中的任意一种。但是在内部,实际上只有两种可用的隔离级别,分别对应读已提交和可串行化。如果你选择了读未提交的级别,实际上你用的是读已提交,在你选择可重复的读级别的时候,实际上你用的是可串行化,所以实际的隔离级别可能比你选择的更严格。虽然这看起来是有悖于我们的直觉,但是SQL标准的确允许这样做,因为四种隔离级别只定义了哪种现象不能发生,但是没有定义那种现象一定发生,所以除了不允许的事务特性之外,所有的特性都是允许的。举例来说,如果您请求可重复读模式,那么该标准只是要求您不准脏读以及不可重读,但是却没有要求允许幻读。因此,可串行化事务模式满足可重复读模式的要求,即使它跟定义没有完全吻合。因此,您应当确切的知道,当您请求读未提交模式的时候,您实际得到的却是读已提交模式;而当您请求可重复读的时候,实际得到的是可串行化模式。您还应当意识到,默认情况下,如果您没有请求一个特定的隔离级别,那么您得到的将是读已提交隔离级别。
下面我们了解一下读已提交和可串行化之间的主要区别。在读已提交模式下SELECT 查询只能看到该查询开始之前提交的数据而永远无法看到未提交的数据或者是在查询执行时其他并行的事务提交的改变;不过 SELECT 的确看得见同一次事务中前面更新的结果,即使它们还没提交也看得到。实际上,一个 SELECT 查询看到一个在该查询开始运行的瞬间该数据库的一个快照。请注意两个相邻的 SELECT 命令可能看到不同的数据,哪怕它们是在同一个事务里,因为其它事务会在第一个SELECT执行的时候提交。当一个事务处于可串行化级别的时候,一个 SELECT 查询只能看到在该事务开始之前提交的数据而永远看不到未提交的数据或事务执行中其他并行事务提交的修改;不过,SELECT 的确看得到同一次事务中前面的更新的效果,即使事务还没有提交也一样。这个行为和读已提交级别是不太一样,它的 SELECT 看到的是该事务开始时的快照,而不是该事务内部当前查询开始时的快照。这样,一个事务内部后面的SELECT命令总是看到同样的数据。这意味着,读已提交模式下一个事务内部后面的SELECT命令可以看到不同的数据,但是在可串行化模式下却总是看到同样的数据。
对于以上区别,请读者一定弄清楚。虽然刚看上去有些复杂,但是只要抓住两个要点,理解起来还是很容易的:首先,PostgreSQL运行事务的并发运行,也就是说一个事务执行的时候,并不妨碍另一事务对相同数据操作。其次,一定注意快照的概念,事务提交前操作的是数据快照而非数据库本身,同时注意不同隔离级别使用的是何时的快照——事务开始之前的快照,还是事务内部操作开始之前的快照?我想只要抓住了以上要点,我们就能很好的把握各种隔离级别之间的区别了。
上面介绍了事务的基本概念,接下来我们开始演示如何在PostgreSQL客户端中使用事务。