web-dev-qa-db-ja.com

Dynamodbの3つのフィールドの複合主キー(一意のアイテム)

DynamoDBに請求書の明細を保存するテーブルを作成しようとしています。アイテムがCompanyCodeInvoiceNumberおよびLineItemId、金額、その他の広告申込情報の詳細で定義されているとします。

一意のアイテムは、最初の3つの属性の組み合わせによって定義されます。これらの属性のうち2つは、異なるアイテムに対して同じにすることができます。ハッシュ属性と範囲属性として何を選択すればよいですか?

32
HHH

@ georgeaf99が提供する最初のオプション は機能しません。そのようにすると、CompanyCodeはテーブル内で一意である必要があるためです。したがって、会社ごとに1つのアイテムしか許可されません。 2番目の解決策が唯一の現実的な方法だと思います。

ハッシュキーとしてCompanyCodeを使用できます。その後、アイテムを一意にするために結合する他のすべてのフィールド(この場合はInvoiceNumberLineItemId)を何らかの方法1つの値(フィールドデリミタとの連結など)に結合され、これが範囲キーになります。残念ながら、これはthatいですが、それがDynamoDBのようなNoSQLデータベースの性質です。ただし、正しい一意性でレコードを正常に保存できます。レコードを読み戻すときに、結合されたフィールドを解析して個々の部分に戻したくない場合は、InvoiceNumberLineItemIDに個別のフィールドを追加する必要があります。

会社ごとに多数の請求書がない場合は、ハッシュキーのみでクエリを実行し、クライアント側でフィルタリングを実行できます。会社ごとに多数の請求書があり、単一の請求書のアイテムのみを照会できるようにする必要がある場合は、CompanyCodeとInvoiceNumberにセカンダリインデックスを作成します。

25
JoeMjr2

いくつかのイントロ

効率のために、まったく異なる設計を提案します。 NoSQLデータベース(およびDynamoDBに違いはありません)では、常に最初にアクセスパターンを考慮する必要があります。また、可能であれば、すべてのデータを同じテーブルといくつかのインデックスに収めるよう努力する必要があります。 OPから得たものと彼のコメントから、これらは2つのアクセスパターンです。

  1. 会社Xの場合、完全な請求書Yを取得します(すべてのアイテムまたはアイテムの範囲を含む)[これに基づいて コメント ]
  2. X社のすべての請求書を取得[これに基づいて コメント ]

今、私たちは良い主キーとは何だろうか?良いパーティションキー(PK)とは何ですか?良いソートキー(SK)とは何か、どのセカンダリインデックスを作成する必要があり、どのような種類(ローカルまたはグローバル)なのかという質問に変換しますか?いくつかのリマインダー:

  • 主キーは1つの列または複合にできます
  • 複合主キーは、パーティションキーとソートキーで構成されます
  • パーティションキーは、アイテムのパーティションを決定するハッシュ関数への入力として使用されます
  • ソートキーはコンポジットにすることもできます。これにより、コメントリンクのいずれかで指定されているように、DynamoDBで1対多の関係をモデル化できます。 https://docs.aws.Amazon.com/amazondynamodb/latest/developerguide /bp-sort-keys.html
  • テーブルまたはインデックスでクエリを作成するときは、常にパーティションキーで「=」演算子を使用する必要があります
  • ソートキーの範囲を照会する場合、KeyConditionExpressionのオプションがあります。このオプションは、 ソート用の演算子のセット とその間のすべて(そのうちの1つは関数begins_with (a, substr))を提供します
  • クエリ結果をさらに絞り込む必要がある場合は、FilterExpressionを使用することもできます(投影された属性をフィルタリングします)
  • ローカルセカンダリインデックス(LSI)には同じパーティションキーがありますが、元のテーブルとは異なるソートキーがあり、別のソートキーに従って整理されたデータの異なるビューを提供します
  • グローバルセカンダリインデックス(GSI)には、元のテーブルとは異なるパーティションキーと異なるソートキーがあり、データの完全に異なるビューを提供します
  • 同じパーティションキーを持つすべてのアイテムは一緒に格納され、複合プライマリキーの場合、並べ替えキーの値の順に並べられます。コレクションサイズが10 GBを超えると、DynamoDBはソートキーでパーティションを分割します。

モデリングに戻る

モデル化して同じテーブルに収める必要がある複数のエンティティを扱っていることは明らかです。パーティションキーがテーブル上で一意であるという条件を満たすために、CompanyCodeは自然なパーティションキーとして提供されるため、一意であることを確認します。そうでない場合、2番目のアクセスパターンをどのようにモデル化できますか?

CompanyCodeに一意性が確立されていると仮定して、単純化して、電子メールの形式(またはドメインまたは単なるコードですが、デモには電子メールを使用します)であるとしましょう。

  • 会社と請求書の関係は常に1対多です。
  • 請求書とアイテムの関係は常に1対多です。

下の画像のようなデザインを提案します。 Proposed design in DynamoDB

  • PKがCompanyCodeであり、SKがInvoiceNumberである場合、その会社の請求書に関するすべての属性を格納できます。
  • また、SKがCustomerであるレコードを追加することを妨げるものはありません。これにより、会社に関するすべての属性を格納できます。
  • GSI1では、GSI1PKがテーブルSK(InvoiceNumber)で、GSI1SKがテーブルPK(CompanyCode)である逆ルックアップを作成します。
  • 私は同じテーブルを使用して、PKがLineItemIdで、SKがCompanyCodeである広告申込情報を保存しています(まだ一意です)
  • アイテムエンティティアイテムの場合、GSI1PKはまだInvoiceNumberであり、GSI1SKはテーブルPKであるLineItemIdであるため、請求書エンティティアイテムの場合と同じです。

これでサポートされるアクセスパターン:

  • X社とすべてのアイテムの請求書Yを取得する場合(アクセスパターン1):CompanyCode=Xのテーブルを照会し、=演算子でKeyConditionExpressionのソートキーを使用してInvoiceNumberを使用します。その請求書に関連付けられているすべてのアイテムを取得する場合は、Itemsを使用してProjectionExpression属性を投影します。
  • 会社Xと請求書Yの以前のクエリですべてのアイテムを取得することで、テーブルでBatchGetItem API呼び出し(一意の複合キーLineItemId+CompanyCodeを使用)を実行し、その特定の顧客の特定の請求書に属するすべてのアイテムを取得できるようになりました。 (これには BatchGetItem API の制約がいくつかあります)
  • アクセスパターン2をサポートするには、PKでCompanyCode=Xを使用してクエリを実行し、SKでKeyConditionExpressionbegins_with (a, substr)関数/演算子で使用して、会社Xの請求書のみを取得し、その会社のメタデータは取得しません。これにより、特定の会社/顧客のすべての請求書が提供されます。
  • さらに、上記のGSI1では、特定のInvoiceNumberについて、その特定の請求書に属するすべての品目を簡単に選択できます。 REMEMBER:グローバルセカンダリインデックスのキー値は一意である必要はありません -したがって、GSI1では簡単にinvoice_1->(item_1、item_2)、次に別のinvoice_1->(item_1、item_2)ですが、GSIの2つのアイテムの違いはSKにあります(異なるCompanyCodeに関連付けられます(ただし、デモンストレーションのためにinvoice_1とinvoice_2)。
18
azec-pdx

わかっていると思いますが、主キー(ハッシュ+範囲)から3つ以上の属性を持つことはできません。したがって、実行するクエリのタイプとデータのサイズに応じて、さまざまな方法でテーブルを構築できます。

(上記のクエリタイプ用に最適化:CompanyCodeと3つのみ)

小規模/中規模のデータセットに最適なソリューション:

  • ハッシュキー:CompanyCode
  • CompanyCodeのみを使用してクエリを実行し、他の2つの属性で結果をフィルタリングします

大規模なデータセットに最適なソリューション:

  • ハッシュキー:CompanyCode
  • 範囲キー:InvoiceNumber + LineItemId
  • これにより、インデックスでのみクエリを実行できますが、テーブル構造はかなりugいです
8
georgeaf99