Amazon DynamoDB
開発者ガイド (API バージョン 2012-08-10)

DAX と DynamoDB の整合性モデル

Amazon DynamoDB Accelerator (DAX) は、DynamoDB テーブルにキャッシュを追加するプロセスを簡素化するために設計された書き込みスルーキャッシュサービスです。DAX は DynamoDB とは別に動作するため、アプリケーションが意図どおりに動作するように DAX と DynamoDB 両方の整合性モデルを理解することが重要です。

多くのユースケースでは、アプリケーションでの DAX の使用方法が、DAX クラスター内のデータの整合性、および DAX と DynamoDB 間のデータの整合性に影響します。

DAX クラスターノード間の整合性

アプリケーションの高可用性を実現するには、DAX クラスターのプロビジョニングには少なくとも 3 つのノードを使用してください。次にこれらのノードをリージョン内の複数のアベイラビリティーゾーンに配置します。

DAX クラスターの実行中、データがクラスター内のすべてのノードにレプリケートされます (複数のノードをプロビジョニング済みの場合)。DAX を使用して UpdateItem を正常に実行するアプリケーションの場合を考えてみましょう。このアクションにより、プライマリノードの項目キャッシュが新しい値で変更されます。この値は、クラスター内の他のすべてのノードにレプリケートされます。このレプリケーションの結果、整合性が保たれます。完了までにかかる時間は通常は 1 秒未満です。

このシナリオでは、2 つのクライアントが同じ DAX クラスターから同じキーを読み込んだ時に、それぞれのクライアントがアクセスしたノードによって、異なる値を受け取る可能性があります。更新がクラスター内のすべてのノードで完全にレプリケートされると、ノードはすべて整合性がとれます。(この動作は、DynamoDB の結果整合性特性と似ています。)

DAX を使用するアプリケーションを構築する場合、そのアプリケーションは結果整合性データを許容するように設計されている必要があります。

DAX 項目キャッシュの動作

各 DAX クラスターには 2 つの異なるキャッシュがあります。項目キャッシュとクエリキャッシュです。詳細については、「DAX: 仕組み」を参照してください。

このセクションでは、DAX 項目キャッシュに対する読み込みおよび書き込みの整合性の実装について取り扱います。

読み込みの整合性

DynamoDB では、GetItem オペレーションはデフォルトで結果的に整合性のある読み込みを行います。DynamoDB クライアントで UpdateItem を使用するとします。すぐ後に同じ項目を読み込もうとすると、更新前のデータが表示される場合があります。これは、DynamoDB ストレージロケーション全体への伝播が遅れるためです。通常、整合性は数秒以内に達成します。そのため、読み取りを再試行すると、更新された項目が表示されます。

DAX クライアントで GetItem を使用する場合、オペレーション (この場合は結果的に整合性のある読み込み) は次のように進行します。


                    項目を更新するための番号付きのステップを示すワークフロー図。
  1. DAX クライアントが GetItem リクエストを発行します。DAX は項目キャッシュからリクエストされた項目を読み込もうとします。項目がキャッシュ内にある (キャッシュヒット) 場合は、DAX によりアプリケーションに返されます。

  2. 項目がない場合 (キャッシュミス) は、DAX は DynamoDB に対して GetItem 結果整合性オペレーションを実行します。

  3. DynamoDB からリクエストされた項目が返され、DAX で項目キャッシュに保存されます。

  4. DAX はその項目をアプリケーションに返します。

  5. (非表示) DAX クラスターに複数のノードがある場合、項目がクラスター内の他のすべてのノードにレプリケートされます。

項目は、キャッシュの 有効期限 (TTL) 設定および最も長い間使用されていない (LRU) アルゴリズムによりますが、DAX 項目キャッシュに残ります。詳細については、「DAX: 仕組み」を参照してください。

ただし、この期間は、DAX は項目を DynamoDB から再ロードしません。他者が DynamoDB クライアントを使用し、DAX 全体をバイパスして項目を更新すると、DAX クライアントを使用した GetItem リクエストで、DynamoDB クライアントを使用した GetItem リクエストとは異なる結果が呼び出されます。このシナリオでは、DAX と DynamoDB は、DAX 項目の TTL が期限切れになるまで、同じキーに対して矛盾する値を保持します。

アプリケーションが DAX をバイパスして基礎となる DynamoDB テーブルのデータを変更する場合は、そのアプリケーションはデータの不整合が発生する可能性を予期し許容する必要があります。

注記

GetItem に加えて、DAX クライアントは BatchGetItem リクエストもサポートします。BatchGetItem は基本的には 1 つ以上の GetItem リクエストをまとめるラッパーです。そのため、 DAX ではそれぞれを個別の GetItem オペレーションとして扱います。

書き込みの整合性

DAX は書き込みスルーキャッシュであり、DAX の項目キャッシュが基礎となる DynamoDB テーブルに対して整合性を保つためのプロセスを簡易化します。

DAX クライアントは、DynamoDB と同じ書き込み API オペレーションをサポートします (PutItemUpdateItemDeleteItemBatchWriteItem および TransactWriteItems)。DAX クライアントでこれらのオペレーションを使用すると、項目は DAX と DynamoDB の両方で変更されます。DAX は、項目キャッシュ内の項目を、その TTL 値に関係なく更新します。

たとえば、DAX クライアントから GetItem リクエストを発行して、ProductCatalog テーブルから項目を読み込むとします。(パーティションキーは Id で、ソートキーはありません。) Id101 である項目を取得します。その項目の QuantityOnHand 値は 42 です。DAX は、特定の TTL で項目を項目キャッシュに保存します。この例では、TTL が 10 分とします。3 分後、別のアプリケーションが DAX クライアント使用して、QuantityOnHand 値が 41 になるように同じ項目を更新します。その後はその項目が更新されないとすると、その後 10 分間の同じ項目に対する後続の読み込みは、QuantityOnHand に対してキャッシュされた値を返します (41)。

DAX の書き込み処理方法

DAX は、高パフォーマンスの読み込みを必要とするアプリケーションを対象としています。書き込みスルーキャッシュである DAX は、直接書き込みを発行できるため、書き込みは即時項目キャッシュに反映されます。キャッシュ無効化ロジックを管理する必要はありません。DAX がお客様の代わりにそれを行います。

DAX では、PutItemUpdateItemDeleteItemBatchWriteItem、および TransactWriteItems の各書き込みオペレーションがサポートされます。

これらの PutItemUpdateItemDeleteItem、または BatchWriteItem リクエストを DAX に送信すると、次のことが発生します。

  • DAX は DynamoDB にリクエストを送信します。

  • DynamoDB が DAX に返答し、書き込みが成功したことが確認されます。

  • DAX がキャッシュ項目に項目を書き込みます。

  • DAX がリクエスタに成功を返します。

これらの TransactWriteItems リクエストを DAX に送信すると、次のことが発生します。

  • DAX は DynamoDB にリクエストを送信します。

  • DynamoDB が DAX に返答し、トランザクションが完了したことが確認されます。

  • DAX がリクエスタに成功を返します。

  • バックグラウンドで、DAX は項目キャッシュに項目を格納するために、TransactWriteItems リクエスト内の各項目に対して TransactGetItems リクエストを行います。TransactGetItems は、直列化分離を確実にするために使用されます。

スロットルを含む、何らかの理由で DynamoDB への書き込みが失敗した場合、項目は DAX にキャッシュされません。失敗の例外がリクエスタに返されます。これにより、データが先に DynamoDB 正常に書き込まれない限り、DAX には書き込まれません。

注記

DAX へのすべての書き込みは、項目キャッシュの状態を変更します。ただし、項目キャッシュへの書き込みはクエリキャッシュに影響しません。(DAX の項目キャッシュとクエリキャッシュは異なる目的で使用されます。オペレーションは互いに独立しています。)

DAX クエリキャッシュの動作

DAX は、Query および Scan リクエストからの結果をクエリキャッシュにキャッシュします。ただし、これらの結果は項目キャッシュにまったく影響しません。アプリケーションが DAX を使用して Query または Scan リクエストを発行すると、結果セットは項目キャッシュではなくクエリキャッシュに保存されます。Scan オペレーションを実行して項目キャッシュを「ウォームアップ」することはできません。項目キャッシュとクエリキャッシュは別々の存在です。

クエリ-更新-クエリの整合性

項目キャッシュ、または基礎となる DynamoDB テーブルに対する更新によって、クエリキャッシュに保存されている結果が無効化されたり変更されたりすることはありません。

説明のため、以下のシナリオを想定してください。アプリケーションは、パーティションキーとして DocId を使用し、ソートキーとして RevisionNumber を使用する DocumentRevisions テーブルを使用しています。

  1. クライアントが DocId 101 に対して、RevisionNumber5 以上のすべての項目を求める Query を発行します。DAX は結果セットをクエリキャッシュに保存し、ユーザーに結果セットを返します。

  2. クライアントは、RevisionNumber 値が 20DocId 101 に対して PutItem リクエストを発行します。

  3. クライアントはステップ 1 の説明と同じ Query を発行します (DocId 101 および RevisionNumber >= 5).

このシナリオでは、ステップ 3 で発行された Query のキャッシュ結果セットは、ステップ 1 でキャッシュされた結果セットと同じです。これは、DAX では Query または Scan の結果セットを個別の項目に対する更新に基づいて無効化しないためです。ステップ 2 の PutItem オペレーションは、Query の TTL の有効期限が切れたときにのみ DAX クエリキャッシュに反映されます。

アプリケーションでは、クエリキャッシュの TTL 値、およびクエリキャッシュと項目キャッシュ間の結果の不一致をアプリケーションで許容できる期間を考慮に入れる必要があります。

強力な整合性のあるトランザクション読み込み

強い整合性のある GetItemBatchGetItemQuery、または Scan リクエストを実行するには、ConsistentRead パラメータを true に設定します。DAX は強い整合性のある読み込みリクエストを DynamoDB に渡します。DynamoDB から返答を受け取ると、DAX はクライアントに結果を返しますが、結果をキャッシュしません。DAX は DynamoDB と緊密には連携していないため、単独では強い整合性のある読み込みを提供できません。このため、DAX からの後続の読み取りは、結果的に一貫した読み取りである必要があります。そして、後続の強い整合性のある読み取りはすべて DynamoDB に渡す必要があります。

DAXは、TransactGetItems リクエストを一貫性のある読み込みと同じ方法で処理します。DAX はすべての TransactGetItems リクエストを DynamoDB に渡します。DynamoDB から返答を受け取ると、DAX はクライアントに結果を返しますが、結果をキャッシュしません。

ネガティブキャッシング

DAX は、項目キャッシュとクエリキャッシュの両方でネガティブキャッシュエントリをサポートします。ネガティブキャッシュエントリは、DAX が基礎となる DynamoDB テーブルでリクエストされた項目を見つけられなかったときに発生します。DAX は、エラーを生成する代わりに空の結果をキャッシュし、その結果をユーザーに返します。

たとえば、アプリケーションが GetItem リクエストを DAX クラスターに送信し、DAX 項目キャッシュの中に一致する項目がなかったとします。この場合、DAX は基礎となる DynamoDB テーブルから対応する項目を読み込むことになります。項目が DynamoDB に存在しない場合、DAX は空の項目を項目キャッシュに保存し、その空の項目をアプリケーションに返します。ここで、アプリケーションが同じ項目に対する別の GetItem リクエストを送信するとします。DAX は項目キャッシュ内に空の項目を見つけ、それをすぐにアプリケーションに返します。DynamoDB はまったく参照しません。

ネガティブキャッシュエントリは、項目 TTL の有効期限が切れるか、LRU が呼び出されるか、PutItemUpdateItem、または DeleteItem を使用して項目が変更されるまで、DAX 項目キャッシュに残ります。

DAX クエリキャッシュも、同様の方法でネガティブキャッシュ結果を処理します。アプリケーションが Query または Scan を実行し、DAX クエリキャッシュにキャッシュされた結果がない場合、DAX は DynamoDB にリクエストを送信します。結果セットに一致する項目がない場合、DAX は空の項目セットをクエリキャッシュに保存し、その空の結果セットをアプリケーションに返します。それ以降の Query または Scan リクエストは、その結果セットの TTL の有効期限が切れるで、同じ (空の) 結果セットを呼び出します。

書き込みの戦略

DAX の書き込みスルー動作は、多くのアプリケーションパターンに適しています。ただし、書き込みスルーモデルが適切ではないアプリケーションパターンもいくつかあります。

レイテンシーの影響を受けやすいアプリケーションの場合、DAX を介した書き込みでは余分なネットワークホップが発生します。そのため、DAX への書き込みは、DynamoDB への直接の書き込みよりも少し遅くなります。書き込みレイテンシーが重要なアプリケーションの場合は、代わりに DynamoDB に直接書き込むことで、レイテンシーを軽減できます。詳細については、「書き込み迂回」を参照してください。

書き込み負荷の高いアプリケーション (バルクデータのロードを実行するものなど) では、すべてのデータを DAX 経由で書き込むのは望ましくない場合があります。アプリケーションで読み込むのはそのデータのほんのわずかな割合であるためです。大量のデータを DAX 経由で書き込む場合、読み込まれる新しい項目用にキャッシュ内に空きを作るため、LRU アルゴリズムが実行される必要があります。そのため、読み込みキャッシュとしての DAX の効果が落ちます。

DAX に項目を書き込む場合、項目キャッシュの状態が新しい項目に対応するように変更されます。(たとえば、DAX は新しい項目用の空きを作るために、古いデータを項目キャッシュから削除する必要がある場合があります。) 新しい項目は、キャッシュの LRU アルゴリズムおよびキャッシュの TTL 設定によりますが、項目キャッシュに残ります。項目が項目キャッシュに保持される限り、DAX はその項目を DynamoDB から再ロードしません。

書き込みスルー

DAX 項目キャッシュには書き込みスルーポリシーが導入されています。詳細については、「DAX の書き込み処理方法」を参照してください。

項目を書き込むと、DAX はキャッシュされた項目が DynamoDB に存在する項目と同期されていることを確認します。これは、項目を書き込み直後に再ロードする必要があるアプリケーションにとって有用です。ただし、他のアプリケーションが DynamoDB テーブルに直接書き込んだ場合、DAX 項目キャッシュ内のその項目は、DynamoDB と同期しなくなります。

例として、ProductCatalog テーブルを使用している 2 人のユーザー (Alice と Bob) がいるとします。Alice は DAX を使用してテーブルにアクセスしますが、Bob は DAX をバイパスして、DynamoDB 内のテーブルに直接アクセスします。


                    ユーザー の Alice と Bob が DAX と DynamoDB を使用してテーブルにアクセスする方法の番号付きのステップを示すワークフロー図。
  1. Alice が ProductCatalog テーブル内の項目を更新します。DAX はリクエストを DynamoDB に転送し、更新が成功します。DAX は次にその項目を項目キャッシュに書き込み、Alice に成功応答を返します。この時点から、項目が最終的にキャッシュから削除されるまで、この項目を DAX から読み込んだユーザーには Alice が更新した項目が表示されます。

  2. しばらくして、Bob は Alice が書いたのと同じ ProductCatalog 項目を更新します。ただし、Bob は DynamoDB で項目を直接更新します。DAX は DynamoDB 経由の更新に応じて自動的に項目キャッシュを更新しません。そのため、DAX のユーザーからは、Bob の更新が見えません。

  3. Alice が DAX からその項目を再度読み込みます。項目は項目キャッシュにあります。したがって、DAX は DynamoDB テーブルにアクセスせずに Alice に項目を返します。

このシナリオでは、Alice と Bob では、同じ ProductCatalog 項目で表示が異なります。DAX がその項目を項目キャッシュから削除するか、別のユーザーが同じ項目を DAX を使用して再度更新するまで、この状態になります。

書き込み迂回

アプリケーションで大量のデータ (バルクデータのロードなど) を書き込む必要がある場合、DAX をバイパスしてデータを直接 DynamoDB に書き込む方が妥当な場合があります。このような書き込み迂回戦略により、書き込みレイテンシーを軽減します。ただし、項目キャッシュは DynamoDB のデータと同期しません。

書き込み迂回戦略を使用する場合は、DAX で項目キャッシュに書き込まれるのは、アプリケーションが DAX クライアントを使用してデータを読み込むときであることに注意してください。これは一部のケースでは利点になります。最も頻繁に読み込まれるデータのみが (もっとも頻繁に書き込まれるデータとは反対に) キャッシュされるためです。

たとえば、あるユーザー (Charlie) が、DAX を使用して別のテーブルである GameScores テーブルを使用するとしましょう。GameScores のパーティションキーは UserId であるため、Charlie のスコアは同じ UserId を持ちます。


                    Charlie が DAX を使用して DynamoDB テーブルを操作する方法の番号付きのステップを示すワークフロー図。
  1. Charlie は自分のスコアをすべて取得するため、Query を DAX に送信します。このクエリが以前に発行されていないとすると、DAX は処理のためにクエリを DynamoDB に転送します。結果を DAX クエリキャッシュに保存し、Charlie に結果セットを返します。この結果セットは、削除されるまでクエリキャッシュで使用できます。

  2. ここで、Charlie が Meteor Blasters ゲームをプレイし、ハイスコアを達成したとします。Charlie は UpdateItem リクエストを DynamoDB に送信して、GameScores テーブルの項目を変更します。

  3. 最後に、Charlie はさっきの Query を再実行して、自分のデータをすべて GameScores から取得することにします。この結果には、Meteor Blasters のハイスコアは表示されません。これは、クエリの結果が項目キャッシュではなくクエリキャッシュから取得されるためです。2 つのキャッシュは互いに独立しており、一方のキャッシュでの変更は他方のキャッシュに影響しません。

DAX では、クエリキャッシュの結果セットを DynamoDB の最新のデータに更新しません。クエリキャッシュ内の各結果セットは、Query または Scan オペレーションが実行された時点のものです。したがって、Charlie の Query の結果は、彼の PutItem オペレーションを反映していません。DAX が結果セットをクエリキャッシュから削除するまで、この状態が続きます。