改めてDynamoDBのテーブル設計を考える

以前書いた記事の焼き直しになります。

あれから何度かDynamoDBを使ってきて、考えなおすところもあったので。

用途によっては無理に1つのテーブルにまとめなくてもいいんじゃない?

DynamoDBのテーブルは少ないほどいいというのがAWS公式でもアナウンスされているベストプラクティスですが、是が非でも非正規化して少なくまとめる必要はないんじゃないかというのが私の見解です。

理由その1:テーブルの中身が複雑になる
テーブルを少なくまとめてファセットを多用すると一目でそのテーブルに何が入っているのか全然わからなくなります。


これは実装・デバッグ面で足かせになりますし、補助ドキュメントをちゃんとつくらないと何が何だか分からなくなります。
私見ですが人が理解しづらいデータ構造はやめた方がいいと思っています。
お客さんにわかるように説明するのも一苦労だし。

理由その2:LSI・GSIをたくさん張れない
最初にユースケースを洗い出すのがDynamoDBの鉄則ですが、そうはいっても往々にして仕様変更でユースケースや検索・ソートしたい箇所が変わることは結構あります。
テーブルを多くしておけばそれだけ、1テーブル5つのLSI、20までのGSIの制限が緩くなります。

理由その3:キャパシティの見積もりそんなする?
テーブルが少なければキャパシティの見積もりが楽になるというのがありますが、オンデマンドモードが入ってからは特に考えずにこれに設定することが多いです。
もちろんコストが気になるならちゃんと見積もらないといけないとは思いますが。


N+1問題もありますが、DynamoDBなら速度でゴリ押せる面も多少はあるので扱うデータの大枠の分類ごとにテーブルを作成するようにして、あんまりファセットを使わなくてもいいように最近はしています。

日時で属性を分けなくてもいい

これは以前の記事で分けるよう書いてしまってたので訂正です…。

ソートキーに日時属性つけておけば前方一致や範囲検索で日付or日時検索は実現できます。
まあそれでも共通のパーティションキーは必要なのであくまで用途次第ですが。

集計データはリアルタイムで作成する

これも以前の記事で解がなかったので。
ご存じDynamoDBはSQLみたいに集計関数がないため集計が苦手です。
なので冗長データとなってしまいますが集計前のデータが追加・変更・削除された時点で集計後のデータも更新しに行くような作りにしてしまうことで、集計せずともそこを読みに行くだけで集計結果を取るやり方があります。

集計結果を持つ場所はファセットや、同じ項目内に集計後の属性を設けておいたり。

更新の同期はロジック側で関数化しておくなりして担保しておきましょう。

極力Set型を使わない

文字列セット、数値セットのSet型属性は、Set型ならではの文字型数値型の縛りや重複排除、順序保持の制約がどうしても必要でなければ、単なるリストでなるべく定義するようにしましょう。

セット型を使うと単純なJSONへのコンバートができなくなり面倒な場面が多かったです。

SQL一応使える

SQL互換のPartiQL(パーティクル)に最近対応しました。


キーを意識しないとフルスキャンになってしまうらしいので、使いどころは考えないといけないとは思いますが、今まで辛かったデータの調査が少し楽になるかも。 こんな感じでデータの検索が可能です。
SELECT  id
       ,data_type
       ,project_id
       ,description
       ,attributes.user_id
       ,attributes.user_name
       ,create_at
       ,update_at
  FROM "my-table"."project_id-data_type-index"
 WHERE project_id = 'foo'
   AND Contains(data_type ,'score#')


私見も大いにありますし、作るシステムの性質次第な気もしますが、一意見として参考になれば。