DynamoDBも当初はマネージドとしてシンプルでしたが要求が増えるにつえ複雑になってきました。
*ソフトウェアでは良くあることで、そして新しくシンプルなものが生まれ新しいサイクルが始まる
モデリングなどはドキュメントを見ていただきつつ、この記事では間違えやすい抑えていただきたいポイントを解説します
on-demandモードを使う
初期設定はprovisionedモードになりがちですが固定料金を取られます。また、トラフィックが読めないとき、増減激しいときもあるでしょう
on-demandモードが無難です(2024/11/1から半額になりなおさら)
CDKの場合、dynamodb tableV2を使うと初期設定でon-demandです
provisionedになっているテーブルを探すならAWS Configを使えばアカウント横断で検索できます
provisionedは上級者向け。地味に難しい
かなり利用料金が増えてきて、ワークロードが安定してきたら切り替えると良いでしょう。
provisionedのauto scalingというのもあれば、前払いによる割引もあります
後述にするスロットリングをどう扱うかを含め検討事項は多くなります
CDKならdynamodb tableV2を使う
最新版のconstructです。constructはdynamodb tablev2以外にもしれっとv2が出ていることがあり、時折確認しましょう
batch処理を使う
batch_get_itemなど
batchでの読み込み、書き込みができます。速度面で有利です。
複数件処理では必ずエラーハンドリングを設計しましょう。
tcp keepaliveしておく(特にboto3)
https通信なのでtcp keepaliveしておきましょう(Node.js含め最近は標準設定済みのことが多い)
アプリから見てget_itemが10ms切ってない通信が多いならこれが原因かもしれません
boto3はまだ設定が必要です configオブジェクトを使って設定します
boto3のjson parseが遅すぎる問題
boto3全体に影響あるため修正予定はないようです。boto3自体の問題に加え、Pythonの標準JSONライブラリが遅すぎる件もあります。
自前でparseするか、Pythonをいっそ辞めるかでしょうか
timeoutを入れる
ある程度アクセス数が多いアプリで時折get_itemがタイムアウトになってました。アクセス量が多いなら入れておきましょう
aws内部の通信ならconnect_timeoutの方はせいぜい1秒待って再試行で良いでしょう。初期接続がうまくいかず詰まるイメージ
クオータについて
おおよそのワークロードには何もしなくても対応できますがスパイクには(さすがに)やや弱いです
DynamoDBは分散DBでありテーブル(とGSI)にはパーティションがあります。
ワークロードごとにパーティション単位での読み書きスロットリング制限に達することがあります。
一般に、ホットパーティション対策はDynamoDBに限らずワークロード分析とキー設計が重要です。(本記事で扱う範囲を超えるため割愛します。)
また、on-demandにおけるテーブル(とGSI)単位でのスパイクについては大雑把にいうと30分間隔で自動的に制限が引き上がる仕組みであり、何もしないと30分待ちになる可能性があります
job系の場合SQSなどを挟みゆっくりアクセスするのはありですね。
prewarm機能(warm throughput)
スケーリング作業なしでサポートできるスループット(warm throughput)はコンソールのテーブルの設定から見ることができます。
引き上げリクエストも可能です。こちらは料金が発生します。
自動的に引き上がる分は無料のままで、自分たちで引き上げる場合は料金発生です。
DynamoDBの料金とデータ量
不要GSIの削除や射影の削減は当然として、取り返しが効きづらい点についてです。
データ系は後から修正するのが困難です。一般的な移行コストのほか全上書きによるAWSコストまでかかります。厳しい部分です。
属性のキー名(属性名)の長さとJSONキーの長さ
最初から削っておかないと諦めがちです。
NoSQLは型がないので項目名を繰り返します。非常に勿体なく、省略名を考えると良いですね
customer_id -> cidなど。ソースコード内でわかりやすい名前とマッピングすると良いでしょう。
レコードの生データと量を検討しましょう。属性名のほか、属性値、つまりレコードの中身がJSONの場合も同様にキー名を削りましょう。
UUIDそのまま
UUIDはバイト数をかなり消費します。しかも数値やUUID型ではなく文字列で保存するとさらに増えます。
UUID以外のID体系への切り替えを検討しつつ、例えば文字列なら途中までに絞るというのも一つの方法です。サイズが小さいタイプのランダムIDもあります(言語によって実装違うことも)
データ上書きのコスト構造について
put_itemにせよ、update_itemにせよ、書き込みコストは修正前と修正後のどちらか大きい方で計算されます。つまり、大きい方を丸々書き込んだのと同じコストがかかるイメージです
全レコードについてちょっと修正したつもりが、むしろレコードの中身を大きく削減したつもりが、大惨事になり得ます。
一撃100万!
上書きではなく新しいテーブルを作り移行してコストを下げた事例
移行時のみProvisioned Modeに変更する例
後続へのデータ連携
DynamoDB Export
便利です。ただし、全件exportは大型テーブルならそれなりにコストがかかります。
また、終了時間が必ずしも安定していません。AWSの非同期処理は空いた時間に行われるため、例えば時間制約のある日次ジョブに組み込むと年に数回レベルでの遅延リスクが否定できません。
Kinesis Data Stream + Firehose
特にS3宛でJSONのレコードごとに改行が必要なケースが発生します。
2024年からはFirehoseのProcessorにAppendDelimiterToRecordを設定すればOKです。パラメータも不要。
古い方法だとDynamic PartitioningかLambdaを挟んでの修正が必要でしたが現在は不要です
マネジメントコンソールではnew line delimiterをenabledにします。
テーブル単位でのコストの見方
Cost Explorerにてリソースで集約するとテーブル単位で表示できて便利です。GSI単位で見たいならCloudWatchなどを使いメトリクスから計ります。
スケールしすぎるのが問題?
プロダクトをスケールさせるときにはDBがネックになるのがつきものです。しかしDynamoDBの驚異的なスケーラビリティにより、データストアがボトルネックにならずそのままスケールできてしまいます。
これ自体は喜ばしいことですが、一般にシステムのスケーリングのタイミングで体制全体をスケールさせることを考えると、そのままずるずる進んでしまう可能性があります。 リプレイスのきっかけがなくなるためです。
例えば信頼性、品質、チーム面に手をつけず進み、そのまま崩壊する恐れがあります。プロダクトのスケールにはシステムのスケーリング以外にもたくさんのことが必要です。
その他
よく言われる事例なので手短に
- 通信費が無料のVPC Endpointを設定しておく(Gateway Endpoint)
- DynamoDB Streamsで購読できるのは2本まで
- 複雑ならKinesis Data Streams for DynamoDBも検討
参考リンク
以下も参照ください
最後に
DynamoDBは明示された制約と制限がある分、期待通りに動き続けます。これはエンジニアリング上驚異的なことですが制約と制限は開発者が守る必要があります。用法用量を守りうまく使いましょう。
文責: 高木
おまけクイズ
DynamoDBでput_itemやupdate_itemの書き込みコストはどのように計算されますか?一番正しいものを選んでください。
1. レコードの元データサイズのコストのみかかる
2. 修正後のデータサイズのコストのみかかる
3. 修正前と修正後の大きい方のデータサイズで計算される
4. 属性キー名の長さだけに基づいて計算される
このクイズは生成AIにより作成されました。