上級読了 3 分

DynamoDB の物理パーティション

物理パーティション は、DynamoDB が実際にデータを格納する単位です — SSD のスライスで、複数の Availability Zone にまたいでレプリケートされ、キースペースの1スライスを保持します。テーブルは論理的なものです。バイト — そしてスループットの上限 — が本当に存在するのはパーティションです。

DynamoDB のパーティションはどのように機能しますか?

DynamoDB はテーブルを物理パーティション — Availability Zone にまたいでレプリケートされた SSD のスライス — に分散して格納します。各パーティションは ~10 GB、毎秒 3,000 読み取りユニット、毎秒 1,000 書き込みユニットで頭打ちになります。パーティションキーのハッシュがアイテムの着地先パーティションを決め、DynamoDB はパーティションの成長や熱が続くと自動的に分割します。

  • すべてのパーティションは ~10 GB のストレージ、毎秒 3,000 読み取りユニット、毎秒 1,000 書き込みユニットで頭打ちになる。 その上限はテーブル単位ではなくパーティション単位だ。
  • パーティションキーのハッシュがパーティションを選ぶ。 同じキーのアイテムはまとまって着地する。1つのホットキーは1つのホットパーティションを意味する。
  • DynamoDB はパーティションを自動で分割する — サイズで、そして持続的な熱で — が、すべてのトラフィックを1か所に流し込むキーは直せない。
  • キャパシティを残したままのスロットルが手がかり。 テーブルが 5% 使用率で座っているのに ProvisionedThroughputExceeded エラーが出るなら、単一パーティションが上限に達している。

アイテムがパーティションを見つける仕組み

DynamoDB はパーティションキー値を内部ハッシュ関数に通します。ハッシュ出力が物理パーティションを選びます。同じキーが入れば、毎回同じパーティションが出ます。

SQL から来ると、対応物がありません。チューニングするインデックスの B-tree も、手で割り当てるシャードキーもありません。配置はあなたが制御せず、決して見ることのないハッシュです。

パーティションキーを共有するアイテムは アイテムコレクション を形成し、まとまって格納され、ソートキーで並べられます。それが1つのキーへの Query を安くする理由です — 1つのパーティション上の1つの連続した範囲を読むからです。(Query と Scanを参照。)

ゲームのマッチイベントストアを考えます。テーブルのキーは arenaId(パーティション)と eventKey(ソート)です。

# Item
arenaId    = "ARENA#7f3a"
eventKey   = "EVT#1719100800#a91c"
playerTag  = "Nightjar"
dmgDealt   = 412

アリーナ 7f3a のすべてのイベントは同じパーティションにハッシュされ、ソートキー順に積み重なります。「このマッチのタイムラインを読む」には最適です。その1つのアリーナがすべてのトラフィックを浴びると負債になります。

すべてのパーティションが課す3つの上限

単一のパーティションは、最大で以下を提供するよう設計されています。

上限パーティションあたり数え方
ストレージ約 10 GB生のアイテムバイト
読み取りキャパシティ3,000 読み取りユニット/秒1 RU = 4 KB の強い整合性のある読み取り1回
書き込みキャパシティ1,000 書き込みユニット/秒1 WU = 1 KB の書き込み1回

出典: AWS の パーティションキー設計のベストプラクティス ガイド。

アイテムサイズが計算を左右します。20 KB のアイテムは強い整合性のある読み取り1回あたり 5 読み取りユニットかかるので、1つのパーティションはスロットルする前に毎秒およそ 600 回のそうした読み取りを提供します — 3,000 ではありません。書き込みコストは 1 KB ごとに、読み取りコストは 4 KB ごとに切り上げます。

罠: これらは パーティション の上限であって、テーブル の上限ではありません。テーブルが 40,000 WCU でプロビジョンドされていても、すべての書き込みが 1,000 で頭打ちになる1つのパーティションを叩いているなら、依然としてスロットルしうるのです。

パーティションが分割する仕組み

DynamoDB は2つのケースでパーティションを自動的に追加します。あなたがコマンドを実行することは決してありません。

サイズで分割。 パーティションが ~10 GB へ向かって埋まると、DynamoDB はそのキー範囲を2つに分割し、アイテムの半分を新しいパーティションへ移します。ストレージは透過的に増え、読み取りと書き込みはその間ずっと動き続けます。

熱で分割。 パーティションがスループット上限近くの持続的なトラフィックを受けると、DynamoDB はホットなキー範囲を分割し、それぞれの半分が独自のパーティションに着地するようにします。AWS はこれを split-for-heat メカニズムと呼びます。自然に止まる短いスロットルのバーストは、たいてい分割が起きたばかりであることを意味します。

サイズパーティション A~10 GB / ホット分割トリガー?格納バイトで範囲を半分にトラフィックで範囲を半分に2つのパーティションそれぞれ独自のキャパシティ1つのホットキーは依然として1パーティション上

分割は多くのキーにまたがる余地を生みますが、最下部のノードが落とし穴です — 分割は キー範囲 を分けるのであって、単一のキーは決して分けません。

ホットキーが分割器を打ち負かす理由

ここが地雷です。分割はパーティションキーの 範囲 を再分配します。トラフィックが1つのキー値に集中すると、すべてのリクエストが同じパーティションにハッシュされ、分割すべき範囲が残りません。

アリーナ 7f3a がトーナメント決勝で、他のすべてのアリーナがアイドルのまま毎秒 4,000 書き込みを引き込んでいるなら、1,000 でスロットルします — そして分割は助けになりません。4,000 すべてが1つのキーを共有するからです。新しい KeyRangeThroughputExceeded のスロットル理由は、まさにこれを名指します — テーブルではなく、1つのパーティションのキー範囲が上限を超えている、と。

直し方はキャパシティのスライダーではなく、データモデルにあります。ホットキーを 書き込みシャーディング します — 小さなサフィックスを付けて、1つの論理アリーナが N 個の物理パーティションに広がるようにします。

arenaId = "ARENA#7f3a#3"   # shard 0..9, chosen per write

読み取りはシャードにまたがってファンアウトし、クライアント側でマージします。キーの形と各シャードの Query は、アプリケーションコードを1行触る前にDynamoDB 式ビルダーでプロトタイプできます。

1つの細かい点: LSI の例外

ストレージが パーティションキー単位で 上限になるケースが1つあります。Local Secondary Index が無ければ、1つのパーティションキーのアイテムコレクションは必要なだけ多くのパーティションに自動分割されます — 数十億のソートキー値でも問題ありません。

LSI を追加すると、1つのパーティションキーのコレクション全体が単一の 10 GB パーティションに収まらなければなりません。LSI がそれを共有するからです。それがGSI と LSIで扱う PK 単位の崖で — ほとんどのチームが GSI に手を伸ばすもう1つの理由です。

パーティションを冷たく保つ設計

実際に制御できるレバーはパーティションキーです。行数に対して異なる値の多いものを選び、トラフィックが均等に広がるようにします。(さらなるパターンはシングルテーブル設計に。)

  • 高カーディナリティのキー。 ユーザー単位やテナント単位のキーは、誰もが同時に叩く日単位やステータス単位のキーに勝る。
  • 既知のホットキーに注意。 「現在のトーナメント」や「今日」という値は、出荷した後ではなく前の段階で集中リスクだ。
  • 避けられないホットキーをシャーディングする。 1つのキーが過大なトラフィックを取らざるを得ないとき、サフィックスが標準の脱出口だ。

キャパシティを残したままのスロットルは、1つのパーティションがホットだというシグナルです。問題のアイテムを調べ、シャーディングしたキーレイアウトをDynoTableでリハーサルしましょう — 自分のテーブルに向け、過負荷のキーを見つけ、それがあなたをポケットベルで起こす前に直し方をモデリングしましょう。

更新日