

 Amazon Redshift は、パッチ 198 以降、新しい Python UDF の作成をサポートしなくなります。既存の Python UDF は、2026 年 6 月 30 日まで引き続き機能します。詳細については、[ブログ記事](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)を参照してください。

# 同時書き込み操作を管理する
<a name="c_Concurrent_writes"></a>

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

Amazon Redshift では増分ロードまたは増分変更の実行中にテーブル読み込みができるので、これらのアプリケーションがサポートされます。クエリは、コミットする次のバージョンの待機ではなく、データの最新コミットバージョンまたは*スナップショット*だけを確認します。特定のクエリに別の書き込み操作からのコミットを待機させる場合、それを適宜スケジュールする必要があります。

**注記**  
Amazon Redshift はデフォルトで*自動コミット*動作がサポートされています。この機能では、個別に実行された SQL コマンドが個別にコミットします。([BEGIN](r_BEGIN.md) および [END](r_END.md) ステートメントにより定義された) トランザクションブロックに一連のコマンドを含めた場合、そのブロックは 1 つのトランザクションとしてコミットされます。そのため、必要に応じてロールバックできます。この動作の例外は、TRUNCATE コマンドと VACUUM コマンドです。このコマンドは、現在のトランザクションで行われた未処理の変更をすべて自動的にコミットします。  
一部の SQL クライアントでは、BEGIN コマンドと COMMIT コマンドが自動的に発行されるため、クライアントは、ステートメントのグループをトランザクションとして実行するか、個々のステートメントを独自のトランザクションとして実行するかを制御します。使用しているインターフェイスのマニュアルを確認してください。例えば、Amazon Redshift JDBC ドライバーを使用している場合、複数の (セミコロンで区切られた) SQL コマンドを含むクエリ文字列を持つ JDBC `PreparedStatement` は、すべてのステートメントを 1 つのトランザクションとして実行します。対照的に、SQL Workbench/J を使用して AUTO COMMIT ON を設定した後、複数のステートメントを実行すると、各文が独自のトランザクションとして実行されます。

次のトピックでは、トランザクション、データベーススナップショット、更新、同時動作など、主要な概念とユースケースの一部を紹介します。

**Topics**
+ [Amazon Redshift の分離レベル](c_serial_isolation.md)
+ [書き込みおよび読み取り/書き込みオペレーション](c_write_readwrite.md)
+ [同時書き込みの例](r_Serializable_isolation_example.md)
+ [直列化可能な分離エラーのトラブルシューティング](c_serial_isolation-serializable-isolation-troubleshooting.md)

# Amazon Redshift の分離レベル
<a name="c_serial_isolation"></a>

Amazon Redshift では、同時書き込みオペレーションはテーブルの書き込みロックと*直列化分離*を利用して安全にサポートされます。直列化分離では、テーブルに対して実行されるトランザクションは、そのテーブルに対して実行される唯一のトランザクションであるという錯覚が守られます。

Amazon Redshift データベースは、トランザクションの開始時に各オペレーションでデータの最新のコミット済みバージョンまたはスナップショットを使用することにより、同時書き込み操作をサポートします。データベーススナップショットは、ほとんどの SELECT ステートメント、COPY、DELETE、INSERT、UPDATE、TRUNCATE などの DML コマンド、次の DDL コマンドの最初の発生時にトランザクション内で作成されます。
+  ALTER TABLE (列を追加または削除する) 
+  CREATE TABLE 
+  DROP TABLE 
+  TRUNCATE TABLE 

他のトランザクションはこのスナップショットを変更できません。つまり、トランザクションは互いに分離されます。同時トランザクションは互いに認識されません。互いの変更を検出できません。

どのトランザクションの同時実行も、それらのトランザクションの直列実行と同じ結果を生成する必要があります。そのようなトランザクションの直列実行で、同じ結果が生成されない場合、直列化可能性を阻害する可能性のあるステートメントを実行するトランザクションが中断され、ロールバックされます。

例えば、ユーザーが T1 と T2 の 2 つの同時トランザクションを実行しようとするとします。T1 と T2 を実行すると、次のシナリオの少なくとも 1 つと同じ結果が生成されます。
+ T1 と T2 がこの順序で連続して実行されます。
+ T2 と T1 がこの順序で連続して実行されます。

 Amazon Redshift の分離レベルは、次の問題を防ぎます。
+  ダーティ読み取り - ダーティ読み取りは、トランザクションがコミットされていないデータを読み取ったときに発生します。例えば、トランザクション 1 が行を更新するとします。トランザクション 2 は T1 が更新をコミットする前に、更新された行を読み取ります。T1 が変更をロールバックすると、T2 は Amazon Redshift が存在したことがないと見なすコミットされていない行のデータを読み取ります。
+  再現不可能な読み取り - 再現不可能な読み取りは、1 つのトランザクションが同じ行を 2 回読み取るが、毎回異なるデータを取得する場合に発生します。例えば、トランザクション 1 が行を読み取るとします。トランザクション 2 は、その行を更新または削除し、更新または削除をコミットします。T1 が行を再読み込みすると、異なる行値を取得するか、行が削除されたことを検出します。
+  ファントム – ファントムは、検索条件に一致するが、最初は表示されない行です。例えば、トランザクション 1 が、その検索条件を満たす行のセットを読み取るとします。トランザクション 2 は、T1 の検索条件に一致する UPDATE ステートメントまたは INSERT ステートメントに新しい行を生成します。T1 が検索ステートメントを再実行すると、別の行セットを取得します。

## SNAPSHOT と SERIALIZABLE の分離
<a name="c_serial_isolation-snapshot_and_serializable"></a>

SERIALIZABLE と SNAPSHOT は、Amazon Redshift で利用できる、直列化可能な分離レベルです。

SNAPSHOT 分離は、プロビジョニングされたクラスターとサーバーレスワークグループを作成するときのデフォルトの分離レベルであり、SERIALIZABLE 分離よりも大量のデータを短時間で処理できます。

SERIALIZABLE の分離には時間がかかりますが、同時トランザクションにはより厳しい制約を実装します。この分離レベルは、1 つのトランザクションのみをコミットし、直列化可能な分離違反エラーで他のすべての同時トランザクションをキャンセルすることで、書き込みスキューの異常などの問題を防ぎます。

以下は、SNAPSHOT 分離を使用する場合に 2 つの同時書き込み操作を処理する方法のタイムラインの例です。各ユーザーの UPDATE ステートメントは、同じ行を更新しようとする競合がないため、コミットが許可されます。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/redshift/latest/dg/c_serial_isolation.html)

直列化可能な分離を使用して同じシナリオを実行する場合、Amazon Redshift は直列化可能な分離違反によりユーザー 2 を終了し、エラー `1023` を返します。詳細については、「[直列化可能な分離エラーのトラブルシューティング](c_serial_isolation-serializable-isolation-troubleshooting.md)」を参照してください。この場合、ユーザー 1 だけが正常にコミットできます。

## 考慮事項
<a name="c_serial_isolation-considerations"></a>

Amazon Redshift の分離レベルを使用する場合、以下の点を考慮してください。
+  STV\$1DB\$1ISOLATION\$1LEVEL カタログビューをクエリして、データベースが使用している分離レベルを表示します。詳細については、「[STV\$1DB\$1ISOLATION\$1LEVEL](r_STV_DB_ISOLATION_LEVEL.md)」を参照してください。
+  PG\$1DATABASE\$1INFO ビューをクエリして、データベースでサポートされている同時トランザクションの数を確認します。詳細については、「[PG\$1DATABASE\$1INFO](r_PG_DATABASE_INFO.md)」を参照してください。
+  システムカタログテーブル (PG) と他の Amazon Redshift システムテーブルはトランザクション内にロックされません。そのため、DDL および TRUNCATE オペレーションから発生したデータベースオブジェクトに対する変更は、いずれかの同時トランザクションにコミットすることで表示が可能になります。

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

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

# 書き込みおよび読み取り/書き込みオペレーション
<a name="c_write_readwrite"></a>

異なるタイプのコマンドを実行するタイミングと方法を決定することで、同時書き込み操作の特定の動作を管理できます。次のコマンドがこの話題に関連します。
+ COPY コマンド、(初回または増分) ロードを実行します
+ INSERT コマンド、1 つまたは複数の行を 1 回で追加します
+ UPDATE コマンド、既存の行を変更します
+ DELETE コマンド、行を削除します 

COPY および INSERT オペレーションは純粋な書き込みオペレーションです。DELETE オペレーションと UPDATE オペレーションは読み取り/書き込みオペレーションです (行を削除または更新するには、最初に読み取る必要があります)。同時書き込み操作の結果は、同時に実行されている特定のコマンドに依存します。

UPDATE 操作と DELETE 操作は、書き込み前に初回テーブル読み込みに依存するため、動作が異なります。同時トランザクションが互いに表示されるとすれば、UPDATE と DELETE は最後のコミットからデータのスナップショットを読み取る必要があります。最初の UPDATE または DELETE がそのロックを解除するとき、2 つ目の UPDATE または DELETE はそれがこれから処理するデータが古くなっている可能性がないか決定する必要があります。最初のトランザクションでそのロックが解除されるまで 2 つ目のトランザクションでデータのスナップショットが取得されないため、データは古くなりません。

## 複数のテーブルが関連する同時書き込みトランザクションで発生する可能性のあるデッドロック状況
<a name="c_write_readwrite-potential-deadlock"></a>

トランザクションに複数のテーブルの更新が関連するとき、両方が同じテーブルセットに書き込もうとすると、同時実行トランザクションにデッドロックが発生する可能性があります。コミットまたはロールバックするとき、トランザクションはそのすべてのテーブルロックを一度に解除します。1 つずつロックを解除することはありません。

例えば、T1 と T2 というトランザクションが大体同じ時間に開始します。T1 がテーブル A に書き込みを開始し、T2 がテーブル B に書き込みを開始する場合、両方のトランザクションは競合なく進行します。ただし、T1 がテーブル A への書き込みを終了し、テーブル B への書き込みを開始する必要がある場合、T2 が引き続き B をロックしているため、T1 は進行できません。同様に、T2 がテーブル B への書き込みを終了し、テーブル A への書き込みを開始する必要がある場合、T1 が引き続き A をロックしているため、T2 は進行できません。その書き込みオペレーションがコミットされるまでいずれのトランザクションもそのロックを解除できないため、いずれのトランザクションも進行できません。この種のデッドロックを回避するには、同時書き込みオペレーションの日程を注意深く計画する必要があります。例えば、常にトランザクションと同じ順序でテーブルを更新する必要があります。ロックを指定する場合、DML 操作を実行する前に同じ順序でテーブルをロックします。

## 単一のテーブルが関連する同時書き込みトランザクションの考えられるデッドロック状況
<a name="c_write_readwrite-potential-deadlock-single"></a>

スナップショット分離環境では、同じテーブルで同時に書き込みトランザクションを実行するとデッドロックが発生する可能性があります。スナップショット分離デッドロックは、INSERT ステートメントまたは COPY ステートメントがロックを共有して同時に進行しており、別のステートメントが同じテーブルで排他的ロックを必要とするオペレーション (UPDATE、DELETE、MERGE、DDL) を実行する必要がある場合に発生します。

次のシナリオを考えてみます。

トランザクション 1 (T1):

```
INSERT/COPY INTO table_A;
```

トランザクション 2 (T2):

```
INSERT/COPY INTO table_A; 
            <UPDATE/DELETE/MERGE/DDL statement> table_A
```

デッドロックは、INSERT オペレーションまたは COPY オペレーションを行う複数のトランザクションが共有ロックを使用して同じテーブルで同時に実行され、それらのトランザクションの 1 つが、純粋な書き込みオペレーションの後に排他的ロックを必要とするオペレーション (UPDATE、MERGE、DELETE、または DDL ステートメントなど) を続けて実行しようとする場合に発生する可能性があります。

このような状況でデッドロックを回避するには、排他的ロックを必要とするステートメント (UPDATE/MERGE/DELETE/DDL ステートメント) を別のトランザクションに分割して、INSERT/COPY ステートメントを同時に進行させ、その後排他的ロックを必要とするステートメントを実行するようにします。または、同じテーブルで INSERT/COPY ステートメントと MERGE/UPDATE/MERGE ステートメントを使用するトランザクションの場合、アプリケーションに再試行ロジックを含めて、潜在的なデッドロックを回避できます。

# 同時書き込みの例
<a name="r_Serializable_isolation_example"></a>

次の疑似コードの例は、同時に実行されたときに、トランザクションが進行または待機する仕組みを示しています。

## 直列化可能な分離を使用した同時書き込みの例
<a name="r_Serializable_isolation_example-serializable"></a>

### 直列化可能な分離を使用した同じテーブルへの同時 COPY オペレーション
<a name="r_Serializable_isolation_example-concurrent-copy-operations-into-the-same-table"></a>

トランザクション 1 は LISTING テーブルに行をコピーします: 

```
begin;
copy listing from ...;
end;
```

トランザクション 2 は別のセッションで同時に開始され、さらに多くの行を LISTING テーブルにコピーしようとします。トランザクション 2 は、トランザクション 1 が LISTING テーブルの書き込みロックを解除するまで待機する必要があります。その後、続行できます。

```
begin;
[waits]
copy listing from ;
end;
```

片方または両方のトランザクションに COPY コマンドの代わりに INSERT コマンドが含まれる場合、同じ動作が起こることがあります。

### 直列化可能な分離を使用した同じテーブルからの同時 DELETE オペレーション
<a name="r_Serializable_isolation_example-concurrent-delete-operations-from-the-same-table"></a>

トランザクション 1 がテーブルから行を削除します: 

```
begin;
delete from listing where ...;
end;
```

トランザクション 2 が同時に開始され、同じテーブルから行を削除しようとします。行の削除を試行する前にトランザクション 1 の完了を待つため、トランザクション 2 は成功します。

```
begin
[waits]
delete from listing where ;
end;
```

片方または両方のトランザクションに DELETE コマンドの代わりに同じテーブルへの UPDATE コマンドが含まれる場合、同じ動作が起こることがあります。

### 直列化可能な分離を使用した読み取りオペレーションと書き込みオペレーションがミックスされた同時トランザクション
<a name="r_Serializable_isolation_example-concurrent-transactions"></a>

この例では、まずトランザクション 1 は USERS テーブルから行を削除し、テーブルを再ロードします。次に COUNT(\$1) クエリを実行し、ANALYZE を実行してからコミットします。

```
begin;
delete one row from USERS table;
copy ;
select count(*) from users;
analyze ;
end;
```

その間、トランザクション 2 が開始します。このトランザクションは USERS テーブルへの追加行のコピー、テーブルの分析、最初のトランザクションと同じ COUNT(\$1) クエリの実行を試行します。

```
begin;
[waits]
copy users from ...;
select count(*) from users;
analyze;
end;
```

2 つ目のトランザクションは最初のトランザクションの完了を待つため、成功します。その COUNT クエリはそれが完了したロードに基づいてカウントを返します。

## スナップショット分離を使用した同時書き込みの例
<a name="r_Serializable_isolation_example-snapshot"></a>

### スナップショット分離を使用した同じテーブルへの同時 COPY オペレーション
<a name="r_Serializable_isolation_example-concurrent-copy-operations-into-the-same-table-snapshot"></a>

トランザクション 1 は LISTING テーブルに行をコピーします:

```
begin;
copy listing from ...;
end;
```

トランザクション 2 は別のセッションで同時に開始され、さらに多くの行を LISTING テーブルにコピーしようとします。トランザクション 2 は、いずれかのトランザクションがターゲットテーブル `listing` にデータを書き込む必要があるまで同時に進行できます。その時点では、トランザクションは順番に実行されます。

```
begin; 
//When the COPY statement from T1 needs to write data to the table, the COPY statement from T2 waits.
copy listing from ...; 
end;
```

片方または両方のトランザクションに COPY コマンドの代わりに INSERT コマンドが含まれる場合、同じ動作が起こることがあります。

### スナップショット分離を使用した同じテーブルからの同時 DELETE オペレーション
<a name="r_Serializable_isolation_example-concurrent-delete-operations-from-the-same-table-snapshot"></a>

スナップショット分離を使用した同じテーブルからの同時 DELETE または UPDATE オペレーションは、直列化可能な分離を使用したオペレーションと同じ方法で実行されます。

### スナップショット分離を使用した読み取りオペレーションと書き込みオペレーションがミックスされた同時トランザクション
<a name="r_Serializable_isolation_example-concurrent-transactions-snapshot"></a>

スナップショット分離を使用したオペレーションがミックスされた同時トランザクションは、直列化可能な分離を使用したオペレーションがミックスされたトランザクションと同じ方法で実行されます。

# 直列化可能な分離エラーのトラブルシューティング
<a name="c_serial_isolation-serializable-isolation-troubleshooting"></a>

## エラー: 1023 詳細: Redshift テーブルで直列化可能分離に関する違反が発生しました
<a name="c_serial_isolation-serialization-isolation-1023"></a>

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](r_LOCK.md) コマンドでは、直列化可能な分離エラーを引き起こす可能性のあるオペレーションをブロックします。LOCK コマンドを使用するときは、次の点を確認してください。
  + トランザクション内の読み取り専用 SELECT ステートメントの影響を受けるテーブルなど、トランザクションの影響を受けるすべてのテーブルをロックします。
  + オペレーションが実行される順序に関係なく、テーブルを同じ順序でロックします。
  + オペレーションを実行する前に、トランザクションの開始時にすべてのテーブルをロックします。
+ 同時実行トランザクションにはスナップショット分離を使用します。

  スナップショット分離で ALTER DATABASE コマンドを使用します。ALTER DATABASE の SNAPSHOT パラメータの詳細については、「[パラメータ](r_ALTER_DATABASE.md#r_ALTER_DATABASE-parameters)」を参照してください。

## エラー: 1018 詳細: リレーションが存在しません
<a name="c_serial_isolation-serialization-isolation-1018"></a>

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