![]() |
| Amazon DynamoDB |
はじめに
DynamoDB のホットパーティション。パーティションキーの設計が偏ると、特定のパーティションにアクセスが集中してスロットリングが発生する。。らしいです。DynamoDB について調べていると注意事項として出てきますが、実際に発生するとどんな状態になるのか気になりました。
そこで、架空のコーヒー焙煎メーカー「Kiro Roasters」の在庫管理システムを題材に、意図的にホットパーティションを発生させる検証を行いました。
結論から言うと、この規模の検証では起こせませんでした。
ただ、「なぜ起きなかったのか」を確認する中で、DynamoDB のキャパシティ制御の仕組みと、負荷生成側の設計ミスについて、かなり実践的な知見が得られました。この記事ではその過程と学びを共有します。
Kiro Roasters とは
本記事の検証で使う架空の企業です。AWS Summit Japan でおなじみの serverlesspresso へコーヒー豆を卸す焙煎メーカーをイメージしています。製造メーカーの基幹業務システムを完全サーバーレスでクラウドシフトしていくことが可能なのか、を検証するための題材として設定してみました。
Kiro Roasters の詳細設定
本題には関係ない裏設定的なやつです(クリックで展開)
会社の成り立ち
Kiro Roasters は 2020年に東京で創業したスペシャルティコーヒーの焙煎メーカーです。創業者はもともと大手食品メーカーの生産管理部門出身で、「少量多品種の焙煎豆を、鮮度を保ったまま全国に届ける」をミッションに立ち上げました。
社名の「Kiro」は、コーヒー豆を重さ(キロ)で売買する文化と、焙煎の「火路(きろ)」── 火が豆に伝わる道筋 ── をかけた造語です。実際のところはいつもお世話になっている Kiro ちゃんから取ってます。
ビジネスモデル
B2B(カフェチェーン・レストラン向け卸)と D2C(EC サブスク・ギフト)のハイブリッドモデルを採用しています。売上構成はカフェチェーン向けが60%、EC が25%、業務用卸が15%。
カフェチェーンとの取引では「毎朝決まった時間に届く」が絶対条件のため、朝6〜9時の出荷オペレーションに遅延は許されません。この時間帯に東京倉庫から数十店舗分の焙煎豆が一斉出庫される構造が、今回の検証でホットパーティションを狙うポイントになっています。
倉庫と物流
| 拠点 | 倉庫ID | 出荷比率 | 役割 |
|---|---|---|---|
| 東京本社 | WH-TOKYO | 70% | メイン倉庫。焙煎工場併設。関東圏のカフェチェーン向け出荷の中心 |
| 大阪 | WH-OSAKA | 20% | 関西エリアのカフェ向け。EC出荷の一部も担当 |
| 福岡 | WH-FUKUOKA | 10% | 九州エリア + アジア向け輸出(台湾・シンガポール) |
東京に極端に偏った出荷構造は、急成長期に東京の取引先が増えた結果です。本来なら地方倉庫に分散させるべきですが、焙煎工場が東京にしかないため「焙煎 → 即出荷」の鮮度優先で東京一極集中が続いています。この「業務上の理由で偏りが生まれる」構造は、DynamoDB のキー設計問題と相似形です。
取扱商品(約5,000 SKU)
スペシャルティコーヒーの世界は品種・産地・グレード・焙煎度・容量の組み合わせで SKU が爆発的に増えます。
- 生豆: エチオピア、ブラジル、コロンビア、グアテマラ、ケニア、インドネシア、コスタリカ、パナマの8産地。各産地に2〜3品種 × 2グレード = 約30 SKU
- 焙煎済み豆: 30生豆 × 6焙煎度(ライト〜イタリアン) × 5容量(100g/200g/500g/1kg/5kg業務用)= 約900 SKU
- ブレンド: 自社ブレンド約50種 × 6焙煎度 × 5容量 = 約1,500 SKU
- ドリップバッグ・ギフトセット等: 約500 SKU
- 資材(包装袋、段ボール、ラベル、バルブ等): 約2,000 SKU
- 合計: 約5,000 SKU
人気商品トップ3は「エチオピア イルガチェフェ G1 ミディアムロースト 200g」「Kiro ブレンド シティロースト 500g」「グアテマラ アンティグア SHB フルシティ 1kg(業務用)」。特にエチオピア イルガチェフェ G1 は看板商品で、朝の出荷ラッシュでも出庫件数が最も多くなります。
時間帯別の業務フロー
朝6時。焙煎工場が前日夜に焙煎した豆が倉庫に並ぶ。そこからカフェチェーン数10店舗分の出荷ピッキングが一斉に始まります。
| 時間帯 | イベント | 書き込み負荷 | 読み取り負荷 |
|---|---|---|---|
| 5:00〜6:00 | 焙煎完了品の入庫 | ★★ | ─ |
| 6:00〜9:00 | カフェチェーン向け出荷ラッシュ | ★★★ ピーク | ★★(欠品チェック) |
| 9:00〜12:00 | 仕入れ担当の在庫照会・発注判断 | ★ | ★★★ |
| 14:00〜16:00 | EC出荷(個人向け) | ★★ | ★ |
| 16:00〜18:00 | 在庫補充・倉庫間移動 | ★★ | ★ |
| 月末1日 | 棚卸し(全件スキャン) | ─ | ★★★★ バースト |
最も危険な時間帯は朝 6:00〜9:00。数十店舗分の出庫処理が WH-TOKYO に一斉に書き込みをかけるうえ、そこに営業担当の緊急出庫が割り込んできます。書き込みが1つのパーティションに集中するタイミングです。
なぜコーヒーショップなのか
AWS はワークショップ等でコーヒーショップを事例に使うことが多いです(注文 → 作成 → 受け渡しのイベント駆動パターン等)。Kiro Roasters はそれを「製造メーカー側」に寄せた設定で、仕入れ・焙煎・在庫・出荷という製造業の典型的な業務フローをカバーしつつ、コーヒーという親しみやすい題材で記事の敷居を下げる狙いがあります。
今後の検証シリーズでは、在庫管理だけでなく製造指図(焙煎バッチ管理)、購買(生豆発注の自動化)、会計(原価計算)など、業務システムの各領域に展開していければと思ってます。
命名規則
| 対象 | 形式 | 例 |
|---|---|---|
| 倉庫ID | WH-{CITY} | WH-TOKYO |
| 商品ID | ITEM#{産地}-{品種}-{グレード}-{焙煎度}-{容量} | ITEM#ETH-YIRG-G1-MEDIUM-200G |
| ロットID | LOT#{日付}-{連番} | LOT#2026-05-26-001 |
| 入出庫ID | MOV#{タイムスタンプ}-{連番} | MOV#2026-05-26-08-00-001 |
| 焙煎バッチID | BATCH#{日付}-{AM/PM}-{連番} | BATCH#2026-05-26-AM-01 |
業務システムのデータベース
業務システムの DB といえば多くの場合、RDB(リレーショナルデータベース)が第一候補になると思います。リレーショナルなデータ構造、SQL による柔軟なクエリ、ACID 特性 ── 業務システムに求められる要件との相性は抜群です。
DynamoDB は key-value 型 NoSQL データベースのひとつです。手間なく使えるのでちょっとしたアプリケーションを作る際のデータの入れ物としては重宝しますが、いざ業務システムで活用しようとするともう少ししっかりと特性を把握する必要があります。
では、サーバーレスアーキテクチャを目指すという観点で各サービスを比較してみます。
| 観点 | RDS(サーバーフル) | Aurora Serverless v2 | DynamoDB |
|---|---|---|---|
| DBタイプ | リレーショナルデータベース | リレーショナルデータベース | key-value 型 NoSQL データベース |
| Lambda との相性 | △ コネクション管理が課題 | ○ RDS Proxy 併用で緩和 | ◎ HTTP API、コネクション不要 |
| スケーリング | 手動(インスタンスサイズ変更) | ACU 自動スケール | パーティション自動分割 |
| ゼロスケール | 不可(常時起動) | ◎ 最小キャパシティゼロに対応 | ◎ オンデマンドなら実質ゼロ |
| 運用負荷 | 高い | 低い | ほぼゼロ |
| 複雑なクエリ | ◎ SQL でなんでも | ◎ SQL でなんでも | △ 事前に設計が必要 |
| コスト(低トラフィック) | 高い(常時起動分) | 中程度(スケールダウン可) | 安い |
| コスト(大規模・定常負荷) | 中程度(RI で最適化可) | 高い(ACU 課金が積む) | 中程度(プロビジョンドで抑制可) |
コストは負荷状況に応じて逆転します。Aurora Serverless v2 の利用料金は、同等スペックのプロビジョンドインスタンスより単価が高めに設定されています。
トラフィックが上下する環境ならスケールダウンで元が取れますが、24時間フル稼働の定常負荷ではプロビジョンドインスタンスより高くつきます。「サーバーレスだから安い」とは限らないので、負荷パターンを踏まえた見積もりが必要です。
Lambda + API Gateway でサーバーレスな API を構築する場合、DB だけが「サーバーフル」というのはもったいない。さらに DynamoDB なら HTTP ベースでコネクション管理が不要、オンデマンドモードならトラフィックに応じた完全従量課金が実現できます。
もちろん、「全部 DynamoDB でいける」わけではありません。複数テーブルの JOIN や集計が必要になる月次処理や、厳密な ACID が複数エンティティに跨る会計仕訳などのケースでは、従来通りのリレーショナルデータベースの方が適しています。
一方で DynamoDB が得意なのは、キーを指定した単一アイテムの読み書きです。パーティションキーとソートキーを指定した GetItem / PutItem / UpdateItem はミリ秒オーダーで返ります。アクセスパターンが事前に明確で、1回のリクエストで1件〜数件のアイテムを操作する用途 ── たとえば在庫の入出庫や在庫照会のようなケースでは、RDB より高速かつスケーラブルに動作します。
ただし、DynamoDB にはパーティションキーの設計次第で性能が大きく変わる特性があります。 設計を誤ると特定のパーティションにアクセスが集中する「ホットパーティション」が発生します。
ホットパーティションとは
DynamoDB のパーティション分散
DynamoDB のテーブルには必ずパーティションキー(PK)を設定します。NULL 不可・ユニーク制約ありで、RDB でいう主キーの役割です。オプションでソートキー(SK)を追加でき、PK + SK の組み合わせが各アイテムを一意に識別します。
DynamoDB はこのパーティションキーのハッシュ値に基づいて、データを複数の物理パーティションに分散配置します。リクエストが来ると、PK のハッシュ値から対象パーティションを一発で特定してアクセスするため、データ量が増えてもレイテンシが劣化しません。これがミリ秒オーダーの応答を実現している仕組みです。
ただし、各物理パーティションにはスループットの上限があります。
- 書き込み: 1,000 WCU/秒/パーティション
- 読み取り: 3,000 RCU/秒/パーティション
- ストレージ: 10 GB/パーティション
WCU(Write Capacity Unit)は書き込みのスループット単位で、1 WCU = 最大1KBのアイテムを1秒に1回書き込める容量です。RCU(Read Capacity Unit)は読み取りの単位で、1 RCU = 最大4KBのアイテムを強い一貫性で1秒に1回読み取れます(結果整合性なら2回)。
このキャパシティを確保する方法は2つあります。
| モード | 特徴 | 課金 |
|---|---|---|
| オンデマンド | 負荷に応じて自動スケール。事前設定なし | リクエスト単位 |
| プロビジョンド | RCU / WCU を自分で指定する | 確保した容量 × 時間 |
通常の業務システムならオンデマンドが無難です。トラフィックが読めなくても勝手に追従してくれます。ただし今回の検証ではキャパシティを意図的にコントロールしたいので、プロビジョンドモードを使います。オンデマンドだと DynamoDB が裏で容量を上げてしまい、スロットリングの条件を自分で作れません。
なお、パーティション単位の上限(1,000 WCU / 3,000 RCU)はどちらのモードでも共通です。オンデマンドにしてもホットパーティションは回避できません。
たとえばプロビジョンドモードで WCU=5,000 を設定した場合、DynamoDB は内部でおおよそ5つの物理パーティションにキャパシティを分配します(実際の物理パーティション数は AWS が内部で決定)。
テーブル全体: WCU = 5,000
→ 物理パーティション A: 1,000 WCU
→ 物理パーティション B: 1,000 WCU
→ 物理パーティション C: 1,000 WCU
→ 物理パーティション D: 1,000 WCU
→ 物理パーティション E: 1,000 WCU
テーブル全体では 5,000 WCU/秒を処理できますが、1つのパーティションに集中すると 1,000 WCU/秒で頭打ちになります。残りの4パーティションが空いていても、その余剰は即座に使えません。
テーブル全体のキャパシティに余裕があっても、1つのパーティションキーにアクセスが集中すると、そのパーティションの上限に達してスロットリングが発生します。これがホットパーティションです。
スロットリングとは、DynamoDB がキャパシティを超過したリクエストを受け付けず、エラー(ProvisionedThroughputExceededException)を返すこと
今回の検証テーマ: Bad 設計 vs Good 設計
架空のコーヒー焙煎メーカー「Kiro Roasters」の在庫テーブルで、わざと悪い設計をしてホットパーティションを引き起こしてみます。
Bad 設計(カーディナリティ = 3):
PK: warehouseId → "WH-TOKYO" / "WH-OSAKA" / "WH-FUKUOKA"
SK: itemId → "ITEM#ETH-YIRG-G1-MEDIUM-200G" など
パーティションキーに warehouseId を設定します。現状倉庫が3拠点しかないので、全データが3つのパーティションキー値に集約されます。東京倉庫が出荷の70%を担うため、WH-TOKYO に書き込みが集中します。
Good 設計(カーディナリティ = 5,000):
PK: itemId → "ITEM#ETH-YIRG-G1-MEDIUM-200G" など(約5,000種類)
SK: warehouseId → "WH-TOKYO" / "WH-OSAKA" / "WH-FUKUOKA"
パーティションキーに itemId を設定します。通常使われるような設計です。
約5,000種類の商品 ID をパーティションキーにすることで、書き込みが自然に分散されます。
検証環境
アーキテクチャ
バッチ処理だけでも検証することは可能でしたが、せっかくなので在庫管理システムの必要最小限の機能を組み込んだオンライン画面を Amplify Hosting で立ち上げてみました。
技術スタック
- インフラ: AWS Amplify Gen2(sandbox 環境)
- フロントエンド: Next.js
- バックエンド: Lambda (Node.js 20.x) + API Gateway
- DB: DynamoDB(プロビジョンドモード)
- データ量: 15,000レコード(5,000 SKU × 3倉庫。各倉庫に均等に5,000件ずつ投入)
- 負荷テスト: Lambda 内から DynamoDB SDK を直接呼び出す方式
機能一覧
在庫管理
在庫管理システム全体の画面構成はこのような感じです。一つの画面上で、個別在庫照会、出庫リクエスト、在庫一覧の確認ができるようになっています。

在庫照会機能です。商品IDをインプットして照会ボタンで、個別の在庫状況を取得できます。読み取りになるので DynamoDB では RCU が関係してきます。

各店舗からのマニュアルでの出庫リクエストを処理する機能です。倉庫 x 商品ID で対象を決めて数量を指定して出庫処理を実行します。処理に成功したら"出庫完了"のメッセージが表示されます。

負荷テスト
ホットパーティションを発生させるための負荷テストを実行します。各種パラメータを変更可能。

負荷テストを実行中に、在庫管理の出庫処理を定期的に自動実行する「オンライン影響テスト」を起動します。

結果ダッシュボード
各テーブルでの「オンライン影響テスト」結果を比較するためのダッシュボード画面。

検証の進め方
では、本題である検証内容の説明に入ります。
想定シナリオ
朝7時、カフェチェーン向けの出荷ラッシュが始まる。東京本社倉庫から数十店舗分の焙煎豆が一斉に出庫される。
同じ頃、営業担当が急ぎの追加出庫をかけようとする。「新規カフェから今日中に5kg欲しいって連絡来たから出庫して」
...エラー。出荷ラッシュの大量書き込みが WH-TOKYO パーティションの WCU 上限を使い切っていて、追加の出庫操作が弾かれている。
── というストーリーを再現したかったのですが、うまくいきませんでした。
ゴールと成功条件
やりたいことは1つです。Bad Table のパーティションキー WH-TOKYO に 1,000 WCU/秒を超える書き込みを持続的に叩き込み、スロットリングを発生させること。正しい設計の Good Table にも同様の負荷を与えて、結果を比較します。
成功と言えるのは以下の状態です。
| 条件 | 内容 |
|---|---|
| Bad Table でスロットリング発生 | WH-TOKYO への集中でパーティション上限を超過 |
| Good Table ではスロットリングなし | 5,000キーに分散してあるので上限に届かない |
| テーブル全体には余裕がある | 「容量不足」ではなく「偏り」が原因だと言える状態 |
3つ目が重要です。テーブル全体の容量を絞ってスロットリングさせても、それは単なるキャパシティ不足でホットパーティションではありません。テーブルには余裕があるのに特定パーティションだけが詰まる状態を作る必要があります。
検証内容
負荷テスト用パラメータ
以下のパラメータを変更することで、DynamoDB に与える負荷を変化させて状態を確認します。
| パラメータ | 役割 |
|---|---|
| WCU | テーブル全体の容量。物理パーティション数にも影響する |
| 負荷 RPS | 1秒あたりの書き込みリクエスト数 |
| 継続時間 | 継続的にパーティション上限を超え続ける状態を作るための時間 |
| 倉庫分布 | リクエスト生成時の出庫先倉庫の割合。東京に集中させる |
負荷のかけ方
負荷テストの管理画面から上記パラメータを指定すると、Start Lambda が Worker Lambda を非同期で起動します。各 Worker が担当分の RPS を並列で UpdateItem(在庫数の減算)に変換して DynamoDB に投げます。リクエストは倉庫分布のパラメータに従ってランダムに発生させます。
[管理画面] → [Start Lambda] → [Worker Lambda × N] → DynamoDB
各 Worker が担当 RPS 分を並列実行
併せて負荷テストの実行中に、在庫管理画面から手動で出庫操作を行い「業務ユーザーがエラーを踏むか」も確認します。
検証結果
検証① WCU=100
当初の想定では、Bad Table は PK が3種類(WH-TOKYO / WH-OSAKA / WH-FUKUOKA)なので、物理パーティションも3つに分かれるはず。100 WCU ÷ 3 = 各パーティション約33 WCU。そこに 50 req/s の 70% = 35 WCU/秒 が東京に集中すれば、33 を超えてスロットリングが起きるのでは?と考えました。検証にコストも嵩むので必要最小限を狙った形です。
負荷テストの条件
| パラメータ | 値 |
|---|---|
| テーブル | Bad Table |
| WCU | 100 |
| 負荷 RPS | 50 req/s |
| 継続時間 | 60秒 |
| 倉庫分布 | 東京70% / 大阪20% / 福岡10% |
結果
スロットリングは発生せず
| 指標 | 値 |
|---|---|
| 総リクエスト | 3,000 |
| 成功 | 3,000 |
| スロットル | 0 |
| スロットル率 | 0% |
| 処理時間 | 71秒 |
原因
「物理パーティションが分かれていない」ためでした。
計算してみます。アイテムサイズは約 200バイト(テーブル全体 約3MB ÷ 15,000件)。1KB 未満なので 1書き込み = 1 WCU となります。
実効スループットは 42 req/s(3,000件 ÷ 71秒)で、東京70%集中だと約 35 WCU/秒 が WH-TOKYO に集中しています。ここまでは想定通りです。
ではなぜスロットリングが起きなかったかというと、WCU=100 程度では物理パーティションはおそらく1つしか割り当てられていないためです。パーティションが1つということは、全データが同じ場所にあり「特定パーティションに偏る」という状態自体が発生しません。テーブル全体の設定値 100 WCU に対して 42 WCU/秒しか使っていないので、単純にスロットリングが起きる理由がありません。
「パーティションキーの値が3種類 = 物理パーティションが3つ」ではありませんでした。物理パーティションの数はパーティションキーの種類数ではなく、WCU / RCU / ストレージから総合的に決まります。
次の一手
パーティションが1つしかない状態では、ホットパーティションの検証にならないことが分かりました。ただ、まずは「スロットリングが起きる」状態を確認して Bad / Good の差を見たいと考え、逆のアプローチを取ることにしました。
テーブルのプロビジョン容量を極端に下げれば、少ない負荷でもスロットリングが起きる。そのとき Bad と Good で差が出るのか?
・・・コストをケチって減らす方向ですねw
検証② WCU=5
容量を絞ってスロットリングを強制する
条件
| パラメータ | 値 |
|---|---|
| テーブル | Bad Table / Good Table 両方で実施 |
| WCU | 5(100 から大幅に削減) |
| 負荷 RPS | 10 req/s |
| 継続時間 | 10秒 |
| SDK リトライ | 無効(maxAttempts: 1) |
SDK のリトライを切っているのは、スロットリングが起きたことを確実に検出するためです。
結果
スロットリングは出たが Bad/Good に差がない
| テーブル | 総リクエスト | 成功 | スロットル | スロットル率 |
|---|---|---|---|---|
| Bad Table | 100 | 50 | 50 | 50.0% |
| Good Table | 100 | 46 | 54 | 54.0% |
スロットリング自体は発生しました。しかし Bad と Good で差が出ていません。
むしろ Good の方がわずかに高いという結果です。
原因
今回は「テーブル容量不足によるスロットリング」が発生
検証①と同じく、WCU=5 でも物理パーティションはおそらく1つのままです。Bad / Good どちらも全データが同じパーティションにあるので、キー設計の差は出ようがありません。
加えて、今回は成功条件の3つ目「テーブル全体には余裕がある」を満たしていませんでした。テーブル設定 5 WCU に対して 10 req/s を投げているので、キー設計に関係なく容量不足でスロットリングします。これは「ホットパーティションのスロットリング」とは別の問題です。
次の一手
容量を絞るアプローチは筋が悪いことが分かりました。物理パーティションを複数持つ状態を作り、そこに大量の負荷をかけるしかありません。(当たり前かな?)
WCU を大きくしてテーブルを新規作成すれば、初期割り当てで物理パーティションが複数に分かれるはずです。
検証③ WCU=10000
投入データは同じですが、物理パーティションを増やして大量負荷をかけてみます。
条件
WCU=10000 でテーブルを新規作成しました(初期割り当てで物理パーティションの分割を狙う)。テーブル全体には十分な余裕を持たせた上で、東京への集中で局所的に上限を超えさせます。本来やりたかった形となります。
| パラメータ | 値 |
|---|---|
| テーブル | Bad Table(WCU=10000 で新規作成) |
| WCU | 10,000 |
| 負荷RPS | 1,500 req/s |
| 継続時間 | 120秒 |
| 倉庫分布 | 東京70% / 大阪20% / 福岡10% |
非同期の Worker Lambda は 3 個起動する想定です。各 500 req/s。
1,500 req/s の 70% が東京に集中すれば、1,050 WCU/秒 となり、パーティション上限の 1,000 WCU/秒 をわずかに超過する計算です。
結果
またもやスロットリングなし
| 指標 | 値 |
|---|---|
| 総リクエスト | 180,000 |
| 成功 | 180,000 |
| スロットル | 0 |
| 実処理時間 | 約380秒(設定120秒に対して) |
| 実効 RPS | ~480 req/s |
負荷テスト実行中に手動で出庫操作もしてみましたが、こちらも正常に完了。エラーは一切出ませんでした。
原因
狙った RPS が出ていない
設定 1,500 req/s に対して、実効スループットが ~480 req/s しか出ていません。
実効 480 req/s × 東京70% = 336 WCU/秒。
つまり、パーティション上限 1,000 WCU/秒 に対して 34%。
負荷生成側が指定した RPS を出せていないことが問題でした。
おまけ
ここまでの検証の中でもいくつか仮説を立てながら進めてきました。
結果的に見当違いではありましたが、今後 DynamoDB を取り扱う上で重要になりそうな学びもありましたのでここで紹介します。
バーストキャパシティとアダプティブキャパシティ
検証の過程で混乱したのが、DynamoDB のキャパシティ制御に関する2つの仕組みです。
これらの調整機能が効いたことによりスロットリングが発生しなかったのではないかと考えました。
バーストキャパシティとは
DynamoDB は、プロビジョンドモードで使い切らなかったキャパシティを最大300秒分まで「貯金」しておいてくれます。一時的にプロビジョン設定値を超えるリクエストが来たとき、この貯金から支払って処理してくれる機能です。
たとえば WCU=100 のテーブルが普段 20 WCU/秒しか使っていなければ、余った 80 WCU/秒 × 300秒 = 24,000 WCU 分が蓄積されます。突然 200 WCU/秒のスパイクが来ても、この貯金がある間は処理できます。
ただし注意点が2つあります。
- バーストキャパシティは「保証」ではなくベストエフォート。使えないこともある
- パーティション単位の上限 1,000 WCU/秒を超えることはできない。あくまでプロビジョン設定値の超過を一時的にカバーするだけ
アダプティブキャパシティとは
テーブル全体のプロビジョンが 10,000 WCU でも、物理パーティションが20個あれば各パーティションに割り当てられるのは本来 500 WCU ずつです。でも実際の負荷はパーティションごとに偏ります。
アダプティブキャパシティは、使っていないパーティションの余剰を、忙しいパーティションに自動的に融通する仕組みです。あるパーティションが割り当て分の 500 WCU を使い切りそうなとき、隣のパーティションが 200 WCU しか使っていなければ、余った分を回してくれます。
こちらも上限は絶対です。アダプティブキャパシティがどれだけ融通しても、1つの物理パーティションが使える WCU は 1,000 を超えられません。AWS のドキュメントにも明記されています。
Instant adaptive capacity can still never cross the limit of 3,000 capacity units of reading and 1,000 capacity units of writing for each partition.
— AWS Startups Blog
スロットリングの理由を切り分けるメトリクス
分析を進める中で、CloudWatch にはスロットリングの理由別メトリクスが存在することもわかりました。
| メトリクス | 意味 |
|---|---|
WriteProvisionedThroughputThrottleEvents |
テーブル全体の容量不足によるスロットリング |
WriteKeyRangeThroughputThrottleEvents |
ホットパーティション(1,000 WCU 超過)によるスロットリング |
これを見れば「テーブル容量が足りないのか」「特定パーティションに偏っているのか」を推測なしで切り分けられます。
検証②で観測した50%のスロットリングは前者に起因しており、完全に単なる容量不足でした。当時は WriteThrottleEvents(両方の合計)だけを見ていたため、この区別ができていませんでした。
まとめ
今回の DynamoDB 検証ではホットパーティションを起こしたいけど起こせない。という残念な結果に終わってしまいました。
今回の学びを反映した負荷生成基盤を作り直し、想定通りの RPS を叩き込めるようにした上で再挑戦します。
- 負荷生成コードの構造を改善して実効 RPS を引き上げる
- ワーカーを細分化して並列度を上げる(Lambda のままで到達できる見込み)
- コストをケチらず規模を上げる
-
WriteKeyRangeThroughputThrottleEventsでホットパーティションを直接確認する
特にコストはケチらず行こう!


