前回の記事では、キーを指定してデータを取得する基本を解説しました。

今回は、インデックスが貼られていない属性(日付やログレベルなど)を条件にしてデータを抽出する手法を解説します。

ただし、DynamoDBのScan操作には、RDB(SQL)の感覚で使うと「思わぬ高額請求」や「パフォーマンス低下」を招く罠が潜んでいます。実務で事故を起こさないための正確な知識を身につけましょう。

1. 今回の活用事例:特定サービスのログ抽出

「2024年3月以降の、特定のサービス(例: OrderService)で発生した重大なエラー(CRITICAL)を調査したい」というシナリオを例にします。

ここで重要なのは、今回の手法は「効率的な検索」ではなく、「Scanでテーブル全体を走査しつつ、その中から特定の項目だけをフィルタリングして返す」という動作である点です。

2. 実践!条件指定による返却結果の絞り込み

以下のコマンドは、SystemLogTableを走査し、「サービス名」「日時」「レベル」で絞り込んでCSV化する例です。

Bash
# 1. 日本語ヘッダーの作成
echo "発生日時,ログID,レベル,メッセージ,サービス名,リクエストID" > critical_errors.csv

# 2. AWS CLIでスキャンし、jqで整形して追記
aws dynamodb scan \
  --table-name SystemLogTable \
  --filter-expression "#ts >= :start_date AND #lvl = :level AND service_name = :svc" \
  --expression-attribute-names '{
    "#ts": "timestamp",
    "#lvl": "log_level"
  }' \
  --expression-attribute-values '{
    ":start_date": {"S": "2024-03-01T00:00:00Z"},
    ":level": {"S": "CRITICAL"},
    ":svc": {"S": "OrderService"}
  }' \
  --profile my-aws-profile | \
  jq -r '.Items[] | [
  (.timestamp.S // ""),
  (.log_id.S // ""),
  (.log_level.S // ""),
  ((.message.S // "")[0:50]),
  (.service_name.S // ""),
  (.request_id.S // "")
] | @csv' >> critical_errors.csv

技術的なポイントと補足

  • サービス名の絞り込み: シナリオ通りに抽出するため、service_name = :svc を条件に追加しています。
  • 予約語の回避: timestampなどはDynamoDBの予約語のため、#tsなどのエイリアスを使用しています。
  • jqの堅牢性: 属性が欠落しているアイテムがあっても処理が止まらないよう、// ""(デフォルト値)を指定しています。
  • ISO 8601形式の比較: 日時文字列が「UTC・固定長」の同一フォーマットであれば、文字列比較(>=)による時系列フィルタリングが可能です。

3. 知っておくべき「Scanの制約」とコストの正体

① FilterExpressionは「コスト節約」にならない

--filter-expressionは、「一旦データを読み取った後で、不要な分を捨ててからユーザーに返す」機能です。 読み取りコスト(RCU)は、フィルタ前の「読み取った生データのサイズ」に対して課金されます。結果が1件であっても、スキャンした範囲にデータが詰まっていれば、その分の料金が発生します。

② 課金基準は「件数」ではなく「データサイズ」

よく「100万件読んだら100万件分のコスト」と言われますが、厳密には「読み取ったデータの総サイズ」で計算されます。

  • 1アイテムが非常に小さければコストは抑えられます。
  • Scanを行う際は、テーブル全体の「アイテム数」だけでなく「平均データサイズ」を把握しておくことが重要です。

③ 「1MB制限」の正体

Scan は 1 回で最大 1 MB までしか処理できず、続きがある場合は LastEvaluatedKey を使って次ページを取得します。AWS CLI は通常このページネーションを自動処理するため、見た目は 1 回のコマンドでも、実際には複数回の Scan が内部で実行されることがあります。FilterExpression は読み取り後に評価されるため、巨大なテーブルに対する Scan は返却件数が少なくても高コストになり得る点に注意が必要です。(※--no-paginate を付けると、この自動ページネーションを無効化できます。)

4. 実務での使い分け:ScanかQueryか

今回紹介した Scan + jq の手法は、アドホックな調査や一時的なデータ確認には有効です。

たとえば、「一度だけ過去ログを確認したい」「障害調査のために特定条件のデータを洗い出したい」といった用途では、すぐに実行できる方法として役立ちます。
一方で、同じ条件での抽出を定常的に実行する運用には、Scan をそのまま使い続けるべきではありません。AWS 公式でも、大きなテーブルや多くの結果を捨てるフィルタ付き Scan は可能な限り避け、Query を使えるようにテーブルやインデックスを設計することが推奨されています。

5. 公式リファレンスで深く学ぶ

この記事で解説した手法を実務に投入する際は、以下の公式ドキュメントを一度確認しておくことを強く推奨します。仕様の細かな変更や、より高度なオプションについて正確な情報を得ることができます。

AWS公式ドキュメント(DynamoDB)

jq公式ドキュメント

  • jq Manual (official)
    • 今回使用した //(デフォルト値)や [0:50](スライス)、@csv フォーマットの詳細な使い方が載っています。

まとめ

  1. Scanは全件走査: 条件に合う件数が少なくても、読み取ったデータサイズに応じたコストが発生する。
  2. 1MB制限は評価前: フィルタをかける前のデータ量に基づき、CLIは自動でページをめくる。
  3. 正確なフィルタリング: シナリオに合わせて filter-expression に必要な属性をすべて含める。

DynamoDBの特性を正しく理解し、コストとパフォーマンスのバランスが取れたデータ運用を目指しましょう!

最後まで読んでいただきありがとうございました。