🎃

Data modeling with Amazon DynamoDB–Part1を見てみた(その2)

に公開

はじめに

2025年のAWS re:Inventに会社から参加させてもらえることになり、英語学習を行っている今日この頃です。
私の現在の英語力は一般的な日本人レベルなので、re:Inventに向けて作戦を立てました。
それは、自分の聞くセッション担当の方の英語に慣れること。
全英語力をアップグレードするほどの時間はないので、ドメイン・人を絞りに絞って学習しようという作戦です。
ということで今回は私が聞きたいDynamoDBのセッションを担当するAlex Debrieさんの過去の登壇を見て、こちらの記事にアウトプットすることにします。

AWS re:Invent 2020: Data modeling with Amazon DynamoDB – Part 1

https://www.youtube.com/watch?v=fiP2e-g-r4g
出典:YouTube「Data modeling with Amazon DynamoDB – Part 1(re:Invent 2020)」
登壇者:Alex Debrie
URL:https://www.youtube.com/watch?v=fiP2e-g-r4g

このYoutubeでは以下3つが語られています。

  • DynamoDB basics:DynamoDBの基礎(その1の記事
  • SQL vs NoSQL:SQLとNoSQL(その1の記事
  • One-to-many relationships:1対多の関係(本記事)

One-to-many relationships


上図のような関係性を1対多の関係と言います。

1対多の関係で重要な問いになるのは、
関連アイテムを取得するときに親アイテムに関する情報をどのように取得するかということです。
1つ例を挙げると、Ordersを取得する際にCustomerの情報をどう取得するかということです。

リレーショナルデータベースを使用している場合は、正規化してjoinを行うことで実現していました。
しかし、DynamoDBにはjoinがありません。

そこでDynamoDBの場合は1対多の情報取得に関して、2つのテクニックがあります。

  1. 非正規化すること
  2. データを事前結合すること

ここでは具体的な1対多の関係性をモデリングする方法を3つ紹介します。

  1. 非正規化 + 複雑な属性
  2. 非正規化 + 複製
  3. 複合主キー + query

非正規化 + 複雑な属性


その1の記事でも紹介したSaasアプリケーションの例で見ていきましょう。
組織とユーザーの情報が入っているテーブルに支払い方法の情報を追加してみましょう。

支払い方法は組織と1対多の関係になります。
クレジットカードでの支払いがデフォルトで、失敗したら口座振替といったように1つの組織に紐づく支払い方法が複数ある想定です。
そのような場合のDynamoDBのモデリングは以下のようになります。

PaymentMethod属性を見ると、複雑な属性(Map)になってることがわかります。
このMapにはデフォルトの支払い方法、バックアップの支払い方法を持ち、各方法にはタイプや番号などさまざまな情報が含まれています。

正規化の1つの原則は各列の値をatomicな値に分解することなので、このようなものはリレーショナルデータベースでは見たことがないと思います。

このモデリング方法は以下2つの条件があてはまる場合に適しています。

  1. 関連アイテムに直接アクセスパターンがないこと
    上記の例だと、関連アイテムはPaymentMethodです。PaymentMethodはクレジットカード番号を持っています。
    「このクレジットカード番号がどの組織のものか調べてもらえますか?」と尋ねられることは想定しておらず、もしこのようなアクセスパターンがある場合は、このモデリング方法は使用できません。
    私たちが想定するアクセスパターンは、「月末になり、この組織に請求する必要があります。組織を検索し、その組織に関するすべての支払い方法を見つけて、そのうちの1つが成功するまで処理を勧めます。」というものです。
    その1の記事で紹介した「事前にアクセスパターンを知っておく必要がある」というのはまさにこのような設計をするためですね。

  2. 関連アイテムの数が限られていること(400KB)
    DynamoDBには1アイテム400KBまでという上限があるので、関連アイテムの数が限られている場合にのみこのモデリング方法を使用すべきです。
    支払い方法は数種類に制限できるので、適していました。
    適さない例としては、以下です。
    顧客が注文を行うe-commerceサイトがあるとします。
    複合属性を使用してその顧客に関するすべての注文を1つのアイテムに配置することができます。
    しかし、顧客の注文が30、40と増えていくとある時点でそれ以上の情報を追加できなくなります。
    このように関連アイテムの数を制限できない場合は適していません。

非正規化 + 複製

2つ目のモデリング方法は非正規化 + 複製です。
著者と本の例で見ていきましょう。

ここでやっていることは、著者の出版日、誕生日をコピーしただけです。
データの整合性のためには正規化しておいて、1か所の更新で済むほうがいいので、この種の重複はリレーショナルデータベースでは見たことがないでしょう。

このモデリング方法は以下2つの条件があてはまる場合に適しています。

  1. 複製されたデータが不変である
    上記の例の場合、著者の誕生日は変わりません。
    なのでデータの整合性に関して心配する必要はそれほどありません。

  2. 複製されたデータが頻繁に変更されない、あまり複製されない
    複製されたデータが不変でない場合も、更新頻度が高くなかったり、複製数が2,3個であれば、整合性の問題は起きにくそうと考えてよさそうです。
    更新頻度が高かったり、複製数が多い場合は整合性の問題も起きそうだし、キャパシティユニットが増えてコストも膨大になりそうです。

複合主キー + query

先ほどのSaasアプリケーションで、組織とユーザーの1対多の例を見ていきましょう。

注目すべき点はORGアイテムとUSERアイテムが同じパーティションキーを持っていることです。
DynamoDBは同じパーティションキーを持つアイテムを非常に効率的に取得できるqueryを持っています。
非常に効率的に取得できる理由はパーティションキーが同じであれば、同じパーティションストレージに保存されていて、1か所からごそっと取得できるからですね。

「ある組織のすべてのユーザー一覧を取得してください」というアクセスパターンの場合、
すべてのユーザーを取得する際に、そのユーザーの組織に関する情報を付け足して情報を充実させてあげる必要があります。
同じItem Collection(同じパーティションキーを持つアイテムたち)内に共存させてあげることでDynamoDBクエリで非常に効率的に取得することができます。

リレーショナルデータベースのように読み取り時にjoinするのではなく、
DynamoDBでは書き込み時にjoinしてるというイメージです。
ここでいうjoinは同じItem Collection内に入れることですね。

感想・気づき

いくつかのモデリング方法を学ぶ中で、そのモデリングが成り立つ条件というものがあることを学べました。
アクセスパターンを事前に知っていないと、そのモデリングが成り立つかの判断ができないので、アクセスパターンを事前に知ることがDynamoDBの設計においては大事だなと改めて感じました。

リレーショナルデータベースより癖は強いけど、良い設計ができたり使いどころを間違えなければ、DynamoDBは強力な選択肢であり、設計がハマったら気持ちいいんだろうなとも思いました。

NCDCエンジニアブログ

Discussion