【AWS】DynamoDB の仕様まとめ（備忘録）

# はじめに
髙橋愛理です！  :frog:
AWSサーバーレス開発のチームリーダーやってます。

AWSを代表するNoSQL、DynamoDB。
現在行っている開発でメインで使用してます。

DynamoDBを使ったことのある方は、おわかりだと思いますが、**超曲者DB**です。
融通が利かなくて、初めてこいつの設計をした時は、禿げそうになりました…

今回は、仕様や設定が色々多すぎてよくわからなくなるDynamoDBの主な仕様を、備忘録的にまとめてみました。
「そんな機能あるんだ～」と知らなかった機能を知るきっかけになったり、「そういえばそんなのあったなぁ」なんて思いだしたりするのに役立てていただけたら嬉しいです。

※2023年8月時点での情報です。


# 前提
- AWSを使って開発している
- 主キーやインデックスなどDBに関しての知識がある
- DynamoDBに興味がある人
- DynamoDBを使ったことのある人
- DynamoDBの設計に興味がある人

DynamoDBに関する知識や、主キーなどDBの基礎知識が、ある程度ある方が対象の記事となります。

# DynamoDB仕様一覧

## キー設定

### プライマリキー
データを一意に特定するためのキー。
DynamoDBでは、テーブル作成時に、単独主キー（PK）か、複合主キー（PKとSK）を選択する仕様となっている。
テーブル作成後は変更不可。
（※単独主キー、複合主キーは、DynamoDB特有の概念ではない）

### PK（パーティションキー）とSK（ソートキー）
クエリを発行する際に選択できるキー。  
PKのみ、あるいはPKとSK両方を指定し、データを特定する。

### GSI（グローバルセカンダリインデックス）
任意のキーをPK、SKに指定して作成することができ、  
作成すると、作成したGSIでクエリすることが可能になる。
テーブル作成後も変更可能。

### LSI（ローカルセカンダリーインデックス）
テーブル作成時に指定したPKと、任意のキーをSKにして作成するインデックス。
作成すると、作成したLSIでクエリすることが可能になる。
テーブル作成後は変更不可。

### 属性の射影
GSI、LSI作成時に「主テーブルからどのキーをコピーするか」を選択する。
GSI、LSIを使用してクエリした際に、コピーした項目が取得できるということ。

以下が指定可能。
- All（すべて）
- Only Keys（PK、SKのみ）
- Include（PK、SKと任意のキー）

:frog: 💭
<font color="DarkOliveGreen">キーやインデックスの仕様をしっかり理解しながら設計するのがポイントだよ</font>



## 料金

### キャパシティユニット
読み取り書き込みするときに消費される容量。
料金計算に使われる。

### オンデマンド
キャパシテイユニットを指定しないモード。
トラフィックが予想できない時に指定。
リクエストがあった分だけ料金がかかるので、料金は青天井だが、キャパシティユニットのチューニングをしなくてもいい点がメリット高い。  

### プロビジョンド
キャパシテイユニットをあらかじめ指定しておくモード。
トラフィックが予想できる時に指定。
事前に指定したユニット数に応じて料金が決まるので、いくらかかるかがわりと明確だが、指定したユニット数より多くリクエストがあった場合はスロットリングする。

## テーブルクラス

### DynamoDB 標準
アクセスが頻繁なテーブルに適用。
通常はこちらを選択。

### DynamoDB 標準 – IA 
アクセス頻度が低いテーブルに適用。
ストレージ的な使い方をしたい場合に選択。

## SDK
※今回はNode.jsのSDKのドキュメントを参考にしています。

### 単一項目に対しての操作
一つの項目に対しての操作は以下のAPIが用意されている。
- putItem
- getItem
- deleteItem

### 複数項目に対しての操作
複数の項目に対して一度に操作する場合は、以下のAPIが用意されている。
- batchGetItem
一度に最大16MBかつ100個のアイテムまで処理が可能。
- batchWriteIem
一度に最大16MBかつ25個のアイテムまで処理が可能。

いずれも、対象のデータのプライマリキーを渡しての操作になるので、
対象となるデータが特定できない場合は、事前にクエリするなどして取得しておく必要がある。
（※指定した条件のデータを自動で削除するような機能はない）
単一項目用のAPIを使用して一つ一つ連続して操作を行っても、同じことが実現できるが、Batch系APIの方がパフォーマンスに優れている。

### クエリ
指定したインデックスやテーブルに対して、条件を指定してデータを取得する。

PKは必ず指定する必要があり、完全一致のみ。
SKは任意なので指定しなくてもOK、ソートや絞り込みに使用する。

以下の条件を、昇順降順で指定可。
- 完全一致
- SK指定なし（インデックスにSKが登録されている場合は、値が入っているもの全てがクエリの対象となる）
- 指定した値よりも小さい
- 指定した値以下
- 指定した値次より大きい
- 指定した値以上
- 指定した2つの値の間
- 前方一致

### スキャン
頭から全てのデータを取得する。
指定したテーブルやインデックスから大量にデータを取ってくるので、キャパシティユニットをかなり消費する。
フィルターすることもできるが、あくまでデータを一度取得した上でふるいにかける機能なので、フィルターしたデータに対してもキャパシティユニットは消費され、課金の対象となる。
なので、**クエリの代替えで使うのは相当危険** なので、全件取得する要件以外では、基本的には使用しない方がいい。

:frog: 💭
<font color="DarkOliveGreen">マネジメントコンソール上とかで不用意にスキャンをポチってしちゃダメだよ！</font>

### トランザクション
複数の処理を矛盾なく処理すること。
トランザクション処理は以下のAPIが用意されている。
- TransactWriteItems
一度に4MB以内100個まで処理が可能。
- TransactGetItems
一度に4MB以内100個まで処理が可能。

同じAWSアカウントかつ同じリージョン内であればトランザクションAPIを使用し、複数テーブルに対して複数の処理をトランザクションで行うことができる。
また、更新するにあたって、アイテムの存在の有無や特定のキーに対しての条件（ConditionCheck）を含めることができる。 
例えばConditionCheckを「特定のキーの値が○○だったら更新する」という具合に、うまく使用することで、より整合性を担保することが可能。

### アトミックカウンター
increment、decrementの機能。
同時書き込みが発生しても、ちゃんと採番し続けることが可能。
ただし、更新失敗時の再試行の際に、2回更新する可能性があるので、**多少誤差がでても大丈夫なものにのみ使用する**。
**当然のことながら決済系の処理に使用するのはNG**。

:frog: 💭
<font color="DarkOliveGreen">SDKについては細かく書いたらキリがないので、またの機会に</font>

## その他の機能等
### DynamoDB Stream
データのCRUDをトリガーにしてLambda関数を起動する機能。

### DynamoDB Accelerator（DAX）
DynamoDB専用のキャッシュサーバー。
インスタンスベースのサービス。
SDKでCRUDの向き先をDAXに向けるだけでOK、後は裏でDAXが色々自動でやってくれる。

### ポイントタイムリカバリ (PITR)
自動バックアップの機能。
35日前～5分前の間で、**新しいテーブルとして**復元可能。
既存のテーブルのリストアをする際は、新しいテーブルとして復元した後、古いテーブルと切り替えるような作業が必要になる。

### NoSQLワークベンチ
開発を行う際にすごく便利なツール。
向き先をローカルに（Docker）に向けたり、AWSの環境に向けたりすることが可能。
ローカルに向けてクエリを発行して、設計が適切なものか心置きなく試すことができるので、ものすごく役立つ。


# 最後に
ふう…　なんだかもっともっとありそう（いや、確実にある）。。
細かく書こうと思えばもっともっと色々書けるのですが、今回は備忘録ということで、ここまで。