可序列化隔离错误排查 - Amazon Redshift

从 2025 年 11 月 1 日起,Amazon Redshift 将不再支持创建新的 Python UDF。如果您想要使用 Python UDF,请在该日期之前创建 UDF。现有的 Python UDF 将继续正常运行。有关更多信息,请参阅博客文章

可序列化隔离错误排查

ERROR:1023 DETAIL:Redshift 中的表上的可序列化隔离冲突

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

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

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

  • 重试已取消的事务。

    Amazon 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); Session2_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 语句影响的表。

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

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

  • 对并发事务使用快照隔离

    将 ALTER DATABASE 命令与快照隔离功能结合使用。有关 ALTER DATABASE 的 SNAPSHOT 参数的更多信息,请参阅参数

ERROR:1018 DETAIL:关系不存在

当您在不同会话中运行 Amazon Redshift 并发操作时,您会看到错误消息,如下所示。

ERROR: 1018 DETAIL: Relation does not exist.

Amazon Redshift 中的事务遵循快照隔离。在事务开始后,Amazon Redshift 将拍摄数据库的快照。对于事务的整个生命周期,事务在快照中反映的数据库状态下运行。如果事务从快照中不存在的表读取,则会引发之前显示的 1018 错误消息。即使另一个并发事务在事务拍摄快照后创建表,该事务也无法从新创建的表中读取。

要解决此序列化隔离错误,您可以尝试将事务的起始位置移动到您所知道的该表存在的位置。

如果表是由另一个事务创建的,则此位置至少在该事务提交之后。此外,请确保没有提交可能已删除表的并发事务。

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session2 = # BEGIN;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # SELECT * FROM A;

作为 session2 的读取操作运行的最后一个操作会导致可序列化的隔离错误。当 session2 拍摄快照并且表已被提交的 session1 删除时,会发生此错误。换句话说,即使并发 session3 已创建表,session2 也不会看到该表,因为它不在快照中。

要解决此错误,您可以按以下方式对会话重新排序。

session1 = # BEGIN; session1 = # DROP TABLE A; session1 = # COMMIT;
session3 = # BEGIN; session3 = # CREATE TABLE A (id INT); session3 = # COMMIT;
session2 = # BEGIN; session2 = # SELECT * FROM A;

现在,当 session2 拍摄快照时,session3 已经提交,并且表位于数据库中。Session2 可以从表中读取,而不会出现任何错误。