8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【SQL脳から切り替えよう】DynamoDB使ってみた

Posted at

概要

業務で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という(グローバル)セカンダリインデックスを設定してみました。

Article

Campaign

なるほど、、こんな感じで「物件ごと」「キャンペーンごと」などの出し分けが出来るってわけですね。。

データの操作

要はSQL文です。簡潔なネーミングで助かります。

  • 基本的なCRUD

    • GetItem
      • プライマリキーを指定して、単一のアイテムを取得する
      • プライマリキーが複合キー(パーティションキー+ソートキー)の場合は全て指定する必要がある
    • PutItem
      • プライマリキーを指定して、単一のアイテムを追加する
    • UpdateItem
      • プライマリキーを指定して、単一のアイテムを更新する
    • DeleteItem
      • プライマリキーを指定して、単一のアイテムを削除する
  • バッチオペレーション

    • 複数の操作を並列処理してくれる
    • BatchGetItem
      • 最大100個までGetItemに対応
    • BatchWriteItem
      • 最大25個のPutItemおよびDeleteItemに対応
      • UpdateItemは対応していない
  • クエリとスキャン

    • Query
      • パーティションキーを指定して、複数のアイテムを取得する
      • ソートキーを指定して、検索結果を絞り込む
    • Scan
      • 全てのアイテムを取得する



ここまではいい。躓いたのはここからorg
RDB的なリレーションをどうやって表現するんだ、、だって


JOINできないんだもん。。

「多対多」の関係を考える

記事にタグを付けて、タグは一元管理したいみたいなケース。
1つの記事は複数のタグを設定でき、タグも複数の記事に紐づくという典型的なパターン。
以下の様なデータモデルを考えます。

M:Nリレーション

  • RDBの場合
    • おそらく中間テーブルを作って1対多の関係を作りますよね。

M:NリレーションRDB

  • DynamoDBの場合
    • 同じ考え方でできそう。。
    • TagIdをPKとするセカンダリインデックスを作る
    • PK=PostId, PK=TagIdとしてQueryを投げれば投稿ごと、タグごとのデータが取り出せる

M:NリレーションRDB

一見良さそうだが(多分間違いではない)、実際にデータを入れてみると問題に気付く。

【例】あるタグが付けられた投稿の一覧を取得したい

  • 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としてます。

隣接関係リスト

隣接関係リストGSI

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に半信半疑な今日この頃でした。。

参考文献

8
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?