DynamoDB でリレーショナルデータをモデル化する例 - Amazon DynamoDB

DynamoDB でリレーショナルデータをモデル化する例

この例では、Amazon DynamoDB でリレーショナルデータをモデル化する方法について説明します。DynamoDB テーブルの設計は、「リレーショナルモデル化」に示されているリレーショナルオーダーのエントリスキーマに対応しています。これは、DynamoDB のリレーショナルデータ構造を表す一般的な方法である「隣接関係のリスト設計パターン」に基づいています。

この設計パターンでは、通常、リレーショナルスキーマのさまざまなテーブルに相関するエンティティタイプのセットを定義する必要があります。その後、エンティティ項目は、複合 (パーティションおよびソート) の主キーを使用してテーブルに追加されます。これらのエンティティ項目のパーティションキーは、項目を一意に識別する属性です。また、このパーティションキーは一般的に、すべての項目で PK と呼ばれます。ソートキー属性には、反転されたインデックスまたはグローバルセカンダリインデックスに使用できる属性値が含まれています。これは、一般的に SK と呼ばれます。

以下のエンティティを定義します。このエンティティは、リレーショナルオーダーのエントリスキーマをサポートしています。

  1. HR-Employee - PK: EmployeeID, SK: Employee Name

  2. HR-Region - PK: RegionID, SK: Region Name

  3. HR-Country - PK: CountryId, SK: Country Name

  4. HR-Location - PK: LocationID, SK: Country Name

  5. HR-Job - PK: JobID, SK: Job Title

  6. HR-Department - PK: DepartmentID, SK: DepartmentID

  7. OE-Customer - PK: CustomerID, SK: AccountRepID

  8. OE-Order - PK OrderID, SK: CustomerID

  9. OE-Product - PK: ProductID, SK: Product Name

  10. OE-Warehouse - PK: WarehouseID, SK: Region Name

これらのエンティティ項目をテーブルに追加したら、エンティティ項目のパーティションにエッジ項目を追加することで、それらのエンティティ項目間の関係を定義できます。このステップを以下のテーブルに示します。


      エンティティ項目間の関係を示す表の例。

この例では、EmployeeOrderProduct Entity のパーティションには、テーブル上の他のエンティティ項目へのポインタを含む追加のエッジ項目があります。次に、以前に定義されたすべてのアクセスパターンをサポートするために、グローバルセカンダリインデックス (GSI) をいくつか定義します。すべてのエンティティ項目で、主キーまたはソートキー属性に同じタイプの値を使用するわけではありません。必要なことは、主キー属性とソートキー属性をテーブルに挿入することだけです。

これらのエンティティの一部に適切な名前が使用されており、他のエンティティではソートキーバリューとして他のエンティティ ID が使用されているため、同じグローバルセカンダリインデックスで複数のタイプのクエリをサポートすることができます。この技術は、GSI オーバーロードと呼ばれます。これにより、複数の項目タイプを含むテーブルのデフォルトの 20 のグローバルセカンダリインデックスの制限は効果的に排除されます。これは、GSI 1 として次の図に示されています。


      複数のクエリをサポートするグローバルセカンダリインデックスを示すテーブルの例。

GSI 2 は、一般的なアプリケーションアクセスパターンをサポートするように設計されています。これは、特定の状態のテーブル上のすべての項目を取得することを目的としています。使用可能な状態間で項目の分散が不均一な大きなテーブルの場合は、この項目が、並列でクエリ可能な複数の論理パーティションに分散されていない限り、このアクセスパターンはホットキーになります。この設計パターンは、write sharding と呼ばれます。

GSI 2 で実現するには、アプリケーションで、すべての Order 項目に GSI 2 主キー属性を追加します。これは、0〜N の範囲内の乱数にそれを移入します。N は、特別な理由がない限り、一般的に次の式を使用して計算できます。

ItemsPerRCU = 4KB / AvgItemSize PartitionMaxReadRate = 3K * ItemsPerRCU N = MaxRequiredIO / PartitionMaxReadRate

例えば、次のことを前提とします。

  • 最大 200 万件の注文がシステム内にあり、5 年間で 300 万件に増加します。

  • これらの注文のうち、最大 20% は任意の時点で OPEN 状態になります。

  • 平均注文レコードは約 100 バイトで、注文パーティション内の 3 つの OrderItem レコードはそれぞれ約 50 バイト、注文エンティティの平均サイズは 250 バイトです。

そのテーブルでは、N 係数の計算方法は次のようになります。

ItemsPerRCU = 4KB / 250B = 16 PartitionMaxReadRate = 3K * 16 = 48K N = (0.2 * 3M) / 48K = 13

この場合、Order 状態のすべての OPEN 項目を読み込んでも、物理ストレージレイヤーにホットパーティションが発生しないように、GSI 2 の少なくとも 13 の論理パーティションにわたってすべての注文を分散させる必要があります。この番号は、データセットの異常を考慮した上で、埋め込むことをお勧めします。そのため、N = 15 を使用したモデルは問題ありません。前述したように、これを行うには、テーブルに挿入された Order レコードと OrderItem のレコードの GSI 2 PK 属性にランダムな 0~N 値を追加します。

この概要では、すべての OPEN の請求書を収集する必要があるアクセスパターンは比較的発生頻度が低いことを前提としているため、バースト容量を使用してリクエストを満たすことができます。StateDate Range のソートキー条件を使用して、次のグローバルセカンダリインデックスを照会し、必要に応じて特定の状態のサブセットまたはすべての Orders を生成することができます。


      GSI 2 プライマリキーと計画される属性を示すテーブルの例。

この例では、項目は 15 の論理パーティションにわたってランダムに分散されています。この構造は、アクセスパターンで多数の項目を取得する必要があるために機能します。したがって、15 スレッドのいずれかが空の結果セットを返して、無駄な容量を表すことはほとんどありません。何も返されなかった場合やデータが書き込まれていない場合でも、クエリは常に 1 つの読み込み容量ユニット (RCU) または 1 つの書き込み容量ユニット (WCU) を使用します。

アクセスパターンのこのグローバルセカンダリインデックスで高速なクエリを必要とする場合は、スパースの結果セットを返します。項目を分散させるには、ランダムパターンではなく、ハッシュアルゴリズムを使用することをお勧めします。この場合、実行時にクエリが実行されたときに認識される属性を選択し、項目が挿入されたときにその属性を 0~14 のキースペースにハッシュします。その後、それらをグローバルなセカンダリインデックスから効率的に読み込むことができます。

最後に、前に定義したアクセスパターンを再度使用することができます。次に、新しい DynamoDB バージョンのアプリケーションで使用するアクセスパターンとクエリ条件の一覧を示します。


      アクセスパターンとクエリ条件 (ID と名前別に従業員をクエリ、日付またはステータス別に注文をクエリなど) のリスト。