一、命令摘要

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 [, ...]

where transaction_mode is one of:

ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
READ WRITE | READ ONLY
[ NOT ] DEFERRABLE

二、描述

SET TRANSACTION 该命令设置当前事务的特性。它对任何后续的事务都没有影响。而 SET SESSION CHARACTERISTICS 为回话的后续事务设置默认的事务特性。这些默认的特性可以被单个事务的 SET TRANSACTION 覆盖。

可选的事务特性包括:事务隔离级别、事务访问模式(读写或只读)和可延迟模式。此外,可以为当前事务选择快照。

1、 事务隔离级别

事务隔离级别决定了当有其他事务在并发运行时当前事务可以看到哪些数据:

  • read committed,读已提交。默认隔离级别。某个语句只能看到它执行之前提交的行。
  • repeatable read,可重复读。当前事务的所有语句只能看到此事务中执行第一个查询或修改语句之前提交的行。
  • serializable,序列化。当前事务的所有语句只能看到在此事务中执行第一个查询或修改语句之前提交的行。由于并发执行带来的不确定性,某些事务的执行顺序可能会导致数据的不一致,即使每个事务单独看都是正确的。当数据库系统检测到这种情况时,即某个并发事务的执行模式可能导致无法通过任何串行执行序列来重现的结果时,它会触发一个序列化失败(Serialization Failure)。为了恢复数据的一致性,系统通常会选择一个或多个事务进行回滚(Rollback),并可能将错误信息(如您提到的serialization_failure错误)报告给应用程序。

SQL 标准中有一个隔离级别为 read uncommitted,在 PostgreSQL 中 read uncommitted 被视为 read committed

在执行事务的第一个查询或数据修改语句(select、insert、delete、update、merge、fetch 或 copy)之后,不能再更改事务隔离级别。

2、 事务访问模式

事务访问模式决定事务是读写还是只读。默认值为读写

当事务为只读时,如果要写入的表不是临时表,则不允许使用这些 SQL 命令:

  • insert、update、delete、merge 和 copy from;
  • 所有 create、alter 和 drop 命令;
  • comment、grant、revoke、truncate;
  • explain analyze、execute,如果它们执行的命令是上面其中之一的话。

注意:这里的“只读”是一个“高级别”的概念,不是底层磁盘上面的“只读”,它并不组织所有向磁盘的写入操作。即使事务是只读的,但是数据库系统仍然可能需要在内部执行一些写入操作来维护其状态或进行日志记录。但是,这些内部写入操作不会违反事务的只读性质,因为它们不会影响到事务中可见的数据内容。

此外,只读事务的一个主要目的是防止事务对数据库进行任何修改性操作,以确保数据的一致性和可预测性。在只读事务中执行上述列出的命令可能会导致数据不一致或违反数据库的完整性约束。因此,数据库系统会禁止在只读事务中执行这些命令。

3、 延迟

除非将事务设置为 serializableread only,否则 deferable 属性将不会产生任何效果。当事务配置了这三个属性时,事务在首次获取其快照时可能会阻塞,之后它能够运行而不会产生序列化事务通常的额外开销,并且不会有任何因序列化失败而导致被取消的风险。这种模式非常适合于运行时间较长的报告或备份操作。

deferable 属性允许事务中的锁定延迟到实际访问数据时才进行,而不是在事务开始时立即进行。在 serializableread only 的约束下,这种延迟锁定的能力使得事务能够在不干扰其他并发事务的情况下,以一种较为高效和安全的方式执行。尽管在开始时可能需要等待其他事务释放锁(即阻塞),但一旦获得了必要的快照,事务就可以在没有其他序列化开销的情况下继续执行。这对于需要长时间运行且对数据一致性要求极高的操作特别有用。

4、 快照

SET TRANSACTION SNAPSHOT 命令允许一个新事务使用与现有事务相同的快照来运行。预先存在的事务必须使用 pg_export_snapshot 函数导出其快照。该函数返回一个快照标识符,该标识符必须传递给 SET TRANSACTION SNAPSHOT 以指定要导入的快照。在这个命令中,标识符必须以字符串常量的形式写入,例如 '00000003-0000001B-1'

SET TRANSACTION SNAPSHOT 只能在事务开始时执行,即在事务的第一个查询或数据修改语句之前。

此外事务必须被设置为 serializablerepeatable read 隔离级别。否则,快照将会被立刻丢弃。因为 read committed 隔离级别会为每个命令获取新的快照。

如果导入事务使用 serializable 隔离级别,那么导出快照的事务也必须使用该隔离级别。此外,一个非只读的 serializable 事务不能从只读事务中导入快照。

三、注意

如果 set transaction 执行时前面没有 start transactionbegin,则会发出警告并且不会生效。

start transactionbegin 命令中指定 transaction_mode 可以省去 set transaction,但是不适用于 set transaction snapshot

为会话设置默认的事务模式,可以通过配置参数 default_transaction_isolationdefault_transaction_read_onlydefault_translation_deferrable 代替。

类似的,可以通过配置参数 transaction_inisolationtransaction_read_onlytransaction_deferrable 来设置或检查当前事务的模式。

四、示例

1
2
3
4
5
6
7
8
9
10
begin;  
set transaction isolation level repeatable read ;

select * from z2huo_user;
insert into z2huo_user (id, user_name) values (4, 'zyz');
select * from z2huo_user;
insert into z2huo_user (id, user_name) values (5, 'zyzzyz');
select * from z2huo_user;

commit;

五、扩展

除了延迟和快照是 PostgreSQL 的扩展之外,其他的都是在 SQL 标准中定义的。

标准中默认的隔离级别是 serializable,但在 PostgreSQL 中,默认隔离级别是 read committed

SQL 标准中要求在连续的 transaction_mode 之间使用逗号,但是因为历史原因,PostgreSQL 允许省略逗号。

相关链接

OB tags

#PostgreSQL