直列化可能分離 - Amazon Redshift

直列化可能分離

一部のアプリケーションでは、クエリとロードを同時に行うだけでなく、複数のテーブルまたは同じテーブルに同時に書き込む能力を必要とします。この文脈では、同時とは、厳密に同じ時間に実行するようにスケジュールするという意味ではなく、重複するという意味です。最初のトランザクションがコミットする前に 2 つ目のトランザクションが開始する場合、2 つのトランザクションは同時であると考えられます。同時操作は、同じユーザーまたは異なるユーザーが制御する異なるセッションから発生する可能性があります。

注記

Amazon Redshift はデフォルトの自動コミット動作がサポートされます。この動作では、個別に実行された SQL コマンドが個別にコミットします。(BEGIN および END ステートメントにより定義された) トランザクションブロックに一連のコマンドを含めた場合、そのブロックは 1 つのトランザクションとしてコミットされます。そのため、必要に応じてロールバックできます。この動作の例外は、TRUNCATE コマンドと VACUUM コマンドです。このコマンドは、現在のトランザクションで行われた未処理の変更をすべて自動的にコミットします。

一部の SQL クライアントでは、BEGIN コマンドと COMMIT コマンドが自動的に発行されるため、クライアントは、ステートメントのグループをトランザクションとして実行するか、個々のステートメントを独自のトランザクションとして実行するかを制御します。使用しているインターフェイスのマニュアルを確認してください。例えば、Amazon Redshift JDBC ドライバーを使用している場合、複数の (セミコロンで区切られた) SQL コマンドを含むクエリ文字列を持つ JDBC PreparedStatement は、すべてのステートメントを 1 つのトランザクションとして実行します。対照的に、SQL Workbench/J を使用して AUTO COMMIT ON を設定した後、複数のステートメントを実行すると、各文が独自のトランザクションとして実行されます。

Amazon Redshift では、同時書き込みオペレーションはテーブルの書き込みロックと直列化分離を利用して安全にサポートされます。直列化分離では、テーブルに対して実行されるトランザクションは、そのテーブルに対して実行される唯一のトランザクションであるという錯覚が守られます。例えば、T1 と T2 という 2 つの同時実行トランザクションで次の少なくとも 1 つとして同じ結果が生成されます。

  • T1 と T2 がこの順序で連続して実行されます。

  • T2 と T1 がこの順序で連続して実行されます。

同時トランザクションは互いに認識されません。互いの変更を検出できません。各同時トランザクションにより、トランザクションの始めにデータベースのスナップショットが作成されます。データベーススナップショットは、ほとんどの SELECT ステートメント、COPY、DELETE、INSERT、UPDATE、TRUNCATE などの DML コマンド、次の DDL コマンドの最初の発生時にトランザクション内で作成されます。

  • ALTER TABLE (列を追加または削除する)

  • CREATE TABLE

  • DROP TABLE

  • TRUNCATE TABLE

同時トランザクションの任意の直列実行において、同時実行と同じ結果が生成された場合は、そのトランザクションは「直列化可能」とみなされるので、安全に実行できます。そのようなトランザクションの直列実行で、同じ結果が生成されない場合、直列化可能性を阻害する可能性のあるステートメントを実行するトランザクションが中断され、ロールバックされます。

システムカタログテーブル (PG) と他の Amazon Redshift システムテーブル (STL と STV) はトランザクション内にロックされません。そのため、DDL および TRUNCATE オペレーションから発生したデータベースオブジェクトに対する変更は、いずれかの同時トランザクションにコミットすることで表示が可能になります。

例えば、T1 と T2 という 2 つの同時トランザクションが開始するとき、テーブル A がデータベースに存在します。今、T2 が、テーブルのリストを PG_TABLES カタログテーブルから選択して返しているとします。この時、T1 はテーブル A をドロップしてコミットし、その後 T2 はテーブルを再度リストします。この後、テーブル A はリストされることはなくなります。T2 が削除されたテーブルのクエリを試行すると、Amazon Redshift は "関係が存在しない" というエラーを返します。T2 にテーブルのリストを返す、またはテーブル A が存在することをチェックするカタログクエリは、ユーザーテーブルに対し実行されるオペレーションと同じ分離ルールの対象にはなりません。

これらのテーブルに対する更新のトランザクションはコミット済み読み取り分離モードで実行されます。PG プレフィックスのカタログテーブルは、スナップショットの分離をサポートしていません。

システムテーブルとカタログテーブルの直列化分離

データベーススナップショットは、ユーザーが作成したテーブルまたは Amazon Redshift システムテーブル (STL または STV) を参照する SELECT クエリのトランザクションでも作成されます。どのテーブルも参照しない SELECT クエリは、新しいトランザクションのデータベーススナップショットを作成しません。システムカタログテーブル (PG) でだけ実行される INSERT、DELETE、UPDATE ステートメントでは、新しいトランザクションのデータベーススナップショットは作成されません。

直列化可能分離エラーを修正する方法

エラー: 1023 詳細: Redshift テーブルで直列化可能分離に関する違反が発生しました

Amazon Redshift で直列化可能な分離エラーが検出されると、次のようなエラーメッセージが表示されます。

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

直列化可能な分離エラーに対処するには、次の方法をお試しください。

  • キャンセルされたトランザクションを再試行します。

    Amazon Redshift は、同時ワークロードがシリアル化できないことを検出しました。これは、アプリケーションのロジックにギャップがあることを示唆しています。通常、エラーが発生したトランザクションを再試行することで回避できます。問題が解決しない場合は、他のいずれかの方法をお試しください。

  • 同じアトミックトランザクション内にある不要なオペレーションは、トランザクション外に移動します。

    この方法は、2 つのトランザクション内の個々のオペレーションが、他のトランザクションの結果に影響を及ぼす可能性のある方法で相互参照する場合に適用されます。例えば、次の 2 つのセッションはそれぞれトランザクションを開始します。

    Session1_Redshift=# begin;
    Session2_Redshift=# begin;

    各トランザクションの SELECT ステートメントの結果は、もう一方の INSERT ステートメントの影響を受ける可能性があります。つまり、次のステートメントを任意の順序で連続して実行するとします。いずれの場合でも、結果として、トランザクションが同時に実行された場合よりも、SELECT ステートメントで 1 行多く返ります。同時実行の場合と同じ結果を生成するオペレーションを連続して実行できる順序はありません。そのため、最後に実行されるオペレーションは、直列化可能な分離エラーになります。

    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;

    これらの例では、トランザクションに相互参照はありません。2 つの INSERT ステートメントは相互に影響しません。これらの例では、トランザクションを連続して実行し、同時に実行する場合と同じ結果を生成できる順序が少なくとも 1 つあります。つまり、このトランザクションは直列化可能です。

  • 直列化を適用するには、各セッションですべてのテーブルをロックします。

    LOCK コマンドでは、直列化可能な分離エラーを引き起こす可能性のあるオペレーションをブロックします。LOCK コマンドを使用するときは、次の点を確認してください。

    • トランザクション内の読み取り専用 SELECT ステートメントの影響を受けるテーブルなど、トランザクションの影響を受けるすべてのテーブルをロックします。

    • オペレーションが実行される順序に関係なく、テーブルを同じ順序でロックします。

    • オペレーションを実行する前に、トランザクションの開始時にすべてのテーブルをロックします。

エラー: 1018 詳細: リレーションが存在しません

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 は、エラーなしでテーブルから読み込むことができます。