DynamoDB知見

# はじめに
この記事は2020年3月31日時点のものです。
どこかで見た情報を適当にメモして、あとから自分の言葉で書き直したりものです。
それでも良い方のみご覧ください。
＊CUやモードについては書いてません。

# DynamoDB知見

## 用語
・項目
　レコードのこと
・属性
　カラムのこと
・スキャン
　対象テーブルの全ての項目を取得すること。

## RDBとNoSQLの違い
・RDB
　柔軟にクエリできるが比較的コストが高い、トラフィックが多いと柔軟にスケールできないことも。正規化重要。
・NoSQL
　特定のクエリを爆速にする代わりにそれ以外は高コストで低速に。

## NoSQLの設計について
・ビジネス上の問題とアプリケーションのユースケースを理解してからスキーマを作ろうね。
・出来るだけ少ないテーブル数にしようね。一つがベスト
・関連するデータをまとめること(非正規化)
　例外として、大容量の時系列データが必要な場合や、データセットのアクセスパターンが非常に異なる場合などがあります。
キーの設計が原因でソートされている場合は、関連項目をグループ化して、効率的にクエリできます。

## パーティションキー
必須。
これが一緒=同じパーティションに置かれる
が、パーティションキーの数だけパーティションが作られるわけではない。
各パーティションへのアクセスがなるべく均一になるようパーティションキーを設計すると良い。
パーティションとテーブルは一対一ではない
昔はhashキーと呼ばれていた。

## ソートキー
必須ではない。
これを設定した=パーティションキーとソートキーの複合プライマリキーにした
これを元にパーティション内でソートされて近くに置かれる（まとめて取り出しやすくなる）
もちろんソート、絞り込みにも使える
昔はrangeキーと呼ばれていた。

## プライマリキー
データを一意に識別するためのキーの呼び名で、「パーティションキー」または「パーティションキーとソートキーの複合キー」のこと。
ドキュメントに出てきたりするが、「パーティションキー」または「パーティションキーとソートキーの複合キー」以上でも以下でもない。
当たり前だが注意点として、複合キーにする（パーティションキーとソートキー両方設定する）場合はその組み合わせで一意でなければいけない。
たとえば、「パーティションキー=1,ソートキー=2」が同じで、それ以外の属性が違う50レコードをいくらput(RDB的に言えばinsert)してもDynamoDBに入るのは1レコードだけ。49回updateされることになる。DynamoDBは基本upsertっぽい。

## セカンダリインデックス

https://qiita.com/shibataka000/items/e3f3792201d6fcc397fd　←とてもわかりやすかったのでこちらから引用
`あるテーブルをベースに、異なるパーティションキー・ソートキーのテーブルを作成する仕組みを グローバルセカンダリインデックス（GSI） 
あるテーブルをベースに、パーティションキーはそのままに、異なるソートキーのテーブルを作成する仕組みを ローカルセカンダリインデックス（LSI） `

LSIはテーブル作成後には作れない

**一般的に、LSIではなく、GSIを使用する**でOK。(ちょっとリンクが見つからなかったけど公式にそう書いてあったはず…)
例外として、クエリに強力な整合性が必要な場合は、LSI。
GSIのクエリは結果整合性のみサポート。

### GSI
GSIでは元テーブルのPK,SKは自動で射影される。
テーブルでは、各キーの値は一意である必要があるが、グローバルセカンダリインデックスのキーの値は一意である必要はない。
PK,SKのどちらかがないレコードはGSIには反映されない

### 射影(Projection)
テーブルに定義された属性をインデックス側にも含めることを「射影」という。
**インデックスのテーブルに属性を追加しておかなければ、インデックスを使ったクエリにそれぞれの属性がふくまれない。**

## クエリ
絶対にパーティションキーを指定して検索しなければならない。
ソートキーの指定は必須ではない。
パーティションキー以外の属性を使って検索したい場合はGSIを使おう。
**クエリは一回1MBまでしか取ってこられない。**
そのためそれ以上の容量のデータをクエリで取得するには再帰的に何度もリクエストすることになる。
そのせいでデータ量が多い場合はクエリが信じられないくらい遅くなる。（RDBで取得に１秒くらいだったものがオンデマンドモードなのにDynamoDBで6秒になってしまった…）

### クエリでソートキーに対して(キー条件式で)できないこと
#### 部分一致検索ができない
関数が前方一致のbegins_withしか使えない。
部分一致検索の関数のcontainsが使えない。
Filterとしてならcontainsが使えるが、Filterにはソートキー、プライマリキーが使えない（含めることすら許されない）
つまり**sortキーに対しての部分一致検索はできない**
さらにキー条件式ではnotが使えないので**not sort_key= hoge検索ができない**

### Limitについて
これは**返すアイテム数の上限指定ではなく、評価する（filter適用前の）件数の上限**。
2万項目あるテーブルに、limit2000とかでクエリすると2000件評価してマッチした5件だけ取得したら残りの1万8000件は評価せず戻ってきてしまう。（こんなlimitは果たして必要なのだろうか…？）

### batchGetItemについて
クエリは一回で１MBまでしか取れないが、これは一回で16MBまで取れる。
ただし、パーティションキーとソートキーしか指定できない（filterできない）し、複合プライマリキーならソートキーが必須となるし、**100アイテムまでしか取得できない**。

### フィルタ適用時のQueryのリザルト
* ScannedCount — フィルタ式 (存在する場合) が適用される前に、キー条件式に一致する項目数。 
* Count — フィルタ式 (存在する場合) が適用された後に残っている項目数。 

## スキャン
大量のCUを使うので基本使わない方が良い。

## 個人的感想
とにかく落とし穴が多く、融通が効かない。
クエリで大量のデータを取って来なければならないのならDynamoDBはやめた方がよいと思う。
もしくはPKの設計をやり直した方が良いが、クエリのことを考えるとそうはいかない。
あと、この辺りは探せばいくらでも出てくると思うが、DynamoDBは「全件取得」にめちゃくちゃ弱い。
DynamoDBの強みはNoSQLであることと、スケールを考えないでもよいところだが、それは結構もろ刃のつるぎだったりする。
個人的にはRDBではデータ取得時に大量にjoinしなければいけないなどの問題がある場合に、書き込みはRDBで、読み込みは非正規化したテーブルを持たせたDynamoDBを置くなどの使い方が良いと思う。

# 参考・引用文献
https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Introduction.html
https://www.ketancho.net/entry/2018/01/30/075500
https://qiita.com/shibataka000/items/e3f3792201d6fcc397fd