DynamoDBを使って1週間で80万円を溶かしてしまった話
(この記事の執筆には生成AIを活用しています)
はじめに
こんにちは。マーケティングオートメーションツール「ListFinder」開発チームのはちです。
この記事では、我々がDynamoDBを使ってアクセスログ処理を効率化しようとした結果、1週間でAWS費用が80万円を突破してしまった失敗談を紹介します。
思い返すだけで恐ろしい話ですが、同じようにDynamoDBを使ってみようとしている方々の参考になればと思い、包み隠さず書いてみました。
アクセスログの処理が重すぎる
ListFinderでは、Webアクセス解析を通じてユーザーの行動を可視化しています。
そのため、ユーザーごとのアクセスログを大量に収集・保存・処理しており、現状はPostgreSQLのテーブルに蓄積しています。
このテーブルは以下のような操作が常に並行して発生しています:
- 新規データの登録
- 条件に合致するデータの抽出
- 処理ステータスの更新
その結果、DBの負荷は常に高止まり。システム全体に余裕がない状態でした。
「DynamoDBならいけるのでは?」
高頻度な読み書き・スケーラブルな処理ができるという理由から、DynamoDBの導入を検討しました。
我々がやりたかったことは以下の通りです:
- アクセスログをDynamoDBに保存
-
status = "pending"
の未処理データを抽出 - 処理後に
status = "done"
へ更新
シンプルに見える処理ですし、「これはDynamoDB向きでは?」と判断。
早速実装に取りかかりました。
ドキュメントとの格闘
ここで最初の壁が立ちはだかります。
DynamoDBの公式ドキュメント、めちゃくちゃ分かりづらい……。
何ができるか
は書いてあるのですが、 どうやってやるか
にはぜんぜん辿り着けない。
(この記事を書くにあたり確認したらAPIリファレンスがありますね。当時はなぜか辿り着けませんでした)
Query?Scan?パーティションキー?ソートキー?セカンダリインデックス?
単語の意味や制約はもちろん、どうやって目的のデータを取得すればいいのかすぐには掴めませんでした。
ドキュメントの海を泳ぎながら、何度も実験と失敗を繰り返し、ようやく動作する実装にたどり着きました。
そしてその中で、「柔軟な条件で抽出できるScanが使いやすそう」と判断したのが運命の分かれ道でした。
悲劇は静かに進行していた
DynamoDBには2つのデータ抽出方法があります:
-
Query
:パーティションキーに基づき高速に検索 -
Scan
:テーブル全体を走査し、条件でフィルタリング
このとき我々は、柔軟に条件を付けられるScanを選択。
開発環境では問題なく動いていたため、本番にもそのまま投入してしまいました。
が、実はこれ、超高コストな選択肢だったのです。
爆速で溶けるRCUと爆上がりする請求書
DynamoDBには CU(Capacity Unit)
と呼ばれる従量課金のコストが存在します。
データ読み込み時にかかる Read Capacity Units(RCU)
とデータ書き込み時にかかるWrite Capacity Units(WCU)
です。
Scanは、テーブルの全項目をチェックしてから条件でフィルタリングします。
アクセスログのような数百万件のデータに対して毎分Scanを実行する構成では、当然ながらがRCUが爆発的に増加。
数日後、社内slackに通知されるAWSのコスト予想を見て愕然としました。
「AWS費用の上がり方がとんでもないんですけど…」
目の前が真っ白になりました。
急いで戻して、苦しみは続く
問題が発覚した後、私たちは急いでリリース前の構成に切り戻す対応を取りました。
ただ、すでに一部のデータはDynamoDBに書き込まれていたため、データの復旧・整合性の確保にもかなりの手間がかかりました。
ログを一件ずつ確認し、足りないものを補完し、状態を整えて……と、まるで手作業のパズルのような作業でした。
また、再発防止のために「どこでどのくらいRCUが消費されるのか」をしっかり試算しようとしたのですが、
その情報を集めるだけでも一苦労。
データ量と処理の起動タイミングや一度の実行にかかる時間などを大量に出力されているログなどから手作業で計算。
結局、十分な検証とコストシミュレーションが終わっておらず、今もまだ再チャレンジには至っていません。
「NoSQL設計ってむずかしいな…」という気持ちを噛みしめながら、今後の設計方針を見直しているところです。
今回の失敗から学んだこと
✅ Queryを使えるキー設計をしよう
- DynamoDBは基本的にKVSなのでRDBのように柔軟な検索には不向き
- 例えば
status#yyyymmdd
のようなパーティションキーを工夫することで条件指定が可能になります
✅ Scanは最終手段(というか避けたい)
- Scanは簡単に書けるが、データ量が多いと即死級のコスト
- 特に定期実行系のバッチには絶対使ってはいけない
✅ DynamoDBのコンソールでRCUを確認できる
- コンソールでQueryやScanを試すと消費されるRCUが表示される
- 本番投入前にざっくり試算するだけでも、大事故は防げるかも
✅ コストアラートはこまめにチェックしよう
CloudWatchなどでコストの確認をすれば被害が小さいうちに気づけるかも
おわりに
DynamoDBは非常にパワフルでスケーラブルなサービスですが、設計を間違えると「気づいたら◯十万円」みたいな事態に陥ります。
我々のような失敗を避けるためにも、
- キー設計をちゃんと考える
- Queryベースでのデータアクセスを前提にする
- ドキュメントに書いていない「運用コスト感」も検証する
といった点をぜひ意識してみてください。
Discussion