新しい DynamoDB テーブルは毎秒何件の書き込みでスロットリングが始まるのか?
AWS は、作成したばかりのテーブルが最初から「up to 4,000 write request units per second」(秒あたり最大 4,000 書き込みリクエストユニット)を処理すると文書化しています。実際に何が起きるのかについて誰もが今なお引用する研究 — Capital One と ScyllaDB がどちらもリンクしているあの研究 — がそれを計測したのは 2019 年、ウォームスループットより前、設定可能な最大値より前、現在のスケーリングルールが存在するより前のことです。私たちが知る限り、それ以降に計測結果を公開した人はいません。
そこで、私たちが実行しました。2026-08-27、us-east-1 で数分前に作成したテーブルに対して、書き込み負荷を毎秒 1,000 リクエストから 8,000 リクエストまで上げていきました:
| 投入レート | 達成レート | スロットルされたリクエスト |
|---|---|---|
| 1,000/s | 1,000/s | 0 |
| 2,000/s | 2,000/s | 0 |
| 3,000/s | 3,000/s | 0 |
| 4,000/s | 4,000/s | 0 |
| 5,000/s | 4,132/s | 25,992 |
| 6,000/s | 4,131/s | 55,966 |
| 8,000/s | 4,134/s | 115,922 |
ドキュメントのベースラインは成り立ち、しかもわずかに保守的です。サービスは 4,000/s までは 1 件も拒否せずにすべてを受け入れ、そこからはどれだけ強く押しても 毎秒 4,130 ±2 書き込みに張り付きました。3 つのウィンドウ、3 つの投入レート、同じ上限に 0.05% の範囲で一致します。読み取りはまったくスロットルされませんでした。シード済みのテーブルに毎秒 12,700 件を超える結果整合性の読み取りを流しましたが、それを超えて足りなかった分は DynamoDB ではなく私たち自身のクライアントが原因でした。
テーブル 1 つ、1 日、1 リージョン、一様ランダムなキーを持つ約 1 KB のアイテム — はどこにも見当たりません。そのスコープが、ここにあるすべての数値に付く正直な但し書きです。この記事の残りは、どうやって計測したかについてです。うまくいくまでにベンチマークが 3 回失敗したこと、そしてその失敗のどれ一つとして DynamoDB のせいではなかったことも含めて。
スロットルは 400 で、しかも最初に間違った容疑者を名指しする
上限に達したとき返ってくるエラーは、よく読む価値があります:
ThrottlingException: Throughput exceeds the current capacity of your table or index.
DynamoDB is automatically scaling your table or index so please try again shortly.
If exceptions persist, check if you have a hot key:
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.html計測できた観察が 3 つあります:
- これは HTTP 400 であって 5xx ではありません。 あなたのリトライポリシーとダッシュボードは、それを知っている必要があります。5xx しかリトライしないクライアントはこれを取りこぼし、5xx でしかアラートを上げない監視は、書き込みの 3 分の 1 が跳ね返されている間もサービスを緑色に表示します。
- 最初のスロットルは、ベースラインを超える各ウィンドウの 0.9〜3.8 秒後に到着しました — サービスは上限が働き始める前に短いバーストの猶予をくれます。そして投入レートが高いほど、それは早く噛みつきます。
- ホットキーのヒントは診断ではなく、デフォルトの文言です。 私たちのキーは一様ランダムな UUID で、ホットキーは存在しませんでした。小さなスケールでは、このメッセージの第一容疑者は単にテーブルレベルの上限です。
レイテンシーはそのすべてに無関心でした。書き込みレイテンシーの p50 は、スロットルされていてもいなくても、どのウィンドウでもリージョン内で 4〜5 ms でした。サービスにとって拒否は安上がりです — 遅くなるのではなく、ただ「ノー」と言うだけです。
これはラップトップからは計測できない
最初の計測器は当たり前のものでした。マドリードのラップトップ上で動く Node スクリプトです。毎秒 1,000 書き込みはきれいにペーシングできましたが、2,000 で崩れました — DynamoDB が押し返したからではなく、大西洋を往復する約 100 ms は毎秒 2,000 件のインフライトリクエストに数百の同時ソケットを要求し、イベントループが溺れたからです。サービスは一度もスロットルしませんでした。私たちは自分の Wi-Fi をベンチマークしていたのです。
2 回目の試みでは、ランナーをターゲットと同じリージョンの単一の Lambda 関数へ移しました。リージョン内の往復は約 5 ms で、3 GB の関数 1 つは p50 10 ms で毎秒 2,000 リクエストをきれいにペーシングしました。それを超えると、CPU を張り付かせたまま約 1,100/s で横ばいになりました。リクエストの署名とレスポンスの処理はシングルスレッドの JavaScript であり、1 つのランナーでは毎秒 4,000 件の署名はどうやってもできません。割り当てたメモリは 3 GB、使用したメモリは 253 MB。ボトルネックは最初から RAM ではありませんでした — Lambda の CPU 配分はメモリ設定に比例するので、私たちが買っていたのはストレージではなく計算力だったのです。
そこで最終的な計測器は、8 つの Lambda のフリートです。それぞれが投入レート全体の 8 分の 1 をペーシングし、全員が共有の壁時計 T0 に合わせて開始するので、ウィンドウが揃います。余裕のある 1,000/s のランナー 8 つで、ヘッドルームを残したまま 8,000/s の負荷を投入でき、集計は数えたリクエストの単純な合計です — どこにも外挿はありません。
1 つが成功する前に 3 回の実行が死に、そのたびに DynamoDB は無実だった
フリートの最初の実行は、あるランナーが T0 の 882 秒後に開始したと報告して終わりました — 20 秒の約束に 15 分の遅刻です。2 回目の実行は読み取りタイムアウトで死にました。3 回目は、リトライを無効にした状態で、8 つのランナー全部が一斉に大声で失敗しました。その一方で CloudWatch は、どの Lambda も 4 分の計測を時間どおり、エラーなしできれいに完了したと表示していました。
犯人はラップトップと Lambda の間の接続でした。同期呼び出しは、実行の間ずっと 1 本の HTTPS 接続を完全に無言のまま開いたままにします — そして家庭用ルーターは、無言の接続を数分後に静かに殺します。CLI は死んだソケットを見て、最悪の選択をしました。黙ってリトライしたのです。再実行された計測 Lambda は、自分の T0 がとうに過ぎ去っているのを見つけました。目に見えないまま 2 回走りうるベンチマークハーネスは、ハーネスではありません。AWS の請求書が付いた乱数生成器です。
最終的にうまくいった形には、長時間のリモート計測ならどれにでも今後使うであろう 3 つのルールがあります:
- 撃ちっぱなしにして、結果は帯域外で。 ランナーは非同期で呼び出され(接続はミリ秒で閉じます)、結果は小さな DynamoDB テーブルにアイテムとして書き込まれます。ドライバーは 8 件の結果行をポーリングします。1 つのリクエストより長生きする接続はありません。
- リトライはどこでもオフ。 計測クライアントはリクエストあたり 1 回の試行で動きます — リトライは、私たちが数えるために存在しているまさにそのスロットルを黙って吸収してしまいます — そして呼び出し経路でもリトライは無効なので、どのランナーも 2 回実行されることはありません。
- ハングではなくウォッチドッグを。 各ランナーは自分のスケジュールをデッドラインと競わせます。何かが引っかかったら、無言でタイムアウトするのではなく、部分的なカウントと、どこで止まったのかの正確なスナップショットを返します。自分を説明してくれる失敗した実行は読み取り 1 回分のコストで済みますが、ハングした実行は一晩を奪います。
すべてのリクエストには 8 秒のタイムアウトも付けています。ハングした実行がそうなったのは、タイムアウトのないインフライトリクエストが 1 つ、最後のドレイン処理を永遠に固まらせたからです。約 400 万リクエストにつき 1 つの無制限の待機で、それには十分でした。
読み取りはどうだったか
読み取りフェーズは、1,000 アイテムをシードした 2 つ目の新規テーブルに対して、の GetItem(それぞれ約 1 KB、読み取りユニット 0.5)を使って実行しました:
| 投入レート | 達成レート | スロットル |
|---|---|---|
| 4,000/s | 4,000/s | 0 |
| 8,000/s | 7,941/s | 0 |
| 12,000/s | 11,119/s | 0 |
| 16,000/s | 12,762/s | 0 |
スロットルはゼロ、最後までゼロです。ドキュメントの 12,000 読み取り/s のベースラインは成り立ち、その端を見つけることはできませんでした。投入 16,000/s では 8 つのランナーのうち 5 つが自分自身のクライアント側の飽和に達したので、12,762/s という数字は私たちのフリートが頭打ちになった地点であって、DynamoDB がそうなった地点ではありません。サービスの限界のように飾り立てず、はっきりそう言っておきます。リージョン内の読み取りは p50 2〜4 ms でした。
覚えておく価値のある小さな数字が 2 つあります。新規のオンデマンドテーブルが CreateTable から ACTIVE になるまで、ベンチマークの実行では 22 秒、それ以前の探りでは 7.4 秒かかりました — ベストケースではなく、そのばらつきを見込んでおきましょう。そしてベンチマーク全体、課金対象の書き込み 672,116 件と読み取り 108 万件のコストは $0.97 でした。計測器は再利用でき、実験はコーヒー 1 杯分です。
30 分の負荷では上限は 2 倍にならない
AWS の成長ルールは、オンデマンドのキャパシティは以前のピークの最大 2 倍まで対応すると言っています。それが起きるところを見たかったので、上限の計測の後、テーブルに 8,000 書き込み/s の負荷を 34 分間連続でかけ、達成レートを 10 秒ごとにバケット化しました。その形はこうです:
| 負荷をかけた分数 | 上限 |
|---|---|
| 0〜8 | 約 4,000/s(ベースラインのまま) |
| 9〜25 | 約 5,000/s |
| 26 | 約 6,000/s |
| 27〜34 | 約 7,000/s |
成長はなだらかな傾斜ではなく、約 1,000/s の段差で唐突に訪れます — ある 1 分は 1 つのレートで平らで、次の 1 分は次のレートで平らです。最初の上限は、継続的な過剰需要が丸 8 分続いても動きません。そして 34 分後、テーブルは 7,000/s を処理しました。出発点の 1.75 倍ですが、投入した 8,000 にも、きれいな倍増にも届いていません。ローンチで新しいテーブルに約 4,000 書き込み/s 以上が必要なら、事前にウォームアップしておくか、最大オンデマンドスループットを明示的に設定してください — 成長のメカニズムは本物ですが、即座でもなければ、あなたのスケジュールに気前が良いわけでもありません。この挙動は安定してもいます。2019 年の研究では、9,000/秒の負荷を与えられたテーブルがテスト終了時点でおよそ 7,000/秒まで成長していました — 私たちのテーブルが 7 年後に到達したのと同じ水準です。この実行のコストは $12.67 で、その日にやったことの中で最も高価でした。
クラウドサービスを自分で計測するなら応用できること
- 負荷生成器をターゲットと同じリージョンに置くこと。そうしないと、サービスではなく自分の経路を計測することになります。
- Node のプロセス 1 つは、メモリにかかわらず毎秒約 2,000 件の署名済みリクエストで頭打ちになります。負荷を複数のランナーに分割し、数えた結果を合計しましょう。
- 計測経路のリトライは、すべての層で無効にすること。リトライは、ベンチマークが見るために存在するまさにそのものを隠すために存在します。
- 長い実行をまたいで無言の接続を保持しないこと。非同期で呼び出し、結果は帯域外で届け、ポーリングしましょう。
- すべてのリクエストにタイムアウトを、すべてのランナーには、詰まった状態のスナップショット付きで部分データを返すウォッチドッグを与えましょう。
- ランナーごとに操作数のハード上限を設定して、ペーシングのバグが請求を膨らませる代わりに中断するようにし、実行が作ったものはすべて — テーブル、ロール、関数、ログ —
finallyで片付けましょう。
この記事が支えるリファレンスページ
完全なデータセット — すべてのウィンドウ、すべてのランナー、レイテンシーのパーセンタイル、逐語的なエラー文字列 — は、以前に公開したアイテムサイズとページ制限の探索結果と並んで、私たちの DynamoDB の制限リファレンスにある計測済みの表を裏づけています。DynamoDB を日常的に扱っているなら、DynoTable はそのためのデスクトップクライアントです — 同じチームが、主張を繰り返す前にライブのサービスで確かめるという同じ習慣で作っています。