DynamoDB 全くわからないのでまとめてみた


この記事はミライトデザインアドベントカレンダーの 15 日目の記事です。

https://qiita.com/advent-calendar/2024/miraito-inc

前日は @ucan-lab さんの記事でした。

https://qiita.com/ucan-lab/items/f7f010ee2d13ab99203c

ucan さんはよく便利なツールを教えてくれるのですが、便利なツールの情報をどうやって仕入れているのか毎回不思議です。いつも参考にさせてもらっています。

## この記事について

https://aws.amazon.com/jp/dynamodb/

DynamoDB について今まであまり触る機会がなかったので調べてみることにしました。


## 参考

https://dev.classmethod.jp/articles/dynamodb-chottowakaru/

https://qiita.com/_kensh/items/2351096e6c3bf431ff6f

https://www.youtube.com/watch?v=JeVsqphKKEI

https://qiita.com/kiharito/items/17ff7d4eb098c6bf4656

## DynamoDB の特徴

- 完全マネージド型の NoSQL データベースサービス
    - NoSQL の特徴として値とそれを取得するだけの key がある
- フルマネージド（高可用性、スケーラビリティ）
- 高速な読み書き性能（ミリ秒単位の低遅延）
- 無制限のストレージ容量


## テーブル設計

Key-Value store なので key + 値で保存するだけなので設計もなにもないんじゃないかと思っていたけど、かなり違ってた。

RDB データを保存する方法が違うので、設計の考え方もかなり違ってくる。

[DynamoDB の設計について考えてみる。 - Qiita](https://qiita.com/_kensh/items/2351096e6c3bf431ff6f#dynamodb-%E3%81%AE%E8%A8%AD%E8%A8%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
)

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/3c8981bd-3cf4-e38f-5cb5-7cc019060153.png)



- データの配置は Partition Key と Sort Key によって決まる
- Primary Key ( 主キー ) 
    - アイテムを特定する一意の識別子。単一のパーティションキーまたはパーティションキーとソートキーの組み合わせが使用
    - これは RDB のイメージに近い気がするのでわかりやすいかも？
- Item
    - RDB でいうところのレコード
- Attributes ( 属性 )
    - アイテムに格納される値の名前と値のペア
    - 例えば、ユーザーのデータを管理している場合 `name=tanaka` , `age=32` のようなデータが入っているイメージで良さそう

Partition Key 以外は Item 間で不揃いであっても問題ない。


### 例えば、ゲームの例 

**■ ユーザーとその所有物(道具)を保存する必要がある場合**

```
## ユーザー情報
- ユーザーの ID
- 名前

## 所有物
- 所有物の ID
- 誰が持っているか
- 種類(武器？防具？)
```

**RDB の場合次のように考えることが多いはず**


- 「ユーザー」テーブルと「ユーザーの所有物」テーブルを作る
- 「ユーザーの所有物」は所有者がわかるように「ユーザー ID 」のカラムも必要
- 「ユーザーの所有物」は所有物（道具）ごとに固有の情報も必要なので「道具」テーブルも作る

**DynamoDBの場合は次のようになる**

- ユーザー情報とユーザーの所有物の情報は大体同じタイミングで必要になるから同じ Partition Key （近い位置）にしておく

:::note
RDB はデータの意味を考えて設計する。DynamoDB はアクセスパターンで設計する。
:::


テーブル構成はそれぞれ次のようなイメージになるはず

#### RDB の場合

**■ ユーザーテーブル**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/79ce81e3-80e6-aaab-6dad-2a196c37b733.png)

**■ 所有物テーブル**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/3b66f070-a76d-76a9-db7e-7ab8cc472616.png)



#### DynamoDB の場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/243e4316-00ae-af5d-62d9-23fb547218d3.png)

Partition Key はアクセスする可能性が高いユーザーIDで設定する。

**同じ種類の道具を複数のユーザーが持てる仕様が多いはず、その場合は次のようになると思う**


#### RDB の場合

**■ ユーザーテーブル**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/79ce81e3-80e6-aaab-6dad-2a196c37b733.png)

**■ ユーザーの所有物テーブル**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/02d4bb66-8371-f71d-00b9-cb234c92b5a1.png)


**■ 道具テーブル**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/8ee93554-ac57-9057-bc1b-f02e0715b509.png)


#### DynamoDB の場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364773/efaa99c0-8ecb-1594-60e5-f48b67a4a46d.png)

:::note
基本的には DynamoDB は１テーブルでデータを管理する場合が多いらしい。

ただし、アクセスパターンが異なる場合や、構成が複雑になりすぎる場合はマルチテーブルにすることもあるらしい。
:::

## パフォーマンスチューニング

DynamoDB のパフォーマンスが低下する原因は、**クエリの効率**や**実行回数**に起因する。

- Scan コマンドは全件検索なので基本的には NG
- Item １件データ量が多いと通信に時間がかかることがある（ 最大は 400 KB ）
    - 返却する属性を絞ることができる
    - https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html
- Batch でコマンドをまとめて投げることで実行回数を減らすことができる
    - Batch は 100 件のクエリをまとめて投げると 1 件だけ失敗するようなケースも考えられるのでアプリケーション側で失敗していないか確認する必要がある
    - https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/js-aws-appsync-resolver-reference-dynamodb-batch-get-item.html
    - 高速化はほとんど Batch らしい

## Secondary Index

Primary Key（パーティションキーとソートキー）以外の属性を基にしたクエリを行いたい場合に使用する。

- LSI: Local Secondary Index
    - 同じ Partition Key 内で、異なるソート条件を使用してクエリを行うためのインデックス
    - 新たに指定した Sort Key を利用して、Partition Key 内のデータを検索可能


- GSI: Global Secondary Index
    - テーブルの Partition Key と異なる属性を Partition Key として指定し、Partition Key をまたいで検索を行うためのインデックス
    - Sort Key も別の属性を指定可能
    - 元のテーブルとは独立して、柔軟なクエリを実現する仕組み

（ちょっとこの辺りはまだピンと来ていないので、後日実際に動かして確認してみたい。）


## キャパシティユニット

１秒間に処理が行える量


### 読み込みキャパシティユニット (RCU - Read Capacity Unit)

- 1 RCUは、1秒間に最大4KBのデータを強力な整合性のある読み取りが可能
- 結果整合性のある読み取りの場合、1 RCUは1秒間に最大8KBのデータを読み取ることができる


### 書き込みキャパシティユニット (WCU - Write Capacity Unit)
- 1 WCUは、1秒間に最大1KBのデータを書き込むことができる

## キャパシティモード

### オンデマンドキャパシティモード
- 実際に実行された読み取り・書き込みリクエストに対してのみ料金が発生

オンデマンドモードにしている場合でも無制限にリクエストを受け付けられるわけではないらしい？

https://dev.classmethod.jp/articles/dynamodb-max-ondemand-ycsb/


### プロビジョニング済みキャパシティモード
- 事前に読み取り・書き込みのキャパシティユニット数を指定し、指定したユニット数に対して時間課金が発生



## 終わり

（正直急ぎで書いたので、もし間違ってそうなら教えてください。）

明日は @__tomotomon さんが React のライブラリを紹介してくれるらしいです！よろしくお願いします。