DynamoDB のホットパーティション:見つけ方と直し方
DynamoDB は、それぞれが独自のスループット枠を持つ多数の物理パーティションにデータを 分散します。ホットパーティション とは、1つのキーが自分の枠でさばける以上の読み取りや 書き込みを大量に引き寄せることです — その結果、そのキーへのリクエストがスロットルする 一方で、テーブルの残りは遊んでいます。
DynamoDB のホットパーティションとは?
DynamoDB のホットパーティションとは、1つの が、 そのスループット枠でさばける以上の読み取りや書き込みを大量に吸収し、そのキーへの リクエストがスロットルする一方でテーブルの残りが遊んでいる状態です。原因はテーブルの サイズではなくキー設計 — セレブアイテム、カーディナリティの低いキー、今日の日付 — です。 対処は書き込みを分散させることです。
- 原因はキー設計であって、テーブルサイズではありません。 1つの
にトラフィックが集中すること — 人気ユーザー、
status="OPEN"フラグ、今日の日付 — が罠です。 - アダプティブキャパシティは助けにはなりますが、解決策ではありません。 DynamoDB は 熱を自動的に再分配しますが、それでも単一のアイテムや単一のキーが、1つのパーティションで さばける量を超えることがあります。
- 対処は書き込みを分散させることです。 キーにエントロピーを加える(ライトシャーディング)か、 ホットな読み取りパスを、より分散したアクセスパターンへ移します。
- SQL から来ると、これに相当するものがありません。 リレーショナルテーブルには 「ある行のインデックス値が人気すぎる」という概念がありません — DynamoDB の キーごとにフラットなスループットというモデルにはあるのです。
そもそもなぜパーティションが存在するのか
DynamoDB は、単一ノードの SQL モデルをパーティション分割された水平スケールのモデルへと 交換した、2007年の Amazon Dynamo 論文の実運用上の後継です。データは パーティションキー のハッシュによって、物理ストレージノード全体にシャーディングされます。
各パーティションは有限量のデータを保持し、有限量のスループットをさばきます。AWS は、 1パーティションあたり毎秒 3,000 の読み取りユニットと 1,000 の書き込みユニット という ハードな上限を文書化しています (AWS — パーティションの挙動)。 課金モードを変えてもこの物理的な上限は上がりません。変わるのは、テーブルレベルの支出がどう 計測されるかだけです。テーブルレベルのコストには料金計算ツールを、 スロットルがパーティション起因かテーブル起因かを見るには Contributor Insights を使ってください。
その上限がすべてを物語ります。テーブルの スループットは、 全パーティションにわたる合計です。あるキーのアイテムコレクションは1つのパーティションで 始まり、熱による分割(split-for-heat)がそれをソートキーの境界で複数に切り分けることが できます — ただし、テーブルに LSI があるか、ソートキーが単調増加している場合は、 1つのパーティションに固定されます。
罠に名前をつける: 1つのキーに積み上がるトラフィック
スループットが均等に共有されるのは、アクセスがキー全体に均等に分散している場合に限り ます。1つのキーが不均衡なトラフィックを受けた瞬間、そのキーだけがスロットルし、 テーブル全体のキャパシティは使われないまま残ります。
典型的なホットキーの形:
- セレブアイテム — 誰もが読む1人のユーザー、1つの商品、1つのテナント。
- カーディナリティの低いパーティションキー —
status、country、type。 取りうる値が少ないと、すべての仕事をこなすパーティションも少なくなります。 - 時刻でバケット化したキー —
PK = "2026-06-23"。今日の書き込みはすべて1つの パーティションを叩き、昨日のパーティションは永遠に冷たいままです。
SQL から来ると、これらはどれも問題になりません。人気の値に対する B-tree インデックスは 問題ないのです。DynamoDB では、人気の値 そのもの が物理配置の単位なので、人気が スループットの崖になります。
具体例: セレブのリーダーボード
グローバルなゲームのリーダーボードを運用しているとします。スコアは次のようにキー付けした テーブルに入っています。
PK = "BOARD#global"
SK = "PLAYER#<playerId>"
読み取りはスコア上位 N 件を取得し、書き込みは各試合後にプレイヤーの currentScore を
更新します。グローバルボードのすべての行が 1つの パーティションキー — BOARD#global
— を共有するため、あらゆる読み取りと書き込みが単一のパーティションに当たります。
そこに、自分のランクの更新ボタンを連打する200万人のライブ視聴者を抱えた配信者を加えると、
その1つのパーティションは 3,000 読み取りユニットを超えます。テーブル内の他のあらゆる
ボードが遊んでいる一方で、グローバルボードでは
ProvisionedThroughputExceededException が返ってきます。
自爆装置は BOARD#global への集約です。1つの論理的なボードを、1つの物理的なキーとして
モデル化してしまったのです。
書き込みを分散する: キーのシャーディング
対処は、カーディナリティを人為的に作り出すことです。パーティションキーに シャード サフィックス を付け、1つの論理ボードを N 個の物理パーティションにファンアウトさせます。
PK = "BOARD#global#<shard>" -- shard = playerId mod 10
SK = "PLAYER#<playerId>"
書き込みは1つではなく10個のパーティションに散らばるようになりました — 書き込みの余裕が
10倍です。代償は、ボード全体の読み取りが10個すべてのシャードに当たってマージする必要が
あることです。単一の Query はシャード境界をまたげないからです。読み取りの単純さを、
書き込みの分散と引き換えにするわけです。
自分で違いを確かめてみてください。下のビジュアライザに、同じキーを繰り返して貼り付けると、
すべての書き込みが1つのバケットに入ります — ホットパーティションです。シャードサフィックス
(BOARD#global#0 … #9)を加えると、同じ書き込みが均等にファンアウトします。
これは 直感のための教材用ハッシュであって、DynamoDB の実際の内部ハッシュではありません — 実際の関数とパーティション境界は AWS の内部実装です。これは「均等な分散 vs 偏り」として 読んでください。あるキーがどの物理パーティションに入るかの予測としてではありません。
AWS はこれを ライトシャーディング と呼び、高頻度でカーディナリティの低いキーにこそ これを推奨しています (AWS — ライトシャーディングの使用)。
これは シングルテーブル設計 の裏にあるのと同じ の発想です — データが「自然に」収まる形ではなく、 アクセスパターンに合わせてキーを形作るのです。
楽な部分はアダプティブキャパシティに任せる
DynamoDB には アダプティブキャパシティ が搭載されており、re:Invent 2018 のセッション 「Amazon DynamoDB Under the Hood」(DAT401) で取り上げられています。これはテーブルの スループットを、熱を帯びているパーティションへ継続的に再分配し、恒常的にホットなキーを 独自のパーティションへ隔離 します(キーレベルの隔離、 AWS — バースト&アダプティブキャパシティ)。
これは即時かつ無料です — ただし物理法則に縛られます (アダプティブキャパシティの仕組み)。アダプティブキャパシティは熱を キー 間 で移動でき、熱による分割はホットなアイテムコレクションをソートキーの境界で 分割することさえできます。パーティションごとの上限が絶対的なまま残るのは、単一のホットな アイテム、単調増加するソートキー、または LSI を持つテーブル — セレブキーが依然として スロットルする場合 — に限られます。シャーディングは確定的な対処で、熱による分割は遅くて 機会依存的なので、それを当てにして待ってはいけません。
忙しいキーでスロットルが見えたときの判断経路は次のとおりです。
ほとんどのホットパーティションは「キーをシャーディングする」か「アダプティブキャパシティに 吸収させる」のどちらかに落ち着きます — この図は、単にどちらの枝にいるかを示すものです。
再設計の前に診断する
見えないものは直せません。スロットリングは、ProvisionedThroughputExceededException
(プロビジョンド)として、あるいは ThrottledRequests、
ReadThrottleEvents/WriteThrottleEvents、そして
ReadThrottleEventsForKeyRange/WriteThrottleEventsForKeyRange —
パーティション上限に特化したカウント — として CloudWatch に現れます
(AWS — CloudWatch メトリクス)。
それを CloudWatch Contributor Insights for DynamoDB と組み合わせましょう。最も アクセスの多いキーを直接ランク付けしてくれます — セレブキーを名前で確認する最速の方法です (AWS — Contributor Insights)。 そもそもホットキーが原因なのかまだ確信が持てないなら — DynamoDB は4つの異なる理由で スロットルします — まずは スロットリングのガイド から始めて、実際に当たった上限が どれなのかをメトリクスに語らせましょう。
シャーディングした読み取りパスをテストするときは、各シャードの
KeyConditionExpression を手で組み立てることになります。それらをタイプミスなく生成するには
DynamoDB Expression Builder を使ってください —
シャードごとに正確な PK = :pk AND begins_with(SK, :sk) の形を出力します。
避けるべき落とし穴
- 単調増加するソートキー。 単調なソートキー(タイムスタンプ、シーケンス番号)は、 すべての新規書き込みを1つのアイテムコレクションの同じ末尾に押し込み、熱による分割も 助けになりません — コレクションは 1,000 書き込みユニットに張り付いたままです。 ソートキーにエントロピーを加えるか、パーティションキーをシャーディングしましょう。
- 読み取りが多いパスを不必要にシャーディングすること。 読み取りが支配的でアイテムが 小さいなら、キャッシュや、より分散したキーを持つ GSI のほうが、 シャーディングの散らばった読み取りコストに勝ることが多いです。
- ホットパーティションと遅い
Scanを混同すること。Scanは何もかも読むから 遅く、ホットパーティションは1つのキーが過負荷だからスロットルします。別々の問題です — Query と Scan を参照してください。
次のステップ
シャーディングしたキーをスケッチし、実データに対して読み取りパスを検証しましょう。 DynamoDB Expression Builder でシャードごとの 条件を組み立て、DynoTable をダウンロード して自分のテーブルに対して実行し、 どのパーティションが実際に熱を帯びるかを見てください。