Level Up Amazon DynamoDB 参加レポート

今回は2021年6月3日にAmazonさん主催で実施された「Level Up Amazon DynamoDB - オンラインで学ぶAmazon DynamoDBのベストプラクティス」の参加レポートを上げようと思います。

資料はおそらく参加された方だけに公開されているものなので、自分が話を聞いていて気になったところのメモ書きになります。

前半パートは主にDynamoDBの歴史や実際の設計部分について、
後半パートはDynamoDBを実運用する上での情報といった感じのパート分けになってました。

# 前半パート

## 設計思想

Amazonにおける**You build it, You run it.**(あなたが書いたものはあなたが運用しなさい)の原則

→ 運用を限りなく簡単にする

- セキュリティ (パッチ適用や暗号化 etc)
- 耐久性 (サーバ・ラックの維持やバックアップ・リストア etc)
- 可用性 (モニタリングやクロスリージョンレプリケーション etc)
- 性能 (パフォーマンスチューニング・インデックス etc)
- 拡張性 (キャパシティプランニング etc)

などから解放されることで、より柔軟なシステム設計が可能に。

`前者3つのセキュリティ・耐久性・可用性は従来のRDBのマネージドサービスであるRDSでもある程度実現可能でしたが、DynamoDBはそこに加えて後者の横の拡張性がかなり柔軟になっており、拡張しても性能の劣化が起こりにくい構成になっているなと感じました。`

## 歴史

- 2004/12：Amazon.com の拡大に伴うデータベースの拡張性に関する課題
  - RDBの限界・・・
  - データベースを縦ではなく横にスケールしたい
  - クエリの70%は非常に簡単なKey Valueなので別のアーキテクチャを検討→NoSQL
- 2007/10：Dynamoペーパーの公開
  - https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf
  - この段階では実装寄りではなく、学術論文に近い段階
- 2012/01：DynamoDBの一般提供開始
- 現在：Amazonのほとんどを駆動するTier0サービス

`DynamoDBがもう9年も前から一般提供されていることに驚きました。
データベースとして運用されるNoSQLとしてはMongoDB等の名前を以前から聞いたことはありましたが、DynamoDBに関してはここ数年、サーバレスとマイクロサービスの文脈で目にする機会が加速度的に増えたように思います。`


## 高いリクエスト量と低いレイテンシー

参考
https://aws.amazon.com/jp/blogs/news/amazon-dynamodb-auto-scaling-performance-and-cost-optimization-at-any-scale/

![dynamodb-auto-scaling-6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/942208/f6c32764-0549-6a39-927e-604ac3dff6e0.gif)

- 左図のオレンジ：プロビジョニングされたキャパシティユニット(オートスケーリング)
- 左図の青：実際のアクセス量の推移
- 右図の青：同じ時間軸での実際のDBのレイテンシーが何msかの推移

通常のDBであれば負荷がかかるほど遅くなるが、DynamoDBは負荷が高くなるとレイテンシーが減る4ms→2.5msまで減る。
(アクセス数が増えたことで、キャッシュの使用量が上がる or コネクションが維持されやすくオーバーヘッドが減ったなどの理由が考えられるとのこと)

`DynamoDBは負荷がかかっても、上記のようにレイテンシーがむしろ減るような事例が実際のデータにて確認されており、普段RDBの負荷増大に悩まされている身からしては非常に驚きました。`


## DynamoDBのTable構造

- partition key・sort key・dataに分類される
- dataはschema lessなので、どんなデータでも持てる
- グローバルセカンダリインデックス(GSI)
  - 異なるパーティションまたはソートキーを使用できる
  - インデックスはすべてのパーティションキーにまたがる
  - 複合インデックスに複合ソートキーを使用する
- スキーマ内のデータ型を変えたい場合は以下のようなやり方がある
  - 一気に読み込んで一気に処理する
  - 都度古いアイテムを読み込んだ際に判定して1個ずつ書き換える
  - 完全に別の値として追加する(例：created_at → new_created_at) など

## 読み取りオペレーション

以下の3種の読み取り方法がある。

### GetItem

- プライマリーのキーで持ってくる
- 正確に0 or 1個を返す

### Query

- パーティションキーの正確な値とソートキー条件を指定
- 0個以上のコレクションを返す

### Scan

- 非キー属性のフィルタ条件
- テーブルのすべての項目を読み取る

`並列化されてるから時間はそこまでかからないかも知れないが、RCUの消費はかなり気になるところ。
なので、可能な限りフルスキャンを実行しないようにするため、グローバルセカンダリインデックス等を用いて必要なデータのみを持ってこられるようにする必要がある。
自分がDynamoDBについての初学者であまりピンとこなかったので、追加で調べた時のAmazon公式の資料を添付しておきます。`

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GSI.html



# 後半パート

## キャパシティモード

2種類のキャパシティモードを選択可能

- Provisioned Capacity
  - 階段状に増減する方式で、予め最大消費量の枠を確保してその範囲でやりくりする
  - スケーリングは随時可能。オートスケーリングも設定可能

- オンデマンド
  - 使った分のみ課金されるシステム
  - ただし、一定量を超えた際の費用はお高め

参考
https://dev.classmethod.jp/articles/reinvent2018-compare-dynamodb-on-demand-price-with-provisioned-price/

`社内ツールなど、ほぼアクセスされないようなシステムであればオンデマンド、常に一定程度のキャパシティが使用されるシステムであればProvisionedが良い。`

キャパシティユニットを消費しきってしまうとスロットリングが発生してエラーを返してしまうので、オートスケーリングの設定等をしましょう。

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

RCU(リードキャパシティユニット)とWCU(ライトキャパシティユニット)の2種類が存在。
画像データをバイナリ化して保存するなど、巨大なデータを書き込むと1回の読み書きでで複数のキャパシティユニットを消費するので注意しましょう。

## レプリケーション

- 3つのAZに書き込み、3つのAZで実行
- レプリケーションしたあとのGetItemの整合性は比較的とれていて、レプリケーション前のものが読み込まれることは少ない
- グローバルセカンダリインデックスと結果整合性

## スループットの管理

- 書き込みキャパシティーユニット
- 読み込みキャパシティーユニット
- 書き込み・読み込みユニットのAutoScalingもある


## コスト削減

- Attribute名は短くした方がいい
  - DynamoDBは各レコードにKeyとValueを持つため
  - Keyの長さのデータ容量も何十万何百万アイテムも同じものを持つなるとコストになる
  - Attribute名を短くすることにより、1レコードあたりのデータ量が1WCU内に収まると、書き込み時にもコスト削減の意味が出てくる

`今まで多少冗長でもわかりやすいテーブル・カラム名を名付ける方針で実装してきたので、データ量を抑えるためにAttribute名の長さを削減するという発想はなかったので驚きました。`

## バックアップ

- オンデマンドバックアップで継続的バックアップ
- ポイントタイムリカバリ
- 競合はLast Write Win(最後の書き込みが有効)

## セキュリティ

- IAM・VPCによる制御
- KMSによる暗号化
- CroudTrail等による証跡の管理

## NoSQL Workbench

- GUIツール
- データモデルのサンプルも色々(ブックマークサービスや音楽配信サービスなど)
- 手動操作でインデックスを作ったり、ビジュアライザしたりできる
- Commit to DynamoDBで実際のDBに書き込むことも可能

`実際にデータベースを作ったり、中身を見たりをGUIベースで行えるので、学習の補助として非常によさそう。
また、サンプルのデータモデルが何パターンもあるので、設計の参考にもなりそうです。`


# まとめ

- RDBからNoSQLにすることで、かなり設計の方針が変わってくる
  - データの階層化、読み込み方、インデックスの作り方、Attribute名を短くするなど
- 冪等性、結果整合性には注意
- 巨大なDynamoDBのマスターDBを持つのか、各マイクロサービスのバックエンドとして細かく分散したテーブルごとのDBを持つのかなど、設計の方針はどちらもメリット・デメリットがありそう
  - 同じDBに全部突っ込んでもデータ型が柔軟なので、インデックスの作り方によってはそれでも機能しそうです
  - 今までは1つのRDSに全てのテーブル構造と関連を持つのが当たり前だったので、そもそもDBを分割するという発想自体ができるようになったのが大きなパラダイムシフトだと思いました
  - DynamoDBへのレコードの追加をトリガーにLambdaを起動するみたいな使い方もあるので、今後は複数DB前提の設計についても調べてみたいです