

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# Amazon QLDB 同時実行モデル
<a name="concurrency"></a>

**重要**  
サポート終了通知: 既存のお客様は、07/31/2025 のサポート終了まで Amazon QLDB を使用できます。詳細については、[「Amazon QLDB 台帳を Amazon Aurora PostgreSQL に移行する](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)」を参照してください。

Amazon QLDB は、高性能なオンライントランザクション処理 (OLTP) ワークロードのニーズに対応することを目的としています。QLDB は、SQL のようなクエリ機能をサポートし、完全な ACID トランザクションを提供します。加えて、QLDB のデータ項目は、柔軟なスキーマや直感的なデータモデリングを提供するドキュメントでもあります。ジャーナルを中核としているため、QLDB を使用して、データへのあらゆる変更の完全かつ検証可能な履歴にアクセスしたり、必要に応じて一貫したトランザクションを他のデータサービスにストリーミングしたりすることができます。

**Topics**
+ [

## オプティミスティック同時実行制御
](#concurrency.occ)
+ [

## インデックスを使用してテーブル全体のスキャンを回避する
](#concurrency.indexes)
+ [

## 挿入の OCC 競合
](#concurrency.inserts)
+ [

## トランザクションをべき等にする
](#concurrency.idempotent)
+ [

## 秘匿化 OCC コンフリクト
](#concurrency.redaction)
+ [

## 同時セッションの管理
](#concurrency.sessions)

## オプティミスティック同時実行制御
<a name="concurrency.occ"></a>

QLDB では、オプティミスティック同時実行制御 (OCC) を使用して同時実行制御が実行されます。**OCC は、複数のトランザクションが干渉し合うことなく頻繁に完了できるという原則に基づいています。

OCC を使用すると、QLDB のトランザクションはデータベースリソースのロックを取得せず、完全な直列化可能分離で動作します。QLDB では、同時トランザクションが連続して実行されるため、トランザクションが連続して開始された場合と同じ効果を発揮します。

コミットする前に、各トランザクションは検証チェックを実行し、他のコミットされたトランザクションがアクセスしているデータを変更していないことを確認します。このチェックによって競合する変更、またはデータの変更の状態が明らかになった場合、トランザクションのコミットは拒否されます。ただし、トランザクションは再開できます。

トランザクションが QLDB にデータを書き込む場合、OCC モデルの検証チェックは QLDB 自身が実行します。OCC の検証フェーズで障害が発生したためにトランザクションをジャーナルに書き込めない場合、QLDB は、`OccConflictException` をアプリケーションレイヤーに返します。トランザクションの再責任はアプリケーションソフトウェアが負います。アプリケーションは、拒否されたトランザクションを中止し、最初からトランザクション全体を再試行する必要があります。

QLDB ドライバーが OCC の競合およびその他の一時的な例外を処理して再試行する方法については、「[Amazon QLDB でドライバーが使用する再試行ポリシーを理解する](driver-retry-policy.md)」を参照してください。

## インデックスを使用してテーブル全体のスキャンを回避する
<a name="concurrency.indexes"></a>

QLDB では、すべての PartiQL ステートメント (すべての `SELECT` クエリを含む) はトランザクションで処理され、[トランザクションタイムアウト制限](limits.md#limits.fixed)の対象になります。

ベストプラクティスとして、インデックス付きフィールドまたはドキュメント ID でフィルタリングする `WHERE` 述語句を使用してステートメントを実行することをお勧めします。QLDB では、ドキュメントを効率的に検索するために、インデックス付きフィールドに等価演算子 (`WHERE indexedField = 123` や `WHERE indexedField IN (456, 789)` など) が必要です。**

このインデックス付きルックアップがない場合、QLDB はドキュメントを読み取るときに*フルテーブルスキャン*を実行する必要があります。これにより、クエリのレイテンシーとトランザクションのタイムアウトが発生する可能性が生じ、競合するトランザクションとの OCC の競合の可能性も高くなります。

たとえば、`VIN` フィールドにのみインデックスがある `Vehicle` という名前のテーブルについて考えます。これには以下のデータが含まれています。


****  

| VIN | Make | モデル | 色 | 
| --- | --- | --- | --- | 
| "1N4AL11D75C109151" | "Audi" | "A5" | "Silver" | 
| "KM8SRDHF6EU074761" | "Tesla" | "Model S" | "Blue" | 
| "3HGGK5G53FM761765" | "Ducati" | "Monster 1200" | "Yellow" | 
| "1HVBBAANXWH544237" | "Ford" | "F 150" | "Black" | 
| "1C4RJFAG0FC625797" | "Mercedes" | "CLK 350" | "White" | 

2 人の同時ユーザー (Alice と Bob) が、台帳内の同じテーブルを使用しています。彼らは、次のように 2 つの異なるドキュメントを更新します。

Alice:

```
UPDATE Vehicle AS v
SET v.Color = 'Blue'
WHERE v.VIN = '1N4AL11D75C109151'
```

Bob:

```
UPDATE Vehicle AS v
SET v.Color = 'Red'
WHERE v.Make = 'Tesla' AND v.Model = 'Model S'
```

Alice と Bob が同時にトランザクションを開始するとします。Alice の `UPDATE` ステートメントは `VIN` フィールドに対してインデックス付きのルックアップを行うため、該当する 1 つのドキュメントのみを読み取る必要があります。Alice は終了し、最初にトランザクションを正常にコミットします。

Bob のステートメントは、インデックス付けされていないフィールドをフィルタリングするため、テーブルスキャンを実行し、`OccConflictException` が発生します。これは、Alice のコミットされたトランザクションが Bob のステートメントがアクセスしているデータを変更したためです。これには、Bob が更新中のドキュメントだけでなく、テーブル内のすべてのドキュメントが含まれます。

## 挿入の OCC 競合
<a name="concurrency.inserts"></a>

OCC の競合には、以前に存在していたドキュメントだけでなく、新しく挿入されたドキュメントも含まれる場合があります。次の図では、2 人の同時ユーザー (Alice と Bob) が台帳内の同じテーブルを操作しているとします。彼らは両方とも、述語値がまだ存在しないという条件の下でのみ新しいドキュメントを挿入したいと考えています。

![\[2 人の同時ユーザー間の競合例外の例を示す Amazon QLDB オプティミスティック同時実行制御 (OCC) の図。\]](http://docs.aws.amazon.com/ja_jp/qldb/latest/developerguide/images/occ-example.png)


この例では、Alice と Bob の両方が 1 つのトランザクション内で次の `SELECT` および `INSERT` ステートメントを実行します。アプリケーションは、`INSERT` ステートメントが結果を返さない場合にのみ、`SELECT` ステートメントを実行します。

```
SELECT * FROM Vehicle v WHERE v.VIN = 'ABCDE12345EXAMPLE'
```

```
INSERT INTO Vehicle VALUE
{
    'VIN' : 'ABCDE12345EXAMPLE',
    'Type' : 'Wagon',
    'Year' : 2019,
    'Make' : 'Subaru',
    'Model' : 'Outback',
    'Color' : 'Gray'
}
```

Alice と Bob が同時にトランザクションを開始するとします。どちらの `SELECT` クエリも、`ABCDE12345EXAMPLE` の　`VIN` が付いた既存のドキュメントを返しません 。したがって、彼らのアプリケーションは `INSERT` ステートメントを続行します。

Alice は終了し、最初にトランザクションを正常にコミットします。その後、Bob はトランザクションをコミットしようとしますが、それを QLDB が拒否し、`OccConflictException` がスローされます。これは、Alice のコミットされたトランザクションが Bob の `SELECT` クエリの結果セットを変更し、OCC が Bob のトランザクションをコミットする前にこの競合を検出するためです。

このトランザクション例を[べき等](#concurrency.idempotent)にするには `SELECT` クエリが必要です。Bob は、最初からトランザクション全体を再試行することができます。しかし、彼の次の `SELECT` クエリは、Alice が挿入したドキュメントを返すので、Bob のアプリケーションは `INSERT` を実行しません。

## トランザクションをべき等にする
<a name="concurrency.idempotent"></a>

[前のセクション](#concurrency.inserts)の insert トランザクションも、*べき等*トランザクションの例です。つまり、同じトランザクションを複数回実行すると、同じ結果が得られます。Bob が、特定の `VIN` が既に存在しているかどうかを最初に確認せずに `INSERT` を実行すると、テーブルには、重複した `VIN` 値を持つドキュメントが作成されることがあります。

OCC の競合に加えて、他の再試行シナリオを考えてみます。例えば、QLDB はサーバー側でトランザクションを正常にコミットできるが、レスポンスを待機中にクライアントはタイムアウトする可能性があります。ベストプラクティスとして、同時実行または再試行の場合に予期しない結果を避けるために、書き込みトランザクションをべき等にしてください。

## 秘匿化 OCC コンフリクト
<a name="concurrency.redaction"></a>

QLDB は、同じジャーナルブロックの[リビジョンの同時秘匿化](working.redaction.md)を防ぎます。2 人の同時接続ユーザー (Alice と Bob) が、台帳の同じブロックにコミットされている 2 つの異なるドキュメントのリビジョンを秘匿化したいという例を考えてみましょう。まず、Alice は次のように `REDACT_REVISION` ストアドプロシージャを実行して、1 つのリビジョンの秘匿化をリクエストします。

```
EXEC REDACT_REVISION `{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`, '5PLf9SXwndd63lPaSIa0O6', 'ADR2Ll1fGsU4Jr4EqTdnQF'
```

次に、Alice のリクエストがまだ処理中である間に、Bob は次のように別のリビジョンの秘匿化をリクエストします。

```
EXEC REDACT_REVISION `{strandId:"JdxjkR9bSYB5jMHWcI464T", sequenceNo:17}`, '8F0TPCmdNQ6JTRpiLj2TmW', '05K8zpGYWynDlEOK5afDRc'
```

QLDB は、2 つの異なるドキュメントのリビジョンを秘匿化しようとしているにもかかわらず、Bob のリクエストを `OccConflictException` で拒否します。これは、Alice が秘匿化中のリビジョンと Bob のリビジョンが同じブロックにあるからです。Alice のリクエストの処理が完了すると、Bob は秘匿化リクエストを再試行できます。

同様に、2 つの同時トランザクションが同じリビジョンを秘匿化しようとした場合、処理できるリクエストは 1 つだけです。もう 1 つのリクエストは、秘匿化が完了するまで OCC コンフリクト例外で失敗します。その後、同じリビジョンを秘匿化しようとすると、そのリビジョンが既に秘匿化されていることを示すエラーが発生します。

## 同時セッションの管理
<a name="concurrency.sessions"></a>

リレーショナルデータベース管理システム (RDBMS) を使用した経験のある方は同時接続の制限の知識をお持ちでしょう。QLDB の概念は、従来の RDBMS 接続とは異なります。HTTP のリクエストメッセージとレスポンスメッセージを使用してトランザクションを実行するからです。

QLDB では、同様の概念をアクティブセッション**と呼びます。セッションは概念的にはユーザーログインに似ており、これによって、台帳に対するデータトランザクションリクエストの情報が管理されます。アクティブセッションとは、トランザクションがアクティブに実行されているセッションを意味します。トランザクションが終了したばかりで、サービスが別のトランザクションがすぐに開始されることを期待している状態もセッションと見なされることがあります。QLDB ではセッションごとに 1 つのトランザクションがアクティブに実行されます。

台帳ごとの同時アクティブセッションの制限数は、「[Amazon QLDB でのクォータと制限](limits.md#limits.fixed)」に定義されています。この制限に達すると、トランザクションの開始を試行するセッションはエラー (`LimitExceededException`) になります。

セッションのライフサイクルと、データトランザクションの実行時に QLDB ドライバーがセッションを処理する方法の詳細については、「[ドライバーによるセッション管理](driver-session-management.md#driver-session-mgmt.lifecycle)」を参照してください。QLDB ドライバーを使用してアプリケーション内のセッションプールを設定するベストプラクティスについては、「Amazon QLDB の推奨事項**」の「[QldbDriver オブジェクトの設定](driver.best-practices.md#driver.best-practices.configuring)」を参照してください。