Amazon Redshift
数据库开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

可序列化的隔离

某些应用程序不仅需要并发查询和加载,还需要能同时对多个表或同一个表进行写入。在此环境中,并发 指的是重叠,而不是安排在完全相同的时间运行。如果两个事务中的第二个事务在第一个提交前开始,则将两个事务视为并发。并发操作可源自由同一用户或不同用户控制的不同会话。

注意

Amazon Redshift 支持默认的自动提交 行为,其中,每个单独执行的 SQL 命令都将分别提交。如果您在某个事务数据块中包含一组命令(由 BEGINEND 语句定义),则该数据块将作为一个事务提交,以便您在必要时对其进行回滚。此行为的一个例外是 TRUNCATE 命令,此命令可自动提交当前事务中所做的所有待定更改,而不需要 END 语句。

在 Amazon Redshift 中,以保护性方式支持并发写入操作,即对表使用写入锁定和可序列化的隔离 原则。可序列化的隔离会保留一种错觉,即对某个表运行的事务是对该表运行的唯一事务。例如,两个同时运行的事务(T1 和 T2)必须生成与以下至少一个项相同的结果:

  • T1 和 T2 依次运行

  • T2 和 T1 依次运行

并发事务彼此不可见;它们不能相互检测对方的更改。每个并发事务将在事务开始时创建数据库快照。在大多数 SELECT 语句、DML 命令(例如 COPY、DELETE、INSERT、UPDATE 和 TRUNCATE)和以下 DDL 命令首次出现时,将在事务中创建数据库快照:

  • ALTER TABLE(添加或删除列)

  • CREATE TABLE

  • DROP TABLE

  • TRUNCATE TABLE

如果并发事务的任何 序列执行将产生与它们的并发执行相同的结果,则这些事务将被视为“可序列化的”且可安全运行。如果这些事务的任何序列执行均不产生相同结果,则执行会破坏可序列性的语句的事务将被中止并回滚。

系统目录表 (PG) 和其他 Amazon Redshift 系统表(STL 和 STV)在事务中未锁定;因此,DDL 和 TRUNCATE 操作引起的数据库对象更改在提交到任何并发事务时均可见。

例如,假设在两个并发事务(T1 和 T2)开始时,数据库中存在表 A。如果 T2 通过从 PG_TABLES 目录表中进行选择来返回一个表的列表,T1 将删除表 A 并提交,然后 T2 再次列出表,则将不再列出表 A。如果 T2 尝试查询已删除的表,则 Amazon Redshift 将返回“relation does not exist”错误。向 T2 返回表列表或检查表 A 是否存在的目录查询不受与针对用户表的操作相同的隔离规则的约束。

更新这些表的事务在读取已提交 隔离模式下运行。前缀为 PG 的目录表不支持快照隔离。

系统表和目录表的可序列化隔离

对于任何引用用户创建的表或 Amazon Redshift 系统表(STL 或 STV)的 SELECT 查询,还将在事务中创建数据库快照。不引用任何表的 SELECT 查询将不会创建新的事务数据库快照,只在系统目录表 (PG) 上操作的任何 INSERT、DELETE 或 UPDATE 语句也不会创建该快照。

如何修复可序列化的隔离错误

当 Amazon Redshift 检测到可序列化的隔离错误时,您会看到错误消息,如下所示。

ERROR:1023 DETAIL: Serializable isolation violation on table in Redshift

要解决可序列化的隔离错误,您可以尝试以下方法:

  • 将任何不必在同一个原子事务中的操作移到事务之外。

    当两个事务内的各个操作以可能影响另一个事务的结果的方式相互交叉引用时,此方法适用。例如,以下两个会话各自启动一个事务。

    Session1_Redshift=# begin;
    Session2_Redshift=# begin;

    每个事务中的 SELECT 语句的结果可能受另一个事务中的 INSERT 语句影响。换句话说,假设您以任何顺序连续运行以下语句。在每种情况下,如果不是同时运行事务,结果是 SELECT 语句之一另外返回一行。操作顺序运行时,没有哪种顺序可以产生与并发运行时相同的结果。因此,运行的最后一个操作会导致可序列化的隔离错误。

    Session1_Redshift=# select * from tab1; Session1_Redshift=# insert into tab2 values (1);
    Session2_Redshift=# insert into tab1 values (1); Session1_Redshift=# select * from tab2;

    在许多情况下,SELECT 语句的结果并不重要。换句话说,事务中操作的原子性并不重要。在这些情况下,将 SELECT 语句移到事务之外,如以下示例所示。

    Session1_Redshift=# begin; Session1_Redshift=# insert into tab1 values (1) Session1_Redshift=# end; Session1_Redshift=# select * from tab2;
    Session2_Redshift # select * from tab1; Session2_Redshift=# begin; Session2_Redshift=# insert into tab2 values (1) Session2_Redshift=# end;

    在这些示例中,事务中没有交叉引用。两个 INSERT 语句不会相互影响。在这些示例中,至少有一个顺序,其中事务可以顺序运行并产生与并发运行时相同的结果。这意味着事务是可序列化的。

  • 通过锁定每个会话中的所有表来强制序列化。

    LOCK 命令阻止可能导致可序列化隔离错误的操作。使用 LOCK 命令时,请确保执行以下操作:

    • 锁定受事务影响的所有表,包括受事务内部的只读 SELECT 语句影响的表。

    • 无论执行操作的顺序如何,都以相同的顺序锁定表。

    • 在执行任何操作之前,在事务开始时锁定所有表。

  • 重试已中止的事务。

    如果事务与另一个并发事务执行的操作冲突,则事务可能会遇到可序列化的隔离错误。如果冲突的事务不需要同时运行,则只重试中止的事务可能会成功。如果问题仍然存在,请尝试使用其他方法之一。