DynamoDBの最適なキャパシティモード選択を考える

DynamoDBはRDSと比べて非常に安価で実務でも個人開発でも非常に使いやすいDBサービスです。
ですがテーブルの特性に合ったキャパシティ設定を設定しないと、実はもっと安く利用できたかも知れないチャンスを逃す事になります。
DynamoDBのコストをもっと抑えたいと思っている方は参考にしてみてください。

なお、今回は

- 東京リージョン
- テーブルクラスはスタンダード

の前提で記載します

## STEP1: 現状把握
まずは現在運用しているキャパシティモード、キャパシティメトリクスなどを確認して現状を把握します。

- キャパシティモード
    - 支払い方法はオンデマンド or プロビジョンドがあり、料金体制が異なる
- キャパシティメトリクス
    - 読み取りキャパシティ、書き込みキャパシティが現状どれほどあるのか
    - 時間毎のメトリクスグラフの挙動、グラフの波が安定しているか

これらを確認して各テーブルの特性をまずは把握する事が大切です。

まだ運用した事が無い場合、まずはオンデマンドモードで1ヶ月程運用してみる所から始めてください。

## STEP2: キャパシティモードの仕様を確認する

DynamoDBには支払い方法を設定するキャパシティモードという概念があります。
キャパシティモードには
- オンデマンド
- プロビジョンド

の2つがあるので仕様などを確認しましょう。

### 特徴
- オンデマンド
    - 利用したキャパシティ分だけ従量課金で支払い
    - 急激なスパイクがある場合や、アクセスの頻度が予測困難な場合に最適なモード
- プロビジョンド
    - 安定したアクセスが予想できる場合に最適なモード
    - 最大キャパシティ容量を超えた場合はスロットリングが発生してDynamoDBへのアクセスに失敗する
        - 過去5分の間に使われなかったキャパシティを繰り越せるバーストキャパシティという仕組みがあるので、多少なら問題ない
    - オートスケーリングが設定可能
        - 設定する事のデメリットはあまり無いので設定がおすすめ
        - 強いて言えば即応性は無いのであまりにも急激なスパイクの場合はバーストキャパシティをも貫通してスロットリングが起きてしまう事がある。
        - 過去のメトリクスを見て急激なスパイクが来る可能性を考慮しておく

### 料金
- オンデマンド
    - 利用したキャパシティ分だけ従量課金で支払うので書き込み（WRU）、読み込み（RRU）のリクエスト単位で支払いが発生します。
    
    | オンデマンドスループットタイプ | 料金 |
    | --- | --- |
    | 書き込み要求単位 (WRU) | 書き込み要求ユニット 100 万あたり USD 0.715 |
    | 読み出し要求単位 (RRU) | 読み出し要求ユニット 100 万あたり USD 0.1425 |
- プロビジョンド
    - 1時間あたりで使うキャパシティユニット（CU）を事前に割り当てて、その分だけ支払いが発生します。
    - キャパシティユニット
        - 読み込みキャパシティユニット (RCU)
            - 4KB以下の項目に対して1秒あたり2回の結果整合性のある読み込み性能
            - 強力な整合性のある読み込みについては1秒あたり1回(単純に結果整合性の2倍かかる)
        - 書き込みキャパシティユニット (WCU)
            - 最大サイズが 1 KB の項目に対して1秒あたり1回の書き込み性能
    
    | プロビジョン済みスループットタイプ | 1時間あたりの料金 |
    | --- | --- |
    | 書き込みキャパシティーユニット (WCU) | USD 0.000742/WCU |
    | 読み込みキャパシティーユニット (RCU) | USD 0.0001484/RCU |

### 比較

オンデマンドで書き込み、読み込みで月に100万ユニットの消費があった場合に発生する料金と、その料金をプロビジョンド(オートスケーリングなし)で発生させるにはどれ位のユニット消費が必要なのか比較します。

（書き込み、読み込みでそれぞれでキャパシティモードは選択できないので、合算して試算）

- オンデマンド
    - 書き込み、読み込みで月に100万ユニット消費時の料金
        - **$0.715** + **$0.1425** = **$0.8575**
- プロビジョンド
    - 書き込み、読み込みで1時間毎の料金
        - **$0.000742** + **$0.0001484** = **$0.0008904**
    - 1か月に合わせる
        - **$0.0008904** * 24h * 30day = **$0.641088**/month
- 比較
    - **$0.8575**(オンデマンド料金) ÷ **$0.641088**（プロビジョンド料金）= 約1.34 cu

という事で指標としては

- **オンデマンド:** 1時間あたりの平均CU消費が **1.34以下** の場合に効率的。
- **プロビジョンド:** 1時間あたりの平均CU消費が **1.34を超える** 場合に効率的。

グラフにするとこうなりました。（chatGPT産）
![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3807740/eda24c0e-1b0e-bfaf-8c11-2319de3630ef.png)


## STEP3: テーブルのメトリクスを見てみる

ここまで現状把握とキャパシティモードの違いを把握できたら実際に運用しているテーブルを確認します。

例えばこの様なメトリクスのテーブルの場合を考えてみます。

- キャパシティユニット使用量平均（1時間）
![スクリーンショット 2024-12-12 11.45.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3807740/53041366-5ab1-4ad0-c768-51c43ce9c5db.png)

    
- キャパシティユニット使用量最大（1時間）
![スクリーンショット 2024-12-12 11.46.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3807740/2604131b-50bd-cabd-4f70-4a94dabd7ca3.png)

この様なメトリクスはそこまで急激なスパイクも無い、ある程度アクセスが緩やかなのでプロビジョンドモードを検討します。

プロビジョンドモードでは最大キャパシティのオートスケーリングが設定可能で、基本的に設定する事でのデメリットは少ないので設定しておきましょう。

オートスケーリングでは下記の項目を設定しました。

- 最低容量：10
    - 過去の使用量平均の最大の半分を目安に設定
- 最大容量：1000
    - オートスケーリングを設定する場合、最大容量は絶対超えないであろう数値を設定しておく
- ターゲット使用率：70%
    - 最大容量が設定値のXX%を維持する様にスケーリングしてくれる
    - 例えば現在のキャパシティ使用量が100で、70％で設定すれば100/0.7=140くらいを維持するようにスケーリングします
    

## おわり

以上の要領で設定することで前後でキャパシティ容量を大幅に削減できました。

- 書き込み：**$11.37** → **$2.62**（約 **76.96%** 削減）
- 読み込み：**$2.3** → **$0.69**（約 **70.00%** 削減）

（10/21と10/23を比較）
![スクリーンショット 2024-12-12 18.14.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3807740/8f46f38f-0970-e05b-a78c-5c3f6754eac5.png)

設定を変更した後は後続のメトリクスの推移をこまめに監視して更なる最適化を進めていきましょう。
（計算の間違いやご指摘などあれば頂ければ嬉しいです）