12
4

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 1 year has passed since last update.

KDDI Engineer & DesignerAdvent Calendar 2023

Day 21

DynamoDBのテーブル・インデックス設計について考えてみた

Last updated at Posted at 2023-12-20

概要

サーバレスアプリケーションで利用されることの多いAmazon DynamoDBについてまとめてみました。DynamoDBの利用を検討されている方やDynamoDBのテーブル・インデックス設計を考えられている方の参考になると幸いです。

Amazon DynamoDB

DynamoDBの特徴

  • フルマネージド型の NoSQL データベースサービス
  • データは3つの Availability Zone に同期的にレプリケートされるため可用性とデータ耐久性が高い
  • 性能要件に応じて、テーブルごとに読み込み・書き込みキャパシティを設定可能。キャパシティの Auto Scalingやオンデマンドキャパシティにも対応
  • ストレージの容量制限がない。使った分だけの従量課金制

基礎知識

ss_20231220_205437.jpg
テーブルは各Itemを一意に識別できるPrimary Keyを持ちます。Primary Keyは、Partition KeyおよびSort Keyで構成されます。Partition Keyのみで各Itemを一意で識別できる場合はSort Keyは必須ではありません。Primary Key以外の項目は不揃いでも構いません。

Primary Key

Primary Keyは、テーブル内の各Itemを一意に識別できる必要があります。DynamoDBは2種類のプライマリキーをサポートします。

  • Partition Key(PK)
    • PKが重複する項目は登録できません。
  • Partition Key(PK) & Sort Key(SK)
    • PKは重複しても構いません。
    • PKとSKがともに重複する項目は登録できません。

Secondary Index

Primary Keyを用いたクエリに加えて、柔軟なクエリを実現するためにDynamoDBは2種類のSecondary Indexをサポートしています。

Local Secondary Index(LSI)

  • SK以外に絞り込み検索を行うためのIndexです。
  • Primary KeyのPKと同一ですが、SKが異なる属性による検索のために利用します。
  • LSIはテーブル作成時にのみ作成可能です。

Global Secondary Index(GSI)

  • Primary Keyの代わりとなる、PKをまたいだ検索を行うためのIndexです。
  • GSIにより、Primary Keyに依存しない新たなPK・SKを持つことができます。
  • テーブル作成後でも作成可能です。

キャパシティユニットについて

  • Read Capacity Unit (RCU)
    • 結果整合性:1ユニットにつき、最⼤4KBのデータを1秒に2回読み込み可能です。
    • 強い整合性:1ユニットにつき、最⼤4KBのデータを1秒に1回読み込み可能です。
  • Write Capacity Unit (WCU)
    • 1ユニットにつき、最⼤1KBのデータを1秒に1回書き込み可能です。

クエリパターン

具体例がないとわかりにくいと思うので、Primary Key(PK, SK)、LSI, GSIを用いたクエリパターンをご紹介します。

DynamoDBのクエリにおける大前提

  • PKとSKを用いてのみ検索が行なえます。その他の属性に対して検索をかけることはできません。
  • PKは必ず検索条件に指定する必要があります。SKのみを検索条件にすることはできません。
  • PKの検索条件は完全一致条件のみです。
  • SKの検索条件は完全一致および部分一致による検索が可能です。

シンプルな例(セカンダリインデックスを使用しない)

Primary Keyのみでクエリを行う最もシンプルな検索です。

ブログ記事テーブル (Primary Key:PKのみ)

ArticleId(PK) Category AuthorId Title
A001 Tech U001 Agents for Amazon BedrockがGAしました
A002 Tech U002 DynamoDBもベクター検索に対応
A003 Food U001 東京のラーメン有名店10選

このデータから、「ArticleIdがA001である記事」を取得することを考えます。ArticleIdはPKなので、シンプルにArticleIdがA001である記事を取得できます。

ブログ記事 いいねテーブル(Primary Key:PKとSK)

ArticleId(PK) UserId(SK) LikedAt
A001 U001 2023-08-21T14:15:35.095+09:00
A001 U002 2023-08-27T19:02:01.598+09:00
A003 U001 2023-04-06T17:21:26.776+09:00

このデータから、「ArticleIdがA001である記事」を取得することを考えます。ArticleIdはPKなので、シンプルにArticleIdがA001である記事を取得できます。ただし注意点として、取得するデータはArticleIdがA001であるすべての記事であるため上記の場合は2記事取得されます。ArticleId(PK)/UserId(SK)の両方を指定することで該当する1記事が取得されます。

メリット

  • 検索要件がPrimary Keyの一致のみで満たせる場合はシンプルな構成となる。
  • 追加のインデックスを必要としない。

デメリット

  • Primary Key(PK,SK)を用いた検索しかできない。
  • Scan + Filterで他属性に対しても検索できるがコストパフォーマンスが悪い

ローカルセカンダリインデックス(LSI)を使用

ブログ記事 いいねテーブル(Primary Key:PKとSK)

ArticleId(PK) UserId(SK) LikedAt
A001 U001 2023-08-21T14:15:35.095+09:00
A001 U002 2023-08-27T19:02:01.598+09:00
A003 U001 2023-04-06T17:21:26.776+09:00

このデータから、「ArticleIdがA001で、2023/08にいいねされた」データを取得することを考えます。仕様上から、セカンダリインデックスなしではLikedAtを検索条件にすることはできません。この問題は、LikedAtをローカルセカンダリインデックスにすることで解決できます。

ArticleId(PK) UserId(SK) LikedAt(LSI-1-SK)
A001 U001 2023-08-21T14:15:35.095+09:00
A001 U002 2023-08-27T19:02:01.598+09:00
A003 U001 2023-04-06T17:21:26.776+09:00

この例だと、「ArticleIdがA001で、LSIに"2023-08"(部分一致)が含む記事」を検索できます。繰り返しになりますが、LSIはテーブル作成時にのみ作成可能なので注意です。

メリット

  • Primary KeyのSKに加えてLSI-SKを追加できクエリの柔軟性が向上する

デメリット

  • テーブル作成時にしか作成できない。
  • LSIは1つのテーブルあたりに最大5個まで。

グローバルセカンダリインデックス(GSI)を使用

基本的なGSI使用例

ArticleId(PK) Category AuthorId Title
A001 Tech U001 Agents for Amazon Bedrockが
GAしました
A002 Tech U002 DynamoDBもベクター検索に対応
A003 Food U001 東京のラーメン有名店10選

このデータから、「AuthorIdがU001である記事」を取得したいとします。このままの状態では仕様上、AuthorIdをkeyとして検索することはできません。AuthorIdがU001である記事に対してArticleId(PK)は異なるので、LSIも使用することはできません。この問題は、AuthorIdをPKとするグローバルセカンダリインデックスを作成することで解決できます。

ArticleId
(PK)
Category AuthorId
(GSI-1-PK)
Title
A001 Tech U001 Agents for Amazon BedrockがGAしました
A002 Tech U002 DynamoDBもベクター検索に対応
A003 Food U001 東京のラーメン有名店10選

これにより、GSI-1を用いてAuthorIdがU001である記事を取得できます。

また、GSIにはPKに加えてSKを設定することもできます。

ArticleId(PK) Category AuthorId Title
A001 Tech U001 Agents for Amazon BedrockがGAしました
A002 Tech U002 DynamoDBもベクター検索に対応
A003 Food U001 おすすめレストランがあります

このデータから、「AuthorIdがU001であり、CategoryがTechである記事」を取得したいとします。このニーズは、「AuthorIdをPK、CategoryをSK」とするグローバルセカンダリインデックスを作成することで解決できます。

ArticleId
(PK)
Category
(GSI-1-SK)
AuthorId
(GSI-1-PK)
Title
A001 Tech U001 Agents for Amazon BedrockがGAしました
A002 Tech U002 DynamoDBもベクター検索に対応
A003 Food U001 東京のラーメン有名店10選

これにより、GSIを用いて「AuthorIdがU001であり、CategoryがTechである記事」を取得できます。

メリット

  • Primary Key(PK,SK)に依存せず、新規でPK,SKを作成しそれを用いて検索できる
  • GSIは、テーブル作成後に後から作成可能

デメリット

  • GSIが増えることによって利用料および管理コストが増加。
  • GSIは1つのテーブルあたりに最大20個まで。

GSI OVERLOADING

前述したGSIを利用したテーブル・インデックス設計は、検索要件の増加に応じてGSIが増加するデメリットがあります。例えば、Category、AuthorIdなど各属性に対してそれぞれ検索したい場合は各属性ごとにGSIを作成する必要があります。この問題はGSI OVERLOADINGというスキーマレスなDynamoDBらしいテーブル設計パターンで解決することができます。
具体的には以下のdataValueのようにひとつのGSIに複数の検索要件をもたせることができます。

ArticleId
(PK, GSI-1-SK)
DataType
(SK)
DataValue
(GSI-1-PK)
Title
A001 categorty Tech
A001 authorId U001
A001 title Agents for Amazon BedrockがGAしました
A002 categorty Tech
A002 authorId U002
A002 title DynamoDBもベクター検索に対応
A003 categorty Food
A003 authorId U001
A003 title 東京のラーメン有名店10選

メリット

  • 少ないGSIで複数検索要件を満たすことができ、コストやクエリを最適化できる

デメリット

  • テーブル構成が複雑になる

まとめ

  • サーバレスアプリケーションでは、コストやスケーラビリティの観点からDynamoDBが使いやすい
  • DynamoDBは同時に2つ(PK, SK)のkeyのみを用いてクエリする必要があるため、Primary KeyやLSI/GSIの設計が鍵
  • クエリに使用するKey, Index
    • Primary Key(SK, PK)
    • LSI
    • GSI, GSI OVERLOADING

よいサーバレスライフを!

12
4
0

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
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?