# 13.2.事务隔离

13.2.1. 读取提交的隔离级别

13.2.2. 可重复读取隔离级别

13.2.3. 可串行化隔离级别

SQL标准定义了四个事务隔离级别。最严格的是Serializable,该标准在一段中定义了Serializable,该段说,一组可序列化事务的任何并发执行都保证产生与以某种顺序一次运行一个事务相同的效果。其他三个级别是根据现象定义的,这些现象是由并发事务之间的交互产生的,而这些交互不能发生在每个级别上。该标准指出,由于Serializable的定义,这些现象在该级别都不可能出现。(这并不奇怪——如果事务的效果必须与一次运行一个事务的效果一致,那么您怎么能看到由交互引起的任何现象?)

各级禁止的现象有:

脏书

事务读取由并发未提交事务写入的数据。

不可重复读取

事务重新读取以前读取的数据,并发现数据已被另一个事务(自初始读取以来提交的事务)修改。

幻读

事务重新执行一个查询,返回满足搜索条件的一组行,并发现满足该条件的一组行由于另一个最近提交的事务而发生了更改。

序列化异常

成功提交一组事务的结果与每次运行一个事务的所有可能顺序不一致。

中描述了SQL标准和PostgreSQL实现的事务隔离级别表13.1.

表13.1.事务隔离级别

隔离级别 脏书 不可重复读 幻影阅读 序列化异常
读未提交 允许,但在PG中不允许 可能的 可能的 可能的
阅读承诺 不可能 可能的 可能的 可能的
可重复读取 不可能 不可能 允许,但在PG中不允许 可能的
可序列化 不可能 不可能 不可能 不可能

在PostgreSQL中,您可以请求四个标准事务隔离级别中的任何一个,但在内部只实现了三个不同的隔离级别,即PostgreSQL的Read Uncommitted模式的行为类似于Read Committed。这是因为这是将标准隔离级别映射到PostgreSQL的多版本并发控制体系结构的唯一合理方法。

该表还显示,PostgreSQL的可重复读取实现不允许幻象读取。SQL标准允许更严格的行为:四个隔离级别只定义哪些现象不能发生,而不是哪些现象必须发生以下小节详细介绍了可用隔离级别的行为。

要设置事务的事务隔离级别,请使用以下命令设置事务.

# 重要的

一些PostgreSQL数据类型和函数有关于事务行为的特殊规则。特别是,对序列(以及使用电视连续剧)对所有其他事务都立即可见,并且在进行更改的事务中止时不会回滚。看见第9.17节第8.1.4节.

# 13.2.1.读取提交的隔离级别

阅读承诺是PostgreSQL中的默认隔离级别。当事务使用此隔离级别时选择查询(没有更新/分享子句)只查看查询开始前提交的数据;它从不看到未提交的数据或在并发事务执行查询期间提交的更改。实际上选择查询会在查询开始运行时看到数据库的快照。然而选择确实可以看到在其自身事务中执行的先前更新的效果,即使这些更新尚未提交。还要注意两个连续的选择如果其他事务在第一个事务之后提交更改,则命令可以看到不同的数据,即使它们位于单个事务中选择开始和第二次之前选择开始。

使现代化, 删去, 选择更新选择共享命令的行为与选择在搜索目标行方面:它们只会找到在命令开始时提交的目标行。然而,在找到这样的目标行时,它可能已经被另一个并发事务更新(或删除或锁定)。在这种情况下,潜在的更新程序将等待第一个更新事务提交或回滚(如果它仍在进行中)。如果第一个更新程序回滚,则其效果将被否定,第二个更新程序可以继续更新最初找到的行。如果第一个更新程序提交,如果第一个更新程序删除了该行,第二个更新程序将忽略该行,否则它将尝试将其操作应用于该行的更新版本。命令的搜索条件(哪里子句),以查看行的更新版本是否仍与搜索条件匹配。如果是这样,第二个更新程序将使用该行的更新版本继续其操作。就选择更新选择共享,这意味着锁定并返回给客户端的是行的更新版本。

插入带着关于冲突,请更新子句的行为类似。在读提交模式下,建议插入的每一行都将插入或更新。除非存在不相关的错误,否则这两种结果中的一种是可以保证的。如果冲突源于另一笔交易,而该笔交易的影响尚不为客户所知插入这个使现代化子句将影响该行,即使可能该行的版本通常对命令可见。

插入带着在冲突中无所作为由于另一个事务的结果对用户不可见,子句可能会使某一行的插入不继续插入快照同样,这只是在读提交模式下的情况。

由于上述规则,更新命令可能会看到不一致的快照:它可以在试图更新的同一行上看到并发更新命令的效果,但在数据库中的其他行上看不到这些命令的效果。这种行为使得读取提交模式不适用于涉及复杂搜索条件的命令;然而,对于更简单的情况来说,这是恰到好处的。例如,考虑用类似的交易更新银行余额:

BEGIN;
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534;
COMMIT;

如果两个这样的交易同时试图改变账户12345的余额,我们显然希望第二个交易以账户行的更新版本开始。因为每个命令只影响预定的行,所以让它查看该行的更新版本不会产生任何麻烦的不一致性。

在读提交模式下,更复杂的使用可能会产生不希望的结果。例如,考虑一个删去对数据进行操作的命令,该数据由另一个命令(例如,假定)从其限制条件中添加和删除网站是一张两排的桌子网站击打相等910:

BEGIN;
UPDATE website SET hits = hits + 1;
-- run from another session:  DELETE FROM website WHERE hits = 10;
COMMIT;

这个删去即使存在网站点击率=10比赛前后划船使现代化。发生这种情况是因为更新前的行值9被跳过,当使现代化完成并删去获取一个锁,新行值不再为10但是11,不再符合标准。

由于读取提交模式以一个新的快照启动每个命令,该快照包含到该时刻提交的所有事务,因此同一事务中的后续命令在任何情况下都会看到提交的并发事务的效果。上面讨论的问题是仅有一个的命令可以看到数据库的绝对一致的视图。

读提交模式提供的部分事务隔离对于许多应用程序来说是足够的,并且这种模式快速且易于使用;然而,这并不能满足所有情况。执行复杂查询和更新的应用程序可能需要比读取提交模式提供的数据库视图更严格的一致性。

# 13.2.2.可重复读取隔离级别

这个可重复读取隔离级别只查看事务开始前提交的数据;它从不看到未提交的数据或在并发事务执行期间提交的更改。(但是,查询确实看到了在其自身事务中执行的以前更新的效果,即使它们尚未提交。)这是一种比SQL标准要求的隔离级别更有力的保证,可以防止中描述的所有现象表13.1除了序列化异常。如上所述,本标准特别允许这样做,该标准仅描述了最低限度每个隔离级别必须提供保护。

此级别与Read Committed的不同之处在于,可重复读取事务中的查询会在数据库中第一条非事务控制语句开始时看到快照交易,而不是在事务中的当前语句开始时。因此,连续的选择内部命令仅有一个的事务可以看到相同的数据,也就是说,它们看不到在自己的事务启动后提交的其他事务所做的更改。

由于序列化失败,使用此级别的应用程序必须准备好重试事务。

使现代化, 删去, 选择更新选择共享命令的行为与选择在搜索目标行方面:它们只会找到在事务开始时提交的目标行。然而,在找到这样的目标行时,它可能已经被另一个并发事务更新(或删除或锁定)。在这种情况下,可重复读取事务将等待第一个更新事务提交或回滚(如果仍在进行中)。如果第一个更新程序回滚,则其效果将被否定,可重复读取事务可以继续更新最初找到的行。但是,如果第一个更新程序提交(并实际更新或删除了该行,而不仅仅是锁定了它),那么可重复读取事务将与消息一起回滚

ERROR:  could not serialize access due to concurrent update

因为在可重复读取事务开始后,可重复读取事务无法修改或锁定由其他事务更改的行。

当应用程序收到此错误消息时,它应该中止当前事务,并从头开始重试整个事务。第二次到,事务将看到以前提交的更改作为其数据库初始视图的一部分,因此在使用行的新版本作为新事务更新的起点时没有逻辑冲突。

请注意,可能只需要重试更新事务;只读事务永远不会有序列化冲突。

可重复读取模式提供了严格的保证,确保每个事务都能看到数据库的完全稳定视图。然而,该视图不一定总是与同一级别并发事务的某些串行(一次一个)执行一致。例如,即使是这个级别的只读事务,也可能会看到一条控制记录被更新,以显示一个批已经完成,但查看一条详细记录,该记录在逻辑上是批次的一部分,因为它读取了控制记录的早期版本。如果不小心使用显式锁来阻止冲突的事务,通过在此隔离级别上运行的事务强制执行业务规则的尝试就不可能正常工作。

可重复读取隔离级别是使用学术数据库文献和其他一些数据库产品中已知的技术实现的快照隔离。与使用传统锁定技术降低并发性的系统相比,可能会发现行为和性能上的差异。其他一些系统甚至可以提供可重复读取和快照隔离,作为具有不同行为的不同隔离级别。直到SQL标准制定之后,数据库研究人员才正式确定了区分这两种技术的允许现象,这超出了本手册的范围。如需全面治疗,请参阅[berenson95].

# 笔记

在PostgreSQL版本9.1之前,对可序列化事务隔离级别的请求提供了与本文描述的完全相同的行为。为了保留遗留的可序列化行为,现在应该请求可重复读取。

# 13.2.3.可串行化隔离级别

这个可序列化隔离级别提供了最严格的事务隔离。该级别模拟所有提交事务的串行事务执行;好像事务是一个接一个地连续执行的,而不是同时执行的。但是,与可重复读取级别一样,使用此级别的应用程序必须准备好重试由于序列化失败而导致的事务。事实上,该隔离级别的工作原理与可重复读取完全相同,只是它监视可能导致并发可串行化事务集的执行行为与这些事务的所有可能串行(一次一个)执行不一致的条件。除了可重复读取中存在的阻塞之外,这种监控不会引入任何阻塞,但监控和检测可能导致错误的条件会带来一些开销序列化异常会引发序列化失败.

作为一个例子,考虑一个表我的标签,最初包含:

 class | value