中級読了 3 分

DynamoDBのバッチ操作

多くの項目を一度に読み書きする必要があるとき、項目ごとにGetItemPutItemを 1回ずつ撃つと、項目ごとに1回のネットワーク往復になります — 遅く、おしゃべりです。 DynamoDBのバッチAPIは、多くの項目操作を単一のリクエストに畳み込みます。読み込みは BatchGetItem、書き込みはBatchWriteItemです。

これらはスループットとレイテンシーの勝ちであって、整合性の保証ではありません — そしてその区別こそ、人が火傷するところです。バッチはトランザクションではありません

DynamoDBのバッチ操作とは?

DynamoDBのバッチ操作は、多くの項目の読み書きを単一のリクエストに畳み込みます。 BatchGetItemは最大100項目を取得し、BatchWriteItemは最大25項目をputまたはdelete します。それぞれ16 MBが上限です。往復を節約しますが、キャパシティーは節約しません。 決定的に、バッチはトランザクションではありません — 項目は独立して成功または 失敗し、ロールバックはありません。

  • BatchGetItem — 1つ以上のテーブルにまたがって最大100項目(または16 MB)を 1回の呼び出しで取得します。
  • BatchWriteItem — 1回の呼び出しで最大25件のput/delete操作(または16 MB)。 更新はなし — putとdeleteのみです。
  • アトミックではありません。 個々の項目は、他が失敗する一方で成功することが あります。ロールバックはありません。
  • 部分的な失敗は正常です。 スロットリングされた項目はUnprocessedItems / UnprocessedKeysで返ってきます — 自分でバックオフ付きで再試行しなければなりません。
  • 個別呼び出しと同じキャパシティーコスト — バッチ化は往復を節約するのであって、 キャパシティーユニットを節約するのではありません。

問題:多くの項目、1回の往復

サポートデスクを運営しているとしましょう。ダッシュボードはキューを描画するために 50件のチケットをIDで読み込む必要があり、夜間ジョブは解決済みの1,000件のチケットを アーカイブします。それを1項目ずつやると50回(または1,000回)の逐次往復になります — レイテンシーが積み上がり、ジョブは這うように進みます。

バッチ化はそれらをひと握りの呼び出しに畳みます。50件のチケット読み込みは単一の BatchGetItemになり、アーカイブジョブは25件のdeleteずつのBatchWriteItem呼び出しの 連なりになります。往復ははるかに少なく、動かすデータは同じです。

バッチAPIの仕組み

BatchGetItemは(1つ以上のテーブルにまたがる)プライマリキーの集合を取り、 一致する項目を返します。テーブルごとに強力な整合性のある読み込みをリクエスト できます。読めなかったもの — たいていはリクエストがスループット上限をかすめたため — は、呼び出し全体を失敗させるのではなくUnprocessedKeysで返ってきます。

BatchWriteItemPutRequest / DeleteRequest操作のリストを取ります。欠けて いるものに注目してください。更新がありません。バッチ書き込みは項目まるごとを 置き換える(put)か、それを削除する(delete)かのどちらかです — 特定の属性を変更する には依然としてUpdateItemが必要です。書けなかった項目はUnprocessedItemsで 返ってきます。

成功スロットリングバックオフして再試行BatchWriteItem: 25 件のput/deleteアイテム単位の処理書き込み完了UnprocessedItems

バッチは独立した操作の束であり、それぞれが自身で成功 または失敗します — 1つのオールオアナッシングの単位ではありません。

バッチはトランザクションではない

これが罠です。アーカイブジョブのバッチが途中でスループット上限にぶつかると、一部の チケットは削除され、一部はされません — そしてDynamoDBは通ってしまったものを 取り消しません。ロールバックも、隔離も、「25件全部かゼロか」もありません。

オールオアナッシングのセマンティクス — 「チケットをアーカイブ済みに移動しかつ 未対応チケットのカウンターを減らす、さもなくばどちらもしない」 — が必要なら、それは バッチではなくTransactWriteItemsです。トランザクションは より高価で(各操作は2倍で課金され)、100項目で上限が定められていますが、バッチが あえて提供しないアトミック性を与えてくれます。

未処理項目の扱い

正しいバッチの呼び出し元は、常に未処理の集合をチェックして再試行します。 DynamoDBは、リクエスト全体は受理されたが一部の項目を処理できなかったとき — 典型的 には一時的なスロットリング — に、いつでもUnprocessedItems/UnprocessedKeysを 返します。

未処理の項目だけを、指数バックオフとジッターを 付けて再送します。バッチを撃ちっぱなしとして扱うと、ひそかに書き込みが落ち — 数か月後にデータ欠損として表面化する類のバグになります。

DynoTableでのバッチ書き込み

一括ジョブのコストがどうなるかを、まず DynamoDB料金計算機で見積もりましょう — バッチは 束ねる個別の書き込みと同じキャパシティーを、ただより少ないリクエストで消費します。

DynoTableでは、編集をローカルにステージングして、コミットする前にレビューします — 多くの行にまたがる一括変更は、それぞれが1つのAPI呼び出しではなく、グループ化された リクエストとして送られます。一括削除はバッチ化された書き込みとして送られ、 未処理項目の再試行はあなたの代わりに処理されます。

DynoTableで、ステージングされた編集をバッチとしてコミットする前にレビューしているところ。
DynoTableで、ステージングされた編集をバッチとしてコミットする前にレビューしているところ。

落とし穴と次のステップ

  • UnprocessedItems/UnprocessedKeysは常にバックオフ付きで再試行しましょう — それらは例外ではなく想定内です。
  • 部分的失敗のロールバックはありません。 アトミック性が必要ですか? トランザクションを使いましょう。
  • バッチ書き込みに更新はありませんBatchWriteItemはput/deleteのみです。 属性を変更するにはUpdateItemに手を伸ばしましょう。
  • 呼び出しごとの上限に注意 — 書き込み25件 / 読み込み100件 / 16 MB。超えると 呼び出し全体がValidationExceptionで失敗します (BatchGetItemでの項目過多BatchWriteItemでの項目過多)。 より大きなジョブはページ送りしましょう。ページネーションを 参照してください。

再試行ループをスクリプト化せずに一括の読み書きを実行したいですか? DynoTableをダウンロードして、テーブルを直接編集しましょう。

往復の計算

直列の GetItem 呼び出しは、ホップごとにレイテンシーを払います。BatchGetItem はリクエストあたり最大 100 キー、または 16 MB のどちらか先に当たった上限まで束ねます。

パターンキー数約50キー時の往復数メモ
直列 GetItem5050いちばん単純なコード。テールレイテンシーは最悪
1回の BatchGetItem50150回の Get と同じ RCU 合計
2回のバッチ12022回目のバッチが残り20キーを運ぶ

キャパシティコストは変わりません — バッチ化が節約するのはウォールクロック時間とクライアント CPU であって、RCU ではありません。書き込みなら、1,000件の削除をバッチあたり25件でまとめると、個別削除1,000回ではなく BatchWriteItem 40回で済みます。

強い整合性のバッチ読み取り

BatchGetItem はリクエストマップのテーブルごとに ConsistentRead: true を受け付けます。強い読み取りは、同じアイテムの結果整合性読み取りの RCU のなお2倍です。1回のバッチ呼び出しで強い整合性と結果整合性のテーブルを混ぜても問題ありません — 各テーブルエントリが自分のフラグを持ちます。

大きなジョブのチャンク分割

平均 3 KB のアイテム1,000件をアーカイブするとき、1回のバッチ読み取りは100件上限の内側に収まりますが、16 MB を超えることはあり得ます(100 × 3 KB = 300 KB — 安全)。50 KB のアイテムだと、件数上限は100でも、メガバイト上限に約320件/呼び出しで当たります。

書き込みは明示ループでページ送りします。

for each chunk of 25 keys:
  BatchWriteItem
  retry UnprocessedItems with backoff until empty

DynoTable のステージ済みコミットは、対象の書き込みをバッチ化し、未処理アイテムを自動で再試行します — ジッタ付き sleep で自分でスクリプトするのと同じパターンです。

バッチ vs トランザクションの判断

必要なことAPI最大件数部分失敗時
ベストエフォートの一括ロードBatchWriteItem25 ops未処理を再試行
オールオアナッシングの台帳移動TransactWriteItems100 opsトランザクション全体がロールバック
既知の多数キーを読むBatchGetItem100 keys未処理キーを再試行
読み取り + 書き込みを原子的にTransactWriteItems25 transact ops(ドキュメントの上限が適用)全部かゼロか

フィクスチャからバッチロードを種まきするときは、プレーン JSON から put/delete ペイロードを DynamoDB JSON コンバータ で生成できます。

消費キャパシティを点検する

バッチ応答には、要求すればテーブルごとの ConsumedCapacity を含められます。バックフィル中にそれをログしましょう — スロットル率の上昇は、ジョブが完全に止まる前に、未処理セットの肥大として現れます。バッチがスケジュールで回るなら、持続 WCU を 料金計算機 と突き合わせてください。

更新日