概要
業務でDynamoDBを利用する機会があったのですが、SQLに冒された脳ミソではかなり躓いたので備忘録として。。
第一弾は、DynamoDBの基本的な概念をおさらいしたいと思います。
対象:RDBは分かるけど、NoSQLを触ったことのないヒト
DynamoDBのキホン
テーブル、アイテム、属性
- テーブル
- RDBとほぼ同じ概念。アイテムのコレクション
- アイテム
- RDBでいうレコード。一意に識別可能な属性の集合
- 属性
- RDBでいう列、カラム。
プライマリキー
アイテムを一意に識別できるキー。以下の2種類がある。
- パーティションキー
- パーティションキーとソートキー(RDB的には複合キー)
それぞれの意味はこちら。
- パーティション
- データが保存されるDynamoDB内部の物理ストレージ
- DynamoDBのデータは複数のパーティションに分散して保存される
- パーティションキー
- キー値をハッシュ化してデータを保存するパーティションを決定する
- ソートキー
- パーティション内のデータはソートキーで並び替えられて、物理的に近くに保存される
公式の素晴らしい図解はこちら。
セカンダリインデックス
異なるパーティションキー、ソートキーを設定できる仕組み。以下の2種類がある。
- グローバルセカンダリインデックス(GSI)
- 元のテーブルと異なるパーティションキーとソートキーを設定する
- テーブルあたり20個まで
- ローカルセカンダリインデックス(LSI)
- 元のテーブルと同じパーティションキーと、異なるソートキーを設定する
- テーブルあたり5個まで
とりあえず作ってみる
ちなみにNoSQL Workbenchというツールで作成しました。わりと便利w
PK=Article, SK=Campaignという元テーブルに、
PK=Campaign, SK=Articleという(グローバル)セカンダリインデックスを設定してみました。
なるほど、、こんな感じで「物件ごと」「キャンペーンごと」などの出し分けが出来るってわけですね。。
データの操作
要はSQL文です。簡潔なネーミングで助かります。
-
基本的なCRUD
-
GetItem
- プライマリキーを指定して、単一のアイテムを取得する
- プライマリキーが複合キー(パーティションキー+ソートキー)の場合は全て指定する必要がある
-
PutItem
- プライマリキーを指定して、単一のアイテムを追加する
-
UpdateItem
- プライマリキーを指定して、単一のアイテムを更新する
-
DeleteItem
- プライマリキーを指定して、単一のアイテムを削除する
-
-
バッチオペレーション
- 複数の操作を並列処理してくれる
-
BatchGetItem
- 最大100個まで
GetItem
に対応
- 最大100個まで
-
BatchWriteItem
- 最大25個の
PutItem
およびDeleteItem
に対応 -
UpdateItem
は対応していない
- 最大25個の
-
クエリとスキャン
-
Query
- パーティションキーを指定して、複数のアイテムを取得する
- ソートキーを指定して、検索結果を絞り込む
-
Scan
- 全てのアイテムを取得する
-
ここまではいい。躓いたのはここからorg
RDB的なリレーションをどうやって表現するんだ、、だって
JOINできないんだもん。。
「多対多」の関係を考える
記事にタグを付けて、タグは一元管理したいみたいなケース。
1つの記事は複数のタグを設定でき、タグも複数の記事に紐づくという典型的なパターン。
以下の様なデータモデルを考えます。
- RDBの場合
- おそらく中間テーブルを作って1対多の関係を作りますよね。
- DynamoDBの場合
- 同じ考え方でできそう。。
- TagIdをPKとするセカンダリインデックスを作る
- PK=PostId, PK=TagIdとして
Query
を投げれば投稿ごと、タグごとのデータが取り出せる
一見良さそうだが(多分間違いではない)、実際にデータを入れてみると問題に気付く。
【例】あるタグが付けられた投稿の一覧を取得したい
- PK=TagIdで中間テーブル(Links)に
Query
を投げる(1回) - 取得した各PostIdについて、PK=PostIdとしてPostsテーブルに
GetItem
を投げる(N回)
ちょ、リクエスト数多くない??
しかも、アイテム数に比例してリクエスト数が増加するっていう。。JOIN出来ないの?
( n+1問題というらしいです。勉強になりますw)
あれこれドキュメントを探っていると、、 [公式](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html)に素晴らしいプラクティスがありました。
公式にはまたこんな金言も。。
DynamoDB アプリケーションではできるだけ少ないテーブルを維持する必要があります。
本当に、公式ドキュメントを読むのって大事ですよねw
隣接関係リスト
さあ、解決編です。
結論から言うと、一枚のテーブルにします。お馴染み(?)のNoSQL Workbenchで作りました。
PostId=PK, TagId=SK/GSI-PKとしてます。
RDBに慣れた人にとっては、目眩がしそうなデータ構造ですね。。
ポイントは以下になります。
-
PK=SK=PostId
のとき、それは投稿の詳細を表す -
PK=SK=TagId
のとき、それはタグの詳細を表す
実際のクエリはこうなります。
- ある投稿の詳細を取得したい -> PK=SK=PostIdで
GetItem
- あるタグの詳細を取得したい -> PK=SK=TagIdで
GetItem
- ある投稿に付いたタグ一覧を取得したい -> PK=PostIdで
Query
- あるタグの付いた投稿一覧を取得したい -> PK=TagIdで
Query
おまけですが、、
-
UpdateItem
でタグ名を変更しても、各PostIdがもつのはTagIdなので整合性は保たれる(ハズ)
上手くいきそうですね。これ考えたヒトすごーーいw
これでなんとかDynamoDBでテーブル設計ができそうな気がします。。
次回はずばり「トランザクション」です!
ちゃんとロールバックするんだよね!?大丈夫だよね?
まだまだDynamoDBに半信半疑な今日この頃でした。。