中級読了 3 分

DynamoDB のホットパーティション

DynamoDB はデータを多数の物理パーティションに分散させ、それぞれが独自のスループットの取り分を持ちます。ホットパーティションとは、1つのキーがその取り分で処理できる量をはるかに超える読み取りや書き込みを引き寄せる状態のことです。その結果、そのキーへのリクエストはスロットリングされる一方、テーブルの残りは遊んでいます。

DynamoDB のホットパーティションとは?

DynamoDB のホットパーティションとは、1つのパーティションキーが、そのスループットの取り分で処理できる量をはるかに超える読み取りや書き込みを引き寄せる状態のことです。その結果、そのキーへのリクエストはスロットリングされる一方で、テーブルの残りは遊んでいます。原因はテーブルのサイズではなくキー設計です——人気アイテム、低カーディナリティのキー、今日の日付など。治療法は書き込みを分散させることです。

  • 原因はテーブルのサイズではなくキー設計。 1つのパーティションキーにトラフィックが集中すること(人気ユーザー、status="OPEN" というフラグ、今日の日付など)が罠です。
  • アダプティブキャパシティは助けにはなるが、解決策ではない。 DynamoDB は熱を自動で再分散しますが、それでも単一のアイテムや単一のキーが、1つのパーティションが処理できる量を超えることはあります。
  • 治療法は書き込みを分散させること。 キーにエントロピーを加える(書き込みシャーディング)か、ホットな読み取りパスをより分散の良いアクセスパターンに移します。
  • SQL の出身者には、これに相当するものがない。 リレーショナルテーブルには「ある行のインデックス値が人気すぎる」という概念はありません。DynamoDB のフラットなキー単位スループットモデルにはそれがあります。

そもそもなぜパーティションが存在するのか

DynamoDB は、単一ノードの SQL モデルを分散・水平スケールのモデルに置き換えた2007年の Amazon Dynamo 論文の本番版の後継です。データはパーティションキーのハッシュによって物理ストレージノード全体にシャーディングされます。

各パーティションは、有限の量のデータを保持し、有限の量のスループットを処理します。AWS は 1秒あたり1パーティションにつき読み取り 3,000 ユニット、書き込み 1,000 ユニットという厳格な上限を文書化しています(AWS — パーティションの動作)。

その上限がすべてです。テーブルのプロビジョンドスループットは全パーティションの合計ですが、どの1つのキーも常に1つのパーティションにしか着地しません。

罠に名前を付ける: 1つのキーに積み上がるトラフィック

スループットが均等に共有されるのは、アクセスがキー全体に均等に分散されている場合だけです。1つのキーが不均衡なトラフィックを受けた瞬間、テーブル全体のキャパシティが使われないまま、そのキーだけがスロットリングします。

典型的なホットキーの形:

  • 人気アイテム — 誰もが読みに行く1人のユーザー、製品、テナント。
  • 低カーディナリティのパーティションキーstatuscountrytype。区別できる値が少ないということは、わずかなパーティションがすべての仕事をこなすということです。
  • 時間バケットのキー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つの論理的なボードを N 個の物理パーティションにファンアウトさせます:

PK = "BOARD#global#<shard>"  -- shard = playerId mod 10
SK = "PLAYER#<playerId>"

書き込みは1つではなく10個のパーティションに散らばるようになり、書き込みの余裕が10倍になります。代償は、ボード全体の読み取りが10個すべてのシャードに当たってマージしなければならないことです。なぜなら、単一の Query はシャードの境界をまたげないからです。読み取りのシンプルさを書き込みの分散と引き換えにするわけです。

AWS はこれを書き込みシャーディングと呼び、まさに高速かつ低カーディナリティのキーに対して推奨しています(AWS — 書き込みシャーディングの使用)。

これはシングルテーブル設計の背後にあるのと同じキーオーバーロードの発想です。データが「自然に」収まる形ではなく、アクセスパターンに合わせてキーを形作るのです。

簡単な部分はアダプティブキャパシティに任せる

DynamoDB にはアダプティブキャパシティが搭載されています。re:Invent 2018 のセッション "Amazon DynamoDB Under the Hood"(DAT401)で取り上げられたものです。これはテーブルのスループットを、熱を受けているパーティションへと継続的に再配分し、持続的にホットなキーを独自のパーティションに分離します(キーレベルの分離、AWS — バースト&アダプティブキャパシティ)。

これは即座で無料です。ただし物理法則によって制約されます。アダプティブキャパシティは熱をキーで移動できますが、単一のキーをパーティション単位の上限を超えて押し上げることはできません。真の人気キーは依然としてスロットリングします。その壁を越えさせてくれるのがシャーディングです。

混雑したキーでスロットリングを見つけたときの判断パスはこうです:

はいいいえ、同じプレフィックスの多数のキーはいいいえ1つのキーでスロットリング?単一アイテムがホットすぎる?キーをシャーディングまたは読み取りをキャッシュ低カーディナリティのパーティションキー?プレフィックスを書き込みシャーディングアダプティブキャパシティがおそらく処理する

ほとんどのホットパーティションは「キーをシャーディングする」か「アダプティブキャパシティに吸収させる」のどちらかに帰着します。図はどちらの分岐にいるかを示しているだけです。

再設計の前に診断する

見えないものは直せません。スロットリングは ProvisionedThroughputExceededException(プロビジョンド)として、または CloudWatch の ThrottledRequests とパーティション単位の ThrottleCount として現れます(AWS — CloudWatch メトリクス)。

それを CloudWatch Contributor Insights for DynamoDB と組み合わせましょう。最もアクセスされているキーを直接ランク付けしてくれます。人気キーを名前で確認する最速の方法です(AWS — Contributor Insights)。

シャーディングされた読み取りパスをテストするときは、各シャードの KeyConditionExpression を手作りすることになります。DynamoDB Expression Builder を使えばタイプミスなしに生成できます。シャードごとに正確な PK = :pk AND begins_with(SK, :sk) の形を出力してくれます。

避けるべき落とし穴

  • 自動インクリメントまたは単調増加のキー。 パーティションキーとしての連番 ID やタイムスタンプは、連続する書き込みを同じパーティションへルーティングします。ハッシュのプレフィックスを加えましょう。
  • 読み取り中心のパスを不必要にシャーディングすること。 読み取りが支配的でアイテムが小さい場合は、キャッシュや、より分散の良いキーを持つ GSI のほうが、シャーディングの分散読み取りコストに勝ることがよくあります。
  • ホットパーティションと遅い Scan を混同すること。 Scan はすべてを読むから遅いのです。ホットパーティションは1つのキーが過負荷だからスロットリングします。別の問題です。Query と Scan の比較 を参照してください。

次のステップ

シャーディングされたキーを描いたら、実データに対して読み取りパスを検証しましょう。DynamoDB Expression Builder でシャードごとの条件を構築し、DynoTable をダウンロードして自分のテーブルに対して実行し、実際にどのパーティションが熱を受けているかを観察してください。

更新日