PG中的事务
PG中的事务
PostgreSQL为开发者提供了一组丰富的工具来管理对数据的并发访问。在内部,数据一致性通过使用一种多版本模型(多版本并发控制,MVCC)来维护。这就意味着每个 SQL 语句看到的都只是一小段时间之前的数据快照(一个数据库版本),而不管底层数据的当前状态。这样可以保护语句不会看到可能由其他在相同数据行上执行更新的并发事务造成的不一致数据,为每一个数据库会话提供事务隔离。MVCC避免了传统的数据库系统的锁定方法,将锁争夺最小化来允许多用户环境中的合理性能。
使用MVCC并发控制模型而不是锁定的主要优点是在MVCC中,对查询(读)数据的锁请求与写数据的锁请求不冲突,所以读不会阻塞写,而写也从不阻塞读。甚至在通过使用革新的可序列化快照隔离(SSI)级别提供最严格的事务隔离级别时,PostgreSQL也维持这个保证。
在PostgreSQL里也有表和行级别的锁功能,用于那些通常不需要完整事务隔离并且想要显式管理特定冲突点的应用。不过,恰当地使用MVCC通常会提供比锁更好的性能。另外,由应用定义的咨询锁提供了一个获得不依赖于单一事务的锁的机制。
事务隔离
SQL标准定义了四种隔离级别。最严格的是可序列化,在标准中用了一整段来定义它,其中说到一组可序列化事务的任意并发执行被保证效果和以某种顺序一个一个执行这些事务一样。其他三种级别使用并发事务之间交互产生的现象来定义,每一个级别中都要求必须不出现一种现象。注意由于可序列化的定义,在该级别上这些现象都不可能发生(这并不令人惊讶--如果事务的效果与每个时刻只运行一个的相同,你怎么可能看见由于交互产生的现象?)。
在各个级别上被禁止出现的现象是:
脏读
一个事务读取了另一个并行未提交事务写入的数据。
不可重复读
一个事务重新读取之前读取过的数据,发现该数据已经被另一个事务(在初始读之后提交)修改。
幻读
一个事务重新执行一个返回符合一个搜索条件的行集合的查询, 发现满足条件的行集合因为另一个最近提交的事务而发生了改变。
序列化异常
成功提交一组事务的结果与这些事务所有可能的串行执行结果都不一致。
SQL 标准和 PostgreSQL 实现的事务隔离级别在 表 13.1中描述。
表 13.1. 事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 序列化异常 |
---|---|---|---|---|
读未提交 | 允许,但不在 PG 中 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 允许,但不在 PG 中 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 | 不可能 |
在PostgreSQL中,你可以请求四种标准事务隔离级别中的任意一种,但是内部只实现了三种不同的隔离级别,即 PostgreSQL 的读未提交模式的行为和读已提交相同。这是因为把标准隔离级别映射到 PostgreSQL 的多版本并发控制架构的唯一合理的方法。
该表格也显示 PostgreSQL 的可重复读实现不允许幻读。而 SQL 标准允许更严格的行为:四种隔离级别只定义了哪种现像不能发生,但是没有定义哪种现像必须\发生。可用的隔离级别的行为在下面的小节中详细描述。
要设置一个事务的事务隔离级别,使用SET TRANSACTION命令。
重要
某些PostgreSQL数据类型和函数关于事务的行为有特殊的规则。特别是,对一个序列的修改(以及用serial
声明的一列的计数器)是立刻对所有其他事务可见的,并且在作出该修改的事务中断时也不会被回滚。见第 9.17 节和第 8.1.4 节。
读已提交隔离级别
读已提交是PostgreSQL中的默认隔离级别。 当一个事务运行使用这个隔离级别时, 一个查询(没有FOR UPDATE/SHARE
子句)只能看到查询开始之前已经被提交的数据, 而无法看到未提交的数据或在查询执行期间其它事务提交的数据。实际上,SELECT
查询看到的是一个在查询开始运行的瞬间该数据库的一个快照。不过SELECT
可以看见在它自身事务中之前执行的更新的效果,即使它们还没有被提交。还要注意的是,即使在同一个事务里两个相邻的SELECT
命令可能看到不同的数据, 因为其它事务可能会在第一个SELECT
开始和第二个SELECT
开始之间提交。
UPDATE
、DELETE
、SELECT FOR UPDATE
和SELECT FOR SHARE
命令在搜索目标行时的行为和SELECT
一样: 它们将只找到在命令开始时已经被提交的行。 不过,在被找到时,这样的目标行可能已经被其它并发事务更新(或删除或锁住)。在这种情况下, 即将进行的更新将等待第一个更新事务提交或者回滚(如果它还在进行中)。 如果第一个更新事务回滚,那么它的作用将被忽略并且第二个事务可以继续更新最初发现的行。 如果第一个更新事务提交,若该行被第一个更新者删除,则第二个更新事务将忽略该行,否则第二个更新者将试图在该行的已被更新的版本上应用它的操作。该命令的搜索条件(WHERE
子句)将被重新计算来看该行被更新的版本是否仍然符合搜索条件。如果符合,则第二个更新者使用该行的已更新版本继续其操作。在SELECT FOR UPDATE
和SELECT FOR SHARE
的情况下,这意味着把该行的已更新版本锁住并返回给客户端。
带有ON CONFLICT DO UPDATE
子句的 INSERT
行为类似。在读已提交模式,要插入的 每一行将被插入或者更新。除非有不相干的错误出现,这两种结果之一是肯定 会出现的。如果在另一个事务中发生冲突,并且其效果对于INSERT
还不可见,则UPDATE
子句将会 影响那个行,即便那一行对于该命令来说没有\惯常的可见版本。
带有ON CONFLICT DO NOTHING
子句的 INSERT
有可能因为另一个效果对 INSERT
快照不可见的事务的结果无法让插入进行 下去。再一次,这只是读已提交模式中的情况。
因为上面的规则,正在更新的命令可能会看到一个不一致的快照: 它们可以看到并发更新命令在它尝试更新的相同行上的作用,但是却看不到那些命令对数据库里其它行的作用。 这样的行为令读已提交模式不适合用于涉及复杂搜索条件的命令。不过,它对于更简单的情况是正确的。 例如,考虑用这样的命令更新银行余额:
1 2 3 4 | BEGIN; UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345; UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534; COMMIT; |
如果两个这样的事务同时尝试修改帐号 12345 的余额,那我们很明显希望第二个事务从账户行的已更新版本上开始工作。 因为每个命令只影响一个已经决定了的行,让它看到行的已更新版本不会导致任何麻烦的不一致性。
在读已提交模式中,更复杂的使用可能产生不符合需要的结果。例如: 考虑一个在数据上操作的DELETE
命令,它操作的数据正被另一个命令从它的限制条件中移除或者加入,例如,假定website
是一个两行的表,两行的website.hits
等于9
和10
:
1 2 3 4 | BEGIN; UPDATE website SET hits = hits + 1; -- run from another session: DELETE FROM website WHERE hits = 10; COMMIT; |
即便在UPDATE
之前有一个website.hits = 10
的行,DELETE
将不会产生效果。这是因为更新之前的行值9
被跳过,并且当UPDATE
完成并且DELETE
获得一个锁,新行值不再是10
而是11
,这再也不匹配条件了。
因为在读已提交模式中,每个命令都是从一个新的快照开始的,而这个快照包含在该时刻已提交的事务, 因此同一事务中的后续命令将看到任何已提交的并行事务的效果。以上的焦点在于单个\命令是否看到数据库的绝对一致的视图。
读已提交模式提供的部分事务隔离对于许多应用而言是足够的,并且这个模式速度快并且使用简单。 不过,它不是对于所有情况都够用。做复杂查询和更新的应用可能需要比读已提交模式提供的更严格一致的数据库视图。
可重复读隔离级别
可重复读隔离级别只看到在事务开始之前被提交的数据;它从来看不到未提交的数据或者并行事务在本事务执行期间提交的修改(不过,查询能够看见在它的事务中之前执行的更新,即使它们还没有被提交)。这是比SQL标准对此隔离级别所要求的更强的保证,并且阻止表 13.1中描述的除了序列化异常之外的所有现象。如上面所提到的,这是标准特别允许的,标准只描述了每种隔离级别必须提供的最小\保护。
这个级别与读已提交不同之处在于,一个可重复读事务中的查询可以看见在事务\中第一个非事务控制语句开始时的一个快照,而不是事务中当前语句开始时的快照。因此,在一个单一\事务中的后续SELECT
命令看到的是相同的数据,即它们看不到其他事务在本事务启动后提交的修改。
使用这个级别的应用必须准备好由于序列化失败而重试事务。
UPDATE
、DELETE
、SELECT FOR UPDATE
和SELECT FOR SHARE
命令在搜索目标行时的行为和SELECT
一样: 它们将只找到在事务开始时已经被提交的行。 不过,在被找到时,这样的目标行可能已经被其它并发事务更新(或删除或锁住)。在这种情况下, 可重复读事务将等待第一个更新事务提交或者回滚(如果它还在进行中)。 如果第一个更新事务回滚,那么它的作用将被忽略并且可重复读事务可以继续更新最初发现的行。 但是如果第一个更新事务提交(并且实际更新或删除该行,而不是只锁住它),则可重复读事务将回滚并带有如下消息
1 | ERROR: could not serialize access due to concurrent update |
因为一个可重复读事务无法修改或者锁住被其他在可重复读事务开始之后的事务改变的行。
当一个应用接收到这个错误消息,它应该中断当前事务并且从开头重试整个事务。在第二次执行中,该事务将见到作为其初始数据库视图一部分的之前提交的改变,这样在使用行的新版本作为新事务更新的起点时就不会有逻辑冲突。
注意只有更新事务可能需要被重试;只读事务将永远不会有序列化冲突。
可重复读模式提供了一种严格的保证,在其中每一个事务看到数据库的一个完全稳定的视图。不过,这个视图并不需要总是和同一级别上并发事务的某些序列化(一次一个)执行保持一致。例如,即使这个级别上的一个只读事务可能看到一个控制记录被更新,这显示一个批处理已经被完成但是不能\看见作为该批处理的逻辑组成部分的一个细节记录,因为它读取空值记录的一个较早的版本。如果不小心地使用显式锁来阻塞冲突事务,尝试用运行在这个隔离级别的事务来强制业务规则不太可能正确地工作。
可重复读隔离级别是使用学术数据库文献和一些其他数据库产品中称为Snapshot Isolation的已知的技术来实现的。 与使用传统锁技术并降低并发性的系统相比,可以观察到行为和性能方面的差异。 一些其他系统甚至可以提供可重复读取和快照隔离作为具有不同行为的不同隔离级别。 直到SQL标准开发出来之后,数据库研究人员才正式确定区分这两种技术的允许现象,并且超出了本手册的范围。全面的阐述,请参阅[berenson95]。
注意
在PostgreSQL版本 9.1 之前,一个对于可序列化事务隔离级别的请求会提供和这里描述的完全一样的行为。为了保持可序列化行为,现在应该请求可重复读。
可序列化隔离级别
可序列化隔离级别提供了最严格的事务隔离。这个级别为所有已提交事务模拟序列事务执行;就好像事务被按照序列一个接着另一个被执行,而不是并行地被执行。但是,和可重复读级别相似,使用这个级别的应用必须准备好因为序列化失败而重试事务。事实上,这个隔离级别完全像可重复读一样地工作,除了它会监视一些条件,这些条件可能导致一个可序列化事务的并发集合的执行产生的行为与这些事务所有可能的序列化(一次一个)执行不一致。这种监控不会引入超出可重复读之外的阻塞,但是监控会产生一些负荷,并且对那些可能导致序列化异常的条件的检测将触发一次序列化失败。
例如,考虑一个表mytab
,它初始时包含:
1 2 3 4 5 6 | class | value -------+------- 1 | 10 1 | 20 2 | 100 2 | 200 |
假设可序列化事务 A 计算:
1 | SELECT SUM(value) FROM mytab WHERE class = 1; |
并且接着把结果(3)作为一个新行的value
插入,新行的class
= 2
。同时,可序列化事务 B 计算:
1 | SELECT SUM(value) FROM mytab WHERE class = 2; |
并得到结果 300,它会将其与class
= 1
插入到一个新行中。然后两个事务都尝试提交。如果其中一个事务运行在可重复读隔离级别,两者都被允许提交;但是由于没有执行的序列化顺序能在结果上一致,使用可序列化事务将允许一个事务提交并且将回滚另一个并伴有这个消息:
1 | ERROR: could not serialize access due to read/write dependencies among transactions |
这是因为,如果 A 在 B 之前执行,B 将计算得到合计值 330 而不是 300,而且相似地另一种顺序将导致 A 计算出一个不同的合计值。
当依赖可序列化事务来阻止异常时,重要的一点是任何从一个持久化用户表读出数据都不被认为是有效的,直到读它的事务已经成功提交为止。即便是对只读事务也是如此,除了在一个可推迟的只读事务中读取的数据是读出以后立刻有效的,因为这样的一个事务在开始读取任何数据之前会等待,直到它能获得一个快照保证来避免这种问题为止。在所有其他情况下,应用不能依靠在一个后来被中断的事务中读取的结果;相反,它们应当重试事务直到它成功。
要保证真正的可序列化,PostgreSQL使用了谓词锁,这意味着它会保持锁,这些锁让它能够判断在它先运行的情况下,什么时候一个写操作会对一个并发事务中之前读取的结果产生影响。在PostgreSQL中,这些锁并不导致任何阻塞,并且因此不\会导致一个死锁。它们被用来标识和标志并发可序列化事务之间的依赖性,这些事务的组合可能导致序列化异常。相反,一个想要保证数据一致性的读已提交或可重复读事务可能需要拿走一个在整个表上的锁,这可能阻塞其他尝试使用该表的用户,或者它可能会使用不仅会阻塞其他事务还会导致磁盘访问的SELECT FOR UPDATE
或SELECT FOR SHARE
。
像大部分其他数据库系统,PostgreSQL中的谓词锁基于被一个事务真正访问的数据。这些谓词锁将显示在pg_locks
系统视图中,它们的mode
为SIReadLock
。这种在一个查询执行期间获得的特别的锁将依赖于该查询所使用的计划,并且在事务过程中多个细粒度锁(如元组锁)可能和少量粗粒度锁(如页面锁)相结合来防止耗尽用于跟踪锁的内存。如果一个READ ONLY
事务检测到不会有导致序列化异常的冲突发生,它可以在完成前释放其 SIRead 锁。事实上,READ ONLY
事务将常常可以在启动时确立这一事实并避免拿到任何谓词锁。如果你显式地请求一个SERIALIZABLE READ ONLY DEFERRABLE
事务,它将阻塞直到它能够确立这一事实(这是唯一\一种可序列化事务阻塞但可重复读事务不阻塞的情况)。在另一方面,SIRead 锁常常需要被保持到事务提交之后,直到重叠的读写事务完成。
坚持使用可序列化事务可以简化开发。成功提交的并发可序列化事务的任意集合将得到和一次运行一个相同效果的这种保证意味着,如果你能证明一个单一事务在独自运行时能做正确的事情,则你可以相信它在任何混合的可序列化事务中也能做正确的事情,即使它不知道那些其他事务做了些什么,否则它将不会成功提交。重要的是使用这种技术的环境有一种普遍的方法来处理序列化失败(总是会返回一个 SQLSTATE 值 '40001'),因为它将很难准确地预计哪些事务可能为读/写依赖性做贡献并且需要被回滚来阻止序列化异常。读/写依赖性的监控会产生开销,如重启被序列化失败中止的事务,但是作为在该开销和显式锁及SELECT FOR UPDATE
或SELECT FOR SHARE
导致的阻塞之间的一种平衡,可序列化事务是在某些环境中最好性能的选择。
虽然PostgreSQL的可序列化事务隔离级别只允许并发事务在能够证明有一种串行执行能够产生相同效果的前提下提交,但它却不能总是阻止在真正的串行执行中不会发生的错误产生。尤其是可能会看到由于可序列化事务重叠执行导致的唯一约束被违背的情况,这些情况即便在尝试插入键之前就显式地检查过该键不存在也会发生。避免这种问题的方法是,确保所有\插入可能会冲突的键的可序列化事务首先显式地检查它们能不能那样做。例如,试想一个要求用户输入新键的应用,它会通过尝试查询用户给出的键来检查键是否已经存在,或者是通过选取现有最大的键并且加一来产生一个新键。如果某些可序列化事务不遵循这种协议而直接插入新键,则也可能会报告唯一约束被违背,即便在并发事务串行执行的情况下不会发生唯一约束被违背也是如此。
当依赖可序列化事务进行并发控制时,为了最佳性能应该考虑一下问题:
- 在可能时声明事务为
READ ONLY
。 - 控制活动连接的数量,如果需要使用一个连接池。这总是一个重要的性能考虑,但是在一个使用可序列化事务的繁忙系统中这尤为重要。
- 只在一个单一事务中放完整性目的所需要的东西。
- 不要让连接不必要地“闲置在事务中”。配置参数idle_in_transaction_session_timeout可以被用来自动断开拖延会话的连接。
- 在那些由于使用可序列化事务自动提供的保护的地方消除不再需要的显式锁、
SELECT FOR UPDATE
和SELECT FOR SHARE
。 - 当系统因为谓词锁表内存短缺而被强制结合多个页面级谓词锁为一个单一的关系级谓词锁时,序列化失败的比例可能会上升。你可以通过增加max_pred_locks_per_transaction、max_pred_locks_per_relation和max_pred_locks_per_page来避免这种情况。
- 一次顺序扫描将总是需要一个关系级谓词锁。这可能导致序列化失败的比例上升。通过缩减random_page_cost和/或增加cpu_tuple_cost来鼓励使用索引扫描将有助于此。一定要在事务回滚和重启数目的任何减少与查询执行时间的任何全面改变之间进行权衡。
可序列化隔离级别是使用学术数据库文献中称为可序列化快照隔离的技术实现的,通过添加序列化异常事务的检查的方式构建在快照隔离的基础之上。 与使用传统锁技术的其他系统相比,可以观察到行为和性能方面的一些差异。详细信息请参阅[ports12]。
SET TRANSACTION
SET TRANSACTION — 设置当前事务的特性
大纲
1 2 3 4 5 6 7 8 9 | SET TRANSACTION transaction_mode [, ...] SET TRANSACTION SNAPSHOT snapshot_id SET SESSION CHARACTERISTICS AS TRANSACTION transaction_mode [, ...] 其中 transaction_mode 是下列之一: ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } READ WRITE | READ ONLY [ NOT ] DEFERRABLE |
描述
SET TRANSACTION
命令设置当前 会话的特性。SET SESSION CHARACTERISTICS
设置一个会话后续事务的默认 事务特性。在个体事务中可以用 SET TRANSACTION
覆盖这些默认值。
可用的事务特性是事务隔离级别、事务访问模式(读/写或只读)以及 可延迟模式。此外,可以选择一个快照,不过只能用于当前事务而不能 作为会话默认值。
一个事务的隔离级别决定当其他事务并行运行时该事务能看见什么数据:
READ COMMITTED
一个语句只能看到在它开始前提交的行。这是默认值。
REPEATABLE READ
当前事务的所有语句只能看到这个事务中执行的第一个查询或者 数据修改语句之前提交的行。
SERIALIZABLE
当前事务的所有语句只能看到这个事务中执行的第一个查询或者 数据修改语句之前提交的行。如果并发的可序列化事务间的读写 模式可能导致一种那些事务串行(一次一个)执行时不可能出现 的情况,其中之一将会被回滚并且得到一个
serialization_failure
错误。
SQL 标准定义了一种额外的级别:READ UNCOMMITTED
。在 PostgreSQL中READ UNCOMMITTED
被视作 READ COMMITTED
。
一个事务执行了第一个查询或者数据修改语句( SELECT
、 INSERT
、DELETE
、 UPDATE
、FETCH
或 COPY
)之后就无法更改事务隔离级别。 更多有关事务隔离级别和并发控制的信息可见第 13 章。
事务的访问模式决定该事务是否为读/写或者只读。读/写是默认值。 当一个事务为只读时,如果 SQL 命令 INSERT
、UPDATE
、 DELETE
和COPY FROM
要写的表不是一个临时表,则它们不被允许。不允许 CREATE
、ALTER
以及 DROP
命令。不允许COMMENT
、 GRANT
、REVOKE
、 TRUNCATE
。如果EXPLAIN ANALYZE
和EXECUTE
要执行的命令是上述命令之一, 则它们也不被允许。这是一种高层的只读概念,它不能阻止所有对 磁盘的写入。
只有事务也是SERIALIZABLE
以及 READ ONLY
时,DEFERRABLE
事务属性才会有效。当一个事务的所有这三个属性都被选择时,该事务在 第一次获取其快照时可能会阻塞,在那之后它运行时就不会有 SERIALIZABLE
事务的开销并且不会有任何牺牲或者 被一次序列化失败取消的风险。这种模式很适合于长时间运行的报表或者 备份。
SET TRANSACTION SNAPSHOT
命令允许新的事务 使用与一个现有事务相同的快照运行。已经存在的事务 必须已经把它的快照用pg_export_snapshot
函数( 见第 9.27.5 节)导出。 该函数会返回一个快照标识符,SET TRANSACTION SNAPSHOT
需要被给定一个快照标识符来指定要导入的快照。 在这个命令中该标识符必须被写成一个字符串,例如 '000003A1-1'
。 SET TRANSACTION SNAPSHOT
只能在一个事务的 开始执行,并且要在该事务的第一个查询或者数据修改语句(SELECT
、 INSERT
、DELETE
、 UPDATE
、FETCH
或 COPY
)之前执行。此外,该事务必须已经被设置 为SERIALIZABLE
或者 REPEATABLE READ
隔离级别(否则,该快照将被立刻抛弃, 因为READ COMMITTED
模式会为每一个命令取一个新快照)。 如果导入事务使用了SERIALIZABLE
隔离级别,那么导入快照 的事务必须也使用该隔离级别。还有,一个非只读可序列化事务不能导入来自只读 事务的快照。
注解
如果执行SET TRANSACTION
之前没有 START TRANSACTION
或者 BEGIN
,它会发出一个警告并且不会有任何效果。
可以通过在BEGIN
或者 START TRANSACTION
中指定想要的transaction_modes
来省掉 SET TRANSACTION
。但是在 SET TRANSACTION SNAPSHOT
中该选项不可用。
会话默认的事务模式也可以通过设置配置参数 default_transaction_isolation、 default_transaction_read_only和 default_transaction_deferrable来设置(实际上 SET SESSION CHARACTERISTICS
只是用 SET
设置这些变量的等效体)。这意味着可以通过配置文件、 ALTER DATABASE
等方式设置默认值。详见 第 19 章。
示例
要用一个已经存在的事务的同一快照开始一个新事务,首先要从该现有 事务导出快照。这将会返回快照标识符,例如:
1 2 3 4 5 6 | BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; SELECT pg_export_snapshot(); pg_export_snapshot --------------------- 00000003-0000001B-1 (1 row) |
然后在一个新开始的事务的开头把该快照标识符用在一个 SET TRANSACTION SNAPSHOT
命令中:
1 2 | BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; SET TRANSACTION SNAPSHOT '00000003-0000001B-1'; |
兼容性
SQL标准中定义了这些命令,不过 DEFERRABLE
事务模式和 SET TRANSACTION SNAPSHOT
形式除外,这两者是 PostgreSQL扩展。
SERIALIZABLE
是标准中默认的事务隔离级别。在 PostgreSQL中默认值是普通的 READ COMMITTED
,但是你可以按上述的方式更改。
在 SQL 标准中,可以用这些命令设置一个其他的事务特性:诊断区域 的尺寸。这个概念与嵌入式 SQL 有关,并且因此没有在 PostgreSQL服务器中实现。
SQL 标准要求连续的transaction_modes
之间有逗号, 但是出于历史原因 PostgreSQL允许省略逗号。
START TRANSACTION
START TRANSACTION — 开始一个事务块
大纲
1 2 3 4 5 6 7 | START TRANSACTION [ transaction_mode [, ...] ] 其中 transaction_mode 是下列之一: ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } READ WRITE | READ ONLY [ NOT ] DEFERRABLE |
描述
这个命令开始一个新的事务块。如果指定了隔离级别、读写模式 或者可延迟模式,新的事务将会具有这些特性,就像执行了 SET TRANSACTION一样。这和 BEGIN命令一样。
参数
这些参数对于这个语句的含义可参考 SET TRANSACTION。
兼容性
在标准中,没有必要发出START TRANSACTION
来开始一个事务块:任何 SQL 命令会隐式地开始一个块。 PostgreSQL的行为可以被视作 在每个命令之后隐式地发出一个没有跟随在 START TRANSACTION
( 或者BEGIN
)之后的 COMMIT
并且因此通常被称作 “自动提交”。为了方便,其他关系型数据库系统也可能会 提供自动提交特性。
DEFERRABLE
transaction_mode
是一种PostgreSQL语言扩展。
SQL 标准要求在连续的transaction_modes
之间有逗号, 但是由于历史原因PostgreSQL允许 省略逗号。
另见SET TRANSACTION的兼容性小节。
BEGIN
BEGIN — 开始一个事务块
大纲
1 2 3 4 5 6 7 | BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ] 其中 transaction_mode 是以下之一: ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED } READ WRITE | READ ONLY [ NOT ] DEFERRABLE |
描述
BEGIN
开始一个事务块,也就是说所有 BEGIN
命令之后的所有语句将被在一个 事务中执行,直到给出一个显式的COMMIT或 者ROLLBACK。默认情况下(没有 BEGIN
), PostgreSQL在 “自动提交”模式中执行事务,也就是说每个语句都 在自己的事务中执行并且在语句结束时隐式地执行一次提交(如果执 行成功,否则会完成一次回滚)。
在一个事务块内的语句会执行得更快,因为事务的开始/提交也要求可观 的 CPU 和磁盘活动。在进行多个相关更改时,在一个事务内执行多个语 句也有助于保证一致性:在所有相关更新还没有完成之前,其他会话将不 能看到中间状态。
如果指定了隔离级别、读/写模式或者延迟模式,新事务也会有那些特性, 就像执行了SET TRANSACTION一样。
参数
WORK
TRANSACTION
可选的关键词。它们没有效果。
这个语句其他参数的含义请参考 SET TRANSACTION。
注解
START TRANSACTION具有和BEGIN
相同的功能。
在已经在一个事务块中时发出BEGIN
将惹出一个警告 消息。事务状态不会被影响。要在一个事务块中嵌套事务,可以使用保 存点(见SAVEPOINT)。
由于向后兼容的原因,连续的 transaction_modes
之间的逗号可以被省略。
示例
开始一个事务块:
1 | BEGIN; |
兼容性
BEGIN
是一种 PostgreSQL语言扩展。它等效于 SQL 标准的命令START TRANSACTION,它的参考页 包含额外的兼容性信息。
DEFERRABLE
transaction_mode
是一种PostgreSQL语言扩展。
附带地,BEGIN
关键词被用于嵌入式 SQL 中的一种 不同目的。在移植数据库应用时,我们建议小心对待事务语义。
COMMIT
COMMIT — 提交当前事务
大纲
1 | COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] |
描述
COMMIT
提交当前事务。所有由该 事务所作的更改会变得对他人可见并且被保证在崩溃发生时仍能 持久。
参数
WORK
TRANSACTION
可选的关键词。它们没有效果。
AND CHAIN
如果指定了
AND CHAIN
,则立即启动与刚刚完成的事务具有相同事务特征(参见SET TRANSACTION)的新事务。 否则,没有新事务被启动。
注解
使用ROLLBACK中止一个事务。
当不在一个事务内时发出COMMIT
不会 产生危害,但是它会产生一个警告消息。当COMMIT AND CHAIN
不在事务内时是一个错误。
示例
要提交当前事务并且让所有更改持久化:
1 | COMMIT; |
兼容性
命令COMMIT
符合 SQL 标准。 表单COMMIT TRANSACTION
为PostgreSQL扩展。
SAVEPOINT
SAVEPOINT — 在当前事务中定义一个新的保存点
大纲
1 | SAVEPOINT savepoint_name |
描述
SAVEPOINT
在当前事务中建立一个新保存点。
保存点是事务内的一种特殊标记,它允许所有在它被建立之后执行的命令被 回滚,把该事务的状态恢复到它处于保存点时的样子。
参数
savepoint_name
给新保存点的名字。
注解
使用ROLLBACK TO SAVEPOINT回滚到一个保存点。 使用RELEASE SAVEPOINT销毁一个保存点, 但保持在它被建立之后执行的命令的效果。
保存点只能在一个事务块内建立。可以在一个事务内定义多个保存点。
示例
要建立一个保存点并且后来撤销在它建立之后执行的所有命令的效果:
1 2 3 4 5 6 7 | BEGIN; INSERT INTO table1 VALUES (1); SAVEPOINT my_savepoint; INSERT INTO table1 VALUES (2); ROLLBACK TO SAVEPOINT my_savepoint; INSERT INTO table1 VALUES (3); COMMIT; |
上面的事务将插入值 1 和 3,但不会插入 2。
要建立并且稍后销毁一个保存点:
1 2 3 4 5 6 | BEGIN; INSERT INTO table1 VALUES (3); SAVEPOINT my_savepoint; INSERT INTO table1 VALUES (4); RELEASE SAVEPOINT my_savepoint; COMMIT; |
上面的事务将插入 3 和 4。
兼容性
当建立另一个同名保存点时,SQL 要求之前的那个保存点自动被销毁。在 PostgreSQL中,旧的保存点会被保留,不过在进行 回滚或释放时只能使用最近的那一个(用 RELEASE SAVEPOINT
释放较新的保存点将会 导致较旧的保存点再次变得可以被 ROLLBACK TO SAVEPOINT
和 RELEASE SAVEPOINT
访问)。在其他方面, SAVEPOINT
完全符合 SQL。
ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT — 回滚到一个保存点
大纲
1 | ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] savepoint_name |
描述
回滚在该保存点被建立之后执行的所有命令。该保存点保持有效并且可以在 以后再次回滚到它(如果需要)。
ROLLBACK TO SAVEPOINT
隐式地销毁在所提及的保存点 之后建立的所有保存点。
参数
savepoint_name
要回滚到的保存点。
注解
使用RELEASE SAVEPOINT销毁一个保存点而 不抛弃在它建立之后被执行的命令的效果。
指定一个没有被建立的保存点是一种错误。
相对于保存点,游标有一点非事务的行为。在保存点被回滚时,任何在该保存点 内被打开的游标将会被关闭。如果一个先前打开的游标在一个保存点内被 FETCH
或MOVE
命令所影响,而该该保存点 后来又被回滚,那么该游标将保持FETCH
使它指向的位置(也 就是说由FETCH
导致的游标动作不会被回滚)。回滚也不能 撤销关闭一个游标。不过,其他由游标查询导致的副作用(例如 被该查询所调用的易变函数的副作用) 可以\被回滚,只要它们发生在一个后来被回滚的保存点期间。 如果一个游标的执行导致事务中止,它会被置于一种不能被执行的状态,这样当 事务被用ROLLBACK TO SAVEPOINT
恢复后,该游标也不再能 被使用。
示例
要撤销在my_savepoint
建立后执行的命令的效果:
1 | ROLLBACK TO SAVEPOINT my_savepoint; |
游标位置不会受保存点回滚的影响:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | BEGIN; DECLARE foo CURSOR FOR SELECT 1 UNION SELECT 2; SAVEPOINT foo; FETCH 1 FROM foo; ?column? ---------- 1 ROLLBACK TO SAVEPOINT foo; FETCH 1 FROM foo; ?column? ---------- 2 COMMIT; |
兼容性
SQL标准指定关键词 SAVEPOINT
是强制的,但是PostgreSQL 和Oracle允许省略它。SQL 只允许WORK
而 不是TRANSACTION
作为ROLLBACK
之后的噪声词。 还有,SQL 有一个可选的子句 AND [ NO ] CHAIN
,当前 PostgreSQL并不支持。在其他方面,这个命令符合 SQL 标准。
RELEASE SAVEPOINT
RELEASE SAVEPOINT — 销毁一个之前定义的保存点
大纲
1 | RELEASE [ SAVEPOINT ] savepoint_name |
描述
RELEASE SAVEPOINT
销毁在当前事务 中之前定义的一个保存点。
销毁一个保存点会使得它不能再作为一个回滚点,但是它没有其他用户 可见的行为。它不会撤销在该保存点被建立之后执行的命令的效果(要 这样做,可见ROLLBACK TO SAVEPOINT)。当不再需要一个 保存点时销毁它允许系统在事务结束之前回收一些资源。
RELEASE SAVEPOINT
也会销毁所有 在该保存点建立之后建立的保存点。
参数
savepoint_name
要销毁的保存点的名称。
注解
指定一个不是之前定义的保存点名称是错误。
当事务处于中止状态时不能释放保存点。
如果多个保存点具有相同的名称,只有最近被定义的那个会被释放。
示例
建立并且销毁一个保存点:
1 2 3 4 5 6 | BEGIN; INSERT INTO table1 VALUES (3); SAVEPOINT my_savepoint; INSERT INTO table1 VALUES (4); RELEASE SAVEPOINT my_savepoint; COMMIT; |
上述事务将插入 3 和 4。
兼容性
这个命令符合SQL标准。该标准指定关键词 SAVEPOINT
是强制需要的,但 PostgreSQL允许省略。
ABORT
ABORT — 中止当前事务
大纲
1 | ABORT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] |
描述
ABORT
回滚当前事务并且导致由该事务所作的所有更新被丢弃。这个命令的行为与标准SQL命令ROLLBACK的行为一样,并且只是为了历史原因存在。
参数
WORK
TRANSACTION
可选关键词。它们没有效果。
AND CHAIN
如果规定了
AND CHAIN
,新事务立即启动,具有与刚刚完成的事务相同的事务特征(参见 SET TRANSACTION)。否则,不会启动新事务。
注解
使用COMMIT成功地终止一个事务。
在一个事务块之外发出ABORT
会发出一个警告消息并且不会产生效果。
例子
中止所有更改:
1 | ABORT; |
兼容性
这个命令是一个因为历史原因而存在的PostgreSQL扩展。ROLLBACK
是等效的标准 SQL 命令。
ROLLBACK
ROLLBACK — 中止当前事务
大纲
1 | ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ] |
描述
ROLLBACK
回滚当前事务并且导致 该事务所作的所有更新都被抛弃。
参数
WORK
TRANSACTION
可选关键词,没有效果。
AND CHAIN
如果指定了
AND CHAIN
,则立即启动与刚刚完成事务具有相同事务特征(参见 SET TRANSACTION)的新事务。 否则,不会启动任何新事务。
注解
使用COMMIT可成功地终止一个事务。
在一个事务块之外发出ROLLBACK
会发出一个警告并且不会有效果。 事务块之外的ROLLBACK AND CHAIN
是一个错误.
示例
要中止所有更改:
1 | ROLLBACK; |
兼容性
命令ROLLBACK
符合 SQL 标准。窗体ROLLBACK TRANSACTION
是一个PostgreSQL 扩展。