背景・目的
DynamoDB においてトランザクションの導入を検討する機会がありました。本記事では、ドキュメントをもとにトランザクション機構の仕組みや基本的な考え方を整理し、理解を深めたいと思います。
まとめ
下記に特徴をまとめます。
| 特徴 | 説明 |
|---|---|
| DynamoDB Transactionの概要 | ・複数の項目、複数のテーブルをまとめて安全に処理できる ・ロールバックできる ・同時実行制御、不整合も防ぐ ・トランザクションのRead/Write APIを使用し、All or Nothing オペレーションを実現できる |
| トランザクション 書き込みAPI | ・Put ・Update ・Delete ・ConditionCheck |
| コスト | ・トランザクションを有効にするにあたり、追加コストはかからない ・トランザクション内で実行したRead/Write容量(RCU/WCU)に基づいて行われる ・トランザクションでは、1回の操作で2回の処理が行われる ・トランザクション準備用 ・コミット用 |
| Amazon DynamoDB Transactionsの仕組み | オールオアナッシングオペレーションのTransactWriteItems、TransactGetItemsオペレーションとして送信可能 |
| TransactWriteItems API | ・最大100の書き込みアクションを1つのオールオアナッシングオペレーションにグループ化する ・冪等な書き込みオペレーション ・同一AWSアカウント、同一リージョン内の1つ以上のDDBテーブルにある最大100個の異なる項目をターゲットにすることが可能 ・トランザクション内のアイテムの合計サイズは4MB以下 ・アトミックに実行される ・ストリーム、GSI、バックアップはトランザクション対象外のため、コンシューマーで工夫が必要 |
| TransactWriteItems APIのアクション | ・Put ・Update ・Delete ・ConditionCheck |
| トランザクションの対象 | テーブルのみ、GSI・ストリーム等は対象外 |
| 冪等性 | クライアントトークンを使用することで冪等性を保証できる しかし、有効期限は10分間 |
| TransactGetItems API | 同時に最大100個のGetをまとめて実行可能 ・対象は同一AWS・リージョン内のDDBテーブル(複数あり) ・取得サイズは最大4MBまで |
| DynamoDB トランザクションの分離レベル | SERIALIZABLE |
| DynamoDB アクセラレーター (DAX) でのトランザクション API の使用 | TransactWriteItems と TransactGetItems は、DynamoDB と同じ分離レベルで対応されている |
| トランザクションの容量管理 | ・DynamoDB でトランザクションを有効化すること自体には追加コストはかからない ・料金はトランザクションに含まれる読み込み/書き込み分のみ発生する ・トランザクション内の各項目は、下記の2回分のオペレーションが行われる ・ 準備用の読み込み/書き込み ・ コミット用の読み込み/書き込み ・Amazon CloudWatch のメトリクスにも表示される |
| トランザクションのベストプラクティス | -- |
| 容量計画 | テーブルは自動スケーリングを有効化するか、トランザクション内の各項目で発生する2回のR/Wに耐える十分なスループットをプロビジョニングする |
| 冪等性 | AWS公式SDKを使わない場合は、TransactWriteItems に ClientRequestToken を含めて冪等化する |
| 過度なグルーピング回避 | ・不要に多くの操作を1トランザクションにまとめない ・正確性を損なわない範囲で複数トランザクションに分割すると、スループットと成功率が向上 |
| 競合最小化のデータモデリング | 同一項目を同時更新するトランザクションでキャンセルが起き得るため、頻繁に複数項目へまたがって更新する属性群は1項目に集約してトランザクションのスコープを縮小する |
| 大量取り込みの非推奨 | ・大量データ取り込みにトランザクションを使わない ・バルク書き込みは BatchWriteItem を推奨 |
| グローバルテーブルでのトランザクション API の使用 | ・トランザクションのACID保証(不可分性・一貫性・分離性・耐久性)は、書き込みAPIを最初に呼び出したAWSリージョン内のみで有効 ・グローバルテーブルのリージョン間ではトランザクションはサポートされない |
オペレーションごとの振る舞い
| 先行 \ 後発 | Put/Update/Delete | GetItem | BatchGetItem | BatchWriteItem | Query/Scan | TransactWriteItems | TransactGetItems |
|---|---|---|---|---|---|---|---|
| Put/Update/Delete | - | Serializable | Committed Read | Per-item Serializable, unit Not Serializable | Committed Read | Serializable | Committed Read |
| GetItem | Serializable | - | Committed Read | Committed Read | Committed Read | Serializable | Committed Read |
| BatchGetItem | Committed Read | Committed Read | - | Committed Read | Committed Read | Committed Read | Committed Read |
| BatchWriteItem | Per-item Serializable, unit Not Serializable | Committed Read | Committed Read | - | Committed Read | Per-item Serializable, unit Not Serializable | Committed Read |
| Query/Scan | Committed Read | Committed Read | Committed Read | Committed Read | - | Committed Read | Committed Read |
| TransactWriteItems | Serializable | Serializable | Committed Read | Per-item Serializable, unit Not Serializable | Committed Read | Serializable | Serializable |
| TransactGetItems | Committed Read | Committed Read | Committed Read | Committed Read | Committed Read | Serializable | - |
- Serializable
- 先行する処理がまだ完了していなければ、後発は即キャンセル
- Committed Read
- 未コミットは返さないけど、時点の揃ったデータは保証しない
- Not Serializable
- 同じ項目を触っても全体順序は保証しない、部分的に混ざる可能性あり
- Per-item Serializable, unit Not Serializable
- 個々の項目レベルでは Serializableだが、複数項目をまとめた全体単位では Serializable ではない
概要
DynamoDB トランザクションで複雑なワークフローを管理する
下記の内容を基に整理します。
Amazon DynamoDB Transactions は、テーブル内およびテーブル間の複数の項目を調整したり、変更しないといった、デベロッパーのエクスペリエンスを簡素化します。トランザクションによって DynamoDB に不可分性、一貫性、分離性、耐久性 (ACID) が実現されるため、アプリケーション内でのデータの精度を維持することができます。
- DynamodDB(以降、DDBという) Transactionsは下記ができると読み解きました
- 複数の項目、複数のテーブルをまとめて安全に処理できる
- ロールバックできる
- 同時実行制御、不整合も防ぐ
- アプリケーションは正しいデータを維持できる
DynamoDB トランザクション読み込み/書き込み API を使用し、1 つのオースオアナッシングオペレーションとして複数の項目の追加、更新、または削除が必要となる複雑なビジネスワークフローを管理できます。たとえば、ビデオゲームデベロッパーであれば、ゲーム内での項目交換やゲーム内購入の際に、プレイヤーのプロファイル更新の正確性を確保できます。
- DDB トランザクションのRead/Write APIを使用し、All or Nothing オペレーションを実現できる
トランザクション書き込み API を使用して、複数の Put、Update、Delete、ConditionCheck の各アクションをグループ化できます。その後、アクションを単一の TransactWriteItems オペレーションとして送信できます。このオペレーションはユニットとして成功または失敗します。同じことが複数の Get アクションにも当てはまります。この場合、1 つの TransactGetItems オペレーションとしてグループ化し、送信できます。
- トランザクション書き込みAPIを使用して、下記の各アクションをグループ化できる
- Put
- Update
- Delete
- ConditionCheck
- 上記のあと、アクションを単一のTransactWriteItemsオペレーションとして送信できる
- すべて成功するか、1つでも失敗したらロールバックされる
DynamoDB テーブルのトランザクションを有効にするために、追加コストはかかりません。料金の支払いは、トランザクションの一部である読み込みまたは書き込みに対してのみ行われます。DynamoDB は、トランザクション内の各項目の基になっている 2 つの読み込みまたは書き込みを実行します。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。これらの基になっている 2 つの読み込み/書き込みオペレーションは、Amazon CloudWatch メトリクスに表示されます。
- トランザクションを有効にするにあたり、追加コストはかからない
- トランザクション内で実行したRead/Write容量(RCU/WCU)に基づいて行われる
- トランザクションでは、1回の操作で2回の処理が行われる
- トランザクション準備用
- コミット用
Amazon DynamoDB Transactions: 仕組み
下記の内容を基に整理します。
Amazon DynamoDB Transactions を使用すれば、複数のアクションをまとめてグループ化し、1 つのオールオアナッシングの TransactWriteItems または TransactGetItems オペレーションとして送信できます。以下のセクションでは、API オペレーション、容量管理、ベストプラクティス、DynamoDB でのトランザクション操作の使用に関する他の詳細について説明します。
- オールオアナッシングオペレーションのTransactWriteItems、TransactGetItemsオペレーションとして送信可能
TransactWriteItems API
TransactWriteItems は、最大 100 の書き込みアクションを 1 つのオールオアナッシングオペレーションにグループ化する、同期的でべき等な書き込みオペレーションです。これらのアクションは、同じ AWS アカウントおよび同じリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内のアイテムの合計サイズは 4 MB を超えることはできません。すべて成功するかどれも成功しないかのどちらとなるように、アトミックに実行されます。
- 最大100の書き込みアクションを1つのオールオアナッシングオペレーションにグループ化する
- 冪等な書き込みオペレーション
- 同一AWSアカウント、同一リージョン内の1つ以上のDDBテーブルにある最大100個の異なる項目をターゲットにすることが可能
- トランザクション内のアイテムの合計サイズは4MB以下
- アトミックに実行される
同じトランザクション内の複数のオペレーションが同じ項目をターゲットとすることはできません。たとえば、同じトランザクション内で同じ項目に対して ConditionCheck を実行し、Update アクションも実行することはできません。
- 1 つのトランザクション内の複数の同じキー(パーティションキー+ソートキー)を持つアイテムに対して2回以上の操作ができない
以下のタイプのアクションをトランザクションに追加できます。
- Put — PutItem オペレーションを開始し、条件付きで、または条件をまったく指定せずに、新しい項目を作成するか、古い項目を新しい項目に置き換えます。
- Update — UpdateItem オペレーションを開始し、既存の項目の属性を編集するか、まだ存在しない場合は新しい項目をテーブルに追加します。条件付きまたは条件なしで既存の項目で属性を追加、削除、更新するには、このアクションを使用します。
- Delete — DeleteItem オペレーションを開始し、プライマリキーにより識別される 1 つの項目をテーブルで削除します。
- ConditionCheck — 項目が存在することを確認するか、項目の特定の属性の条件を確認します。
- Put
- 追加または上書き
- Update
- 属性の追加、更新、削除
- Delete
- 項目の削除
- ConditionCheck
- 項目の存在、属性条件の確認
DynamoDB でトランザクションが完了すると、変更のグローバルセカンダリインデックス (GSIs)、ストリーム、バックアップへの伝播が開始されます。この伝播は以下のとおり、徐々に行われます。同じトランザクションからのストリームレコードは異なるタイミングで表示されるため、他のトランザクションからのレコードとインターリーブする可能性があります。ストリームコンシューマーは、トランザクションの原子性や順番が保証されていると想定すべきではありません。
- トランザクションで更新されたデータは、基のテーブルがコミットされる。次に、GSI、ストリーム、バックアップへの伝播が始まる
- 伝播は、非同期で行われる
- トランザクション終了直後は、GSIはストリーム側にまだ反映されていない可能性がある
- ストリームのコンシューマーでは順不同や複数の場合、混ざる可能性あり
トランザクションで変更された項目のアトミックスナップショットを確保するには、TransactGetItems オペレーションを使用して、関連するすべての項目をまとめて読み取ります。このオペレーションを使用すると、データの一貫したビューが得られ、完了済みのトランザクションからのすべての変更が表示されるか、まったく表示されません。
- TransactGetItemsを使用して、更新後の内容が読み取れる
- オールオアナッシングな状態
- ※ストリームやGSIは対象外
伝播は即時に実行されるわけでないため、伝播中にテーブルがバックアップから復元された場合や(RestoreTableFromBackup)、特定時点のエクスポートが行われた場合 (ExportTableToPointInTime)、含まれるのが最近のトランザクション中に行われた変更の一部のみとなる場合もあります。
- GSI、バックアップ、エクスポートへの反映は即時ではない
- 反映途中でエクスポート等を行うと、最近のトランザクションの変更が一部しか含まれない可能性がある
冪等性
TransactWriteItems 呼び出しを行ってリクエストが冪等であることを確認する場合、オプションでクライアントトークンを含めることができます。トランザクションを冪等にすると、接続のタイムアウトや他の接続問題に伴って同じオペレーションが複数回送信された場合に、アプリケーションエラーを防ぐことができます。
- オプションのクライアントトークンを指定すると、同じリクエストを複数回送信しても冪等性が保たれ、接続障害などによる重複実行でのアプリケーションエラーを防げる
元の TransactWriteItems 呼び出しが成功した場合、同じクライアントトークンを持つ以降の TransactWriteItems 呼び出しは変更なしで正常に返ります。ReturnConsumedCapacity パラメータが設定されている場合、最初の TransactWriteItems 呼び出しは変更時に消費された書き込みキャパシティーユニットの数を返します。同じクライアントトークンを持つ以降の TransactWriteItems 呼び出しは、項目の読み取り時に消費された読み取りキャパシティユニットの数を返します。
- 同じクライアントトークンで TransactWriteItems を再実行すると、最初の実行が成功していれば変更せずに成功を返す
- 最初の書き込みでは、WCUが消費され(おそらく仕組み上RCUも)、二回目の同一クライアントトークンで書き込みでは、内部で読み込みを行うので、RCUが消費される
冪等性について重要な点
- クライアントトークンは、それを使用するリクエストが完了してから 10 分間有効です。10 分後、同じクライアントトークンを使用するリクエストは新しいリクエストとして扱われます。10 分が経過してから、同じリクエストに同じクライアントトークンを再利用しないでください。
- クライアントトークンは、最初のリクエスト完了から10分間有効
- 10分経過後に同一クライアントトークンを利用した場合、新しいリクエストとして扱われる(重複したり、新しい値で上書きされる可能性がある)。冪等じゃなくなる
- 10 分間のべき等性期間内に同じクライアントトークンを使用してリクエストを繰り返すとき、他の一部のリクエストパラメータを変更した場合、DynamoDB は IdempotentParameterMismatch 例外を返します。
- 10分以内に同一クライアントトークンを利用し、異なるパラメータの場合、例外が返される
書き込みのエラー処理
以下の条件下では、書き込みトランザクションが成功しません。
- いずれかの条件式の条件が満たされていない場合。
- 同じ TransactWriteItems オペレーション内の複数のアクションが同じ項目をターゲットとしているために、トランザクション検証エラーが発生した場合。
- TransactWriteItems リクエストが、TransactWriteItems リクエスト内の 1 つ以上の項目に対する継続中の TransactWriteItems オペレーションと競合する場合。この場合、リクエストは TransactionCanceledException で失敗します。
- トランザクションを完了するプロビジョンドキャパシティーが足りない場合。
- 項目サイズが大きくなりすぎる (400 KB 超)、ローカルセカンダリインデックス (LSI) が大きくなりすぎる、またはトランザクションにより変更が加えられたために同様の検証エラーが発生した場合。
- 無効なデータ形式などのユーザーエラーがある場合。
- トランザクションが成功しない場合
- 条件式(ConditionCheckや、ConditionExpression)がfalse
- 同一 TransactWriteItems 内で複数アクションが同じ項目を対象にする
- 複数トランザクションが発生している際、ロック済みならエラーが返される(待たない)
- キャパシティ不足
- 検証エラー(400KB 超)、LSIが大きすぎる
- 無効なデータ形式
TransactGetItems API
TransactGetItems は、最大 100 個の Get アクションをまとめてグループ化する同期読み取りオペレーションです。これらのアクションは、同じ AWS アカウントおよびリージョン内の 1 つ以上の DynamoDB テーブルにある最大 100 個の異なる項目をターゲットにすることができます。トランザクション内の項目の合計サイズは 4 MB を超えることはできません。
- 同時に最大100個のGetをまとめて実行可能
- 対象は同一AWS・リージョン内のDDBテーブル(複数あり)
- 取得サイズは最大4MBまで
Get アクションは、すべて成功するかすべて失敗するかのどちらとなるように、アトミックに実行されます。
- Get — GetItem オペレーションを開始し、指定されたプライマリキーを持つ項目の属性のセットを取得します。一致する項目が見つからない場合、Get はデータを返しません。
- TransactGetItemsのすべてのGetはアトミックに実行される
- 指定されたプライマリキーの項目を取得し、存在しない場合はデータを返さない
読み込みのエラー処理
以下の条件下では、読み取りトランザクションが成功しません。
- TransactGetItems リクエストが、TransactWriteItems リクエスト内の 1 つ以上の項目に対する継続中の TransactGetItems オペレーションと競合する場合。この場合、リクエストは TransactionCanceledException で失敗します。
- トランザクションを完了するプロビジョンドキャパシティーが足りない場合。
- 無効なデータ形式などのユーザーエラーがある場合。
TransactGetItems が失敗する主な原因は下記の通り
- 対象項目が進行中のトランザクション(TransactWriteItems または別の TransactGetItems)と競合し、TransactionCanceledException が発生
- プロビジョンドキャパシティー不足
- 無効なデータ形式などのユーザーエラー
DynamoDB トランザクションの分離レベル
トランザクションオペレーション (TransactWriteItems または TransactGetItems) と他のオペレーションの分離レベルは、次のとおりです。
SERIALIZABLE
直列化可能分離レベルでは、複数の同時オペレーションの結果は、前のオペレーションが完了するまでオペレーションが開始されない場合と同じになります。
- 複数の同時オペレーションの結果は、前のオペレーションが完了するまでオペレーションが開始されない場合と同様
以下のタイプのオペレーション間には、直列化可能分離があります。
- トランザクションオペレーションと標準書き込みオペレーション (PutItem、UpdateItem、または DeleteItem) の間。
- トランザクションオペレーションと標準読み取りオペレーション (GetItem) の間。
- TransactWriteItems オペレーションと TransactGetItems オペレーションの間。
- SERIALIZABLE
- Transact系オペレーションと更新系(Transactではない)
- Transact系オペレーションと参照系(Transactではない)
- TransactWriteItems間
トランザクションオペレーション間と BatchWriteItem オペレーション内の個々の標準書き込み間には直列化可能分離がありますが、トランザクションとユニットとしての BatchWriteItem オペレーションの間には直列化可能分離はありません。
- BatchWriteItemは、複数の非トランザクション書き込みをまとめたバルク処理のため、アトミックや一貫性の保証はない
同様に、トランザクションオペレーションと GetItems オペレーションの個別の BatchGetItem 間の分離レベルは直列化可能です。ただし、トランザクションとユニットとしての BatchGetItem オペレーション間の分離レベルはコミット済み読み取りです。
- トランザクションオペレーションと、BatchGetItem 内の個別の GetItem の間の分離レベルはSERIALIZABLEである
- トランザクションと BatchGetItem 全体(ユニット)との間の分離レベルはコミット済み読み取り(Committed Read)である
単一の GetItem リクエストは、TransactWriteItems リクエストの前または後に行う 2 つの方法のいずれかで、TransactWriteItems リクエストに関してシリアル化することができます。同時実行 TransactWriteItems リクエストのキーに対する複数の GetItem リクエストは、任意の順序で実行できるため、結果は読み込みがコミットされます。
たとえば、項目 A と項目 B の GetItem リクエストが、項目 A と項目 B の両方を変更する TransactWriteItems リクエストと同時に実行される場合、次の 4 つの可能性があります。
- 両方の GetItem リクエストは、TransactWriteItems リクエストの前に実行されます。
- 両方の GetItem リクエストは、TransactWriteItems リクエストの後に実行されます。
- 項目 A の GetItem リクエストは、TransactWriteItems リクエストの前に実行されます。項目 B の場合、GetItem は TransactWriteItems の後に実行されます。
- 項目 B の GetItem リクエストは、TransactWriteItems リクエストの前に実行されます。項目 A の場合、GetItem は TransactWriteItems の後に実行されます。
- TransactWriteItems(トランザクション書き込み)とぶつかったら、「書き込み前の状態を読む」か「書き込み後の状態を読む」かのどちらかに統一される
- 中途半端な状態は見ない
複数の GetItem リクエストにシリアル化可能な分離レベルが望ましい場合は、TransactGetItems を使用してください。
処理中に同じトランザクション書き込みリクエストの一部であった複数の項目に対して非トランザクション読み取りが行われた場合、一部の項目の新しい状態と他の項目の古い状態を読み取ることができる可能性があります。トランザクション書き込みリクエストに含まれていたすべての項目の新しい状態を読み取ることができるのは、トランザクションが完了したことを示すトランザクション書き込みの応答が成功した場合のみです。
トランザクションが正常に完了し、応答が受信されると、DynamoDB の結果整合性モデルにより、結果整合性のある読み込みオペレーションが短期間、古い状態を返す可能性があります。トランザクションの直後に最新のデータを確実に読み取るには、ConsistentRead を true に設定して、強力な整合性のある読み込みを使用する必要があります。
- 複数の GetItem でシリアル化可能な分離レベルが欲しい場合は、TransactGetItems を使うこと
- 同じトランザクション書き込みに含まれていた複数項目を非トランザクション読み取った場合、下記の可能性がある
- 一部は新しい状態
- 一部は古い状態
- トランザクション書き込みの応答が成功として返った場合のみ、すべての項目が新しい状態になっていることが保証される
- 成功直後でも DynamoDB の結果整合性モデルにより、結果整合読み込み(デフォルト)では古い状態が返る可能性がある
- 最新の状態を必ず読みたければ、ConsistentRead = true にして強い整合性の読み込みを行う
コミット済み読み取り
コミット済み読み取り分離により、読み取りオペレーションは常に項目のコミット済み値を返します。つまり、読み取りによって、最終的に成功しなかったトランザクション書き込みの状態を表すビューが項目に表示されることはありません。コミット済み読み取り分離では、読み取りオペレーションの直後に項目の変更が防止されません。
- コミット済み読み取り分離では、読み取りは必ずコミット済みの値を返す
- 読み取り結果が、後でロールバックするトランザクション書き込みの途中状態になることはない
分離レベルは、トランザクションオペレーションと、複数の標準読み取り (BatchGetItem、Query、または Scan) が関係する読み取りオペレーションの間ではコミット済み読み取りです。トランザクション書き込みにより BatchGetItem、Query、または Scan オペレーションの途中で項目が更新された場合、その後の読み取りオペレーションの部分では、新しくコミットされた値 (ConsistentRead) を使用) か、場合によってはそれ以前のコミット済み値 (結果整合性のある読み込み) を返します。
- トランザクションオペレーションと、複数の標準読み取り(BatchGetItem、Query、Scan)の間の分離レベルはコミット済み読み取りである
- トランザクション書き込みが、これらの複数読み取り処理の途中で対象アイテムを更新した場合、に下記の状態がある
- その後の部分では新しいコミット済み値(ConsistentRead を使用している場合)
- または古いコミット済み値(結果整合性のある読み取りの場合)を返す可能性がある
オペレーションの概要
以下の表は、トランザクションオペレーション (TransactWriteItems または TransactGetItems) と他のオペレーションの間の分離レベルをまとめたものです。
| オペレーション | 分離レベル |
|---|---|
| DeleteItem | 直列化可能 |
| PutItem | 直列化可能 |
| UpdateItem | 直列化可能 |
| GetItem | 直列化可能 |
| BatchGetItem | コミット済み読み取り* |
| BatchWriteItem | 直列化不可* |
| Query | コミット済み読み取り |
| Scan | コミット済み読み取り |
| 他のトランザクションオペレーション | 直列化可能 |
※アスタリスク (*) が付いたレベルは、ユニットとしてオペレーションに適用されます。ただし、これらのオペレーション内の個々のアクションの分離レベルは直列化可能です。
DynamoDB でのトランザクション競合の処理
トランザクション競合は、トランザクション内の項目に対する項目レベルの同時リクエスト中に発生する場合があります。トランザクション競合は、次のシナリオで発生する場合があります。
- 項目に対する PutItem、UpdateItem、または DeleteItem リクエストが、同じ項目を含む継続中の TransactWriteItems リクエストと競合する。
- TransactWriteItems リクエスト内の項目が、継続中の別の TransactWriteItems リクエストの一部である。
- TransactGetItems リクエスト内の項目が、継続中の TransactWriteItems、BatchWriteItem、PutItem、UpdateItem、または DeleteItem リクエストの一部である。
- トランザクション競合は、トランザクション内の項目に対して同時リクエストが発生した場合に起こる。主なシナリオは以下の通り
- 標準書き込み(PutItem / UpdateItem / DeleteItem)が、同じ項目を含む進行中の TransactWriteItems と競合した場合
- TransactWriteItems 内の項目が、進行中の別の TransactWriteItems に含まれている場合
- TransactGetItems 内の項目が、進行中の TransactWriteItems、BatchWriteItem、PutItem、UpdateItem、または DeleteItem に含まれている場合
DynamoDB アクセラレーター (DAX) でのトランザクション API の使用
TransactWriteItems と TransactGetItems が、どちらも DynamoDB と同じ分離レベルで DynamoDB アクセラレーター (DAX) でサポートされています。
TransactWriteItems は DAX を介して書き込みます。DAX は DynamoDB に TransactWriteItems コールを渡し、応答を返します。書き込み後にキャッシュにデータを追加するために、DAX は、TransactWriteItems オペレーション内の各項目に対してバックグラウンドで TransactGetItems をコールします。これにより、追加の読み込み容量単位が消費されます。(詳しくは、トランザクションの容量管理 を参照してください)。この機能により、アプリケーションロジックをシンプルに保ち、トランザクション処理と非トランザクション処理の両方に DAX を使用できます。
TransactGetItems コールは、項目がローカルにキャッシュされることなく DAX を通過します。これは、DAX の強い整合性のある読み込み API と同じです。
- TransactWriteItems と TransactGetItems は、DynamoDB と同じ分離レベルで DynamoDB Accelerator (DAX) に対応している
- TransactWriteItems
- DAX はリクエストを DynamoDB にそのまま渡し、応答を返す
- 書き込み後、キャッシュ更新のため TransactWriteItems 内の各項目に対してバックグラウンドで TransactGetItems を呼び出す
- 追加の読み込みキャパシティユニットが消費される
- TransactGetItems
- DAX を通過して直接 DynamoDB から取得される
トランザクションの容量管理
DynamoDB テーブルのトランザクションを有効にするために、追加コストはかかりません。料金の支払いは、トランザクションの一部である読み込みまたは書き込みに対してのみ行われます。DynamoDB は、トランザクション内の各項目の基になっている 2 つの読み込みまたは書き込みを実行します。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。基になっている 2 つの読み込み/書き込みオペレーションは、Amazon CloudWatch メトリクスに表示されます。
- DynamoDB でトランザクションを有効化すること自体には追加コストはかからない
- 料金はトランザクションに含まれる読み込み/書き込み分のみ発生する
- トランザクション内の各項目は、下記の2回分のオペレーションが行われる
- 準備用の読み込み/書き込み
- コミット用の読み込み/書き込み
- Amazon CloudWatch のメトリクスにも表示される
容量をテーブルにプロビジョニングするとき、トランザクション API により要求される追加の読み取りと書き込みを計画してください。たとえば、アプリケーションが 1 秒あたり 1 件のトランザクションを実行し、各トランザクションは 500 バイトの項目を 3 個テーブルに書き込むとします。各項目には、2 つの書き込みキャパシティーユニット (WCU) が必要です。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。したがって、テーブルには WCU を 6 個プロビジョニングする必要があります。
- プロビジョニングする際には、トランザクションAPIが必要とする追加のRead/Writeを考慮する
前の例で DynamoDB アクセラレーター (DAX) を使用していた場合、TransactWriteItems のコールで項目ごとに 2 つの読み込み容量単位 (RCU) も使用します。したがって、テーブルには追加の RCU を 6 個プロビジョニングする必要があります。
同様に、アプリケーションが 1 秒あたり 1 件の読み込みトランザクションを実行し、各トランザクションは 500 バイトの項目を 3 個テーブルで読み取る場合、読み込み容量単位 (RCU) を 6 個テーブルにプロビジョンする必要があります。各項目を読み取るには、2 つの RCU が必要です。1 つはトランザクションの準備用で、もう 1 つはトランザクションのコミット用です。
さらに、SDK のデフォルトの動作は、TransactionInProgressException 例外が発生した場合にトランザクションを再試行します。これらの再試行で消費される追加の読み込みキャパシティーユニット (RCU) を計画してください。同じことは、ClientRequestToken を使用して独自のコードでトランザクションを再試行しようとする場合に当てはまります。
- SDKはデフォルトで、TransactionInProgressException が起きたトランザクションを自動再試行する
- その再試行により、追加の読み取りRCUが消費されるため、あらかじめ見込んでおく必要がある
- ClientRequestTokenを用いて自前で再試行する場合も、同様に追加のRCU消費が発生する
トランザクションのベストプラクティス
DynamoDB トランザクションの使用時に推奨される以下の手法を検討してください。
- テーブルで自動スケーリングを有効にするか、トランザクションの項目ごとに 2 つの読み取りまたは書き込みオペレーションを実行するのに十分なスループット容量をプロビジョニングしたことを確認します。
- AWS によって提供された SDK を使用していない場合、TransactWriteItems 呼び出しを行うときに ClientRequestToken 属性を含め、リクエストがべき等となるようにします。
- 必要でない場合は、トランザクションにオペレーションをまとめてグループ化しないでください。たとえば、10 個のオペレーションを持つ 1 つのトランザクションを、アプリケーションの正確性を低下させずに複数のトランザクションに分割できる場合、トランザクションを分割することをお勧めします。トランザクションをシンプルにするとスループットが向上し、成功する可能性が高まります。
- 同じ項目を同時に更新する複数のトランザクションによって、トランザクションをキャンセルする競合が発生する可能性があります。そのような競合を最小限に抑えるため、データモデリングには以下の DynamoDB のベストプラクティスをお勧めします。
- 属性のセットが 1 つのトランザクションの一部として複数の項目間で頻繁に更新される場合、属性を 1 つの項目にグループ化し、トランザクションのスコープを減らすことを検討してください。
- データを大量に取り込むためにトランザクションを使用しないでください。一括書き込みには、BatchWriteItem の使用をお勧めします。
- 容量計画
- テーブルは自動スケーリングを有効化するか、トランザクション内の各項目で発生する2回のR/Wに耐える十分なスループットをプロビジョニングする
- 冪等性
- AWS公式SDKを使わない場合は、TransactWriteItems に ClientRequestToken を含めて冪等化する
- 過度なグルーピング回避
- 不要に多くの操作を1トランザクションにまとめない
- 正確性を損なわない範囲で複数トランザクションに分割すると、スループットと成功率が向上
- 競合最小化のデータモデリング
- 同一項目を同時更新するトランザクションでキャンセルが起き得るため、頻繁に複数項目へまたがって更新する属性群は1項目に集約してトランザクションのスコープを縮小する
- 大量取り込みの非推奨
- 大量データ取り込みにトランザクションを使わない
- バルク書き込みは BatchWriteItem を推奨
グローバルテーブルでのトランザクション API の使用
トランザクションオペレーションは、書き込み API が最初に呼び出された AWS リージョン内でのみ、不可分性、一貫性、分離性、および耐久性 (ACID) を保証します。グローバルテーブルのリージョン間では、トランザクションはサポートされていません。たとえば、米国東部 (オハイオ) リージョンと米国西部 (オレゴン) リージョンにレプリカを含むグローバルテーブルがあり、米国東部 (バージニア北部) リージョンで TransactWriteItems オペレーションを実行するとします。変更がレプリケートされると、米国西部 (オレゴン) リージョンで部分的に完了したトランザクションを確認できます。変更は、ソースリージョンでコミットされた後でのみ、他のリージョンにレプリケートされます。
- トランザクションのACID保証(不可分性・一貫性・分離性・耐久性)は、書き込みAPIを最初に呼び出したAWSリージョン内のみで有効
- グローバルテーブルのリージョン間ではトランザクションはサポートされない
考察
今回、DDBのトランザクションについて整理しました。次回は実際に動かして確認してみたいと思います。
参考