はじめに
以下の記事で、AWS Glue と Iceberg を用いて分析基盤を作成してみました。 本記事では、この分析基盤に対して 1万〜15万件 のデータを取り込み、以下の観点で検証します。
- Glue Job の処理時間(XML パース → Iceberg MERGE)の計測
- データ量に対する処理時間のスケーリング傾向の把握
- ワーカー数(並列度)による効果の確認
電力使用量データ量 と AWS Glue ワーカー数
1. 電力使用量データ量(ファイル数)
- 1日分の需要家数を1万件、2万件、5万件、10万件、15万件と変動させます。
- 1ファイル = 1万需要家(重複なし)分の 1日の電力使用量を保存しています。
| ファイル数 | 需要家数 |
|---|---|
| 1 | 1万 |
| 2 | 2万 |
| 5 | 5万 |
| 10 | 10万 |
| 15 | 15万 |
2. Glue ワーカー数
Glueのワーカータイプ と バージョンを以下のように設定して、3回ずつ処理時間を計測していきます。
- Glue ワーカータイプ : G.1X(4 vCPU , 16GB RAM )
- Glue バージョン : 5.0
| ワーカー数 | DPU (G.1X) |
|---|---|
| 2 | 2 |
| 5 | 5 |
| 10 | 10 |
テストケース
- 上記の電力使用量データ量とAWS Glue ワーカー数のパターンを組み合わせると15個のテストケースができます。
- 1テストケースごとに、3回ずつ処理時間を計測していきます。
| # | 需要家数 | ファイル数 | ワーカー数 |
|---|---|---|---|
| 1 | 1万 | 1 | 2 |
| 2 | 1万 | 1 | 5 |
| 3 | 1万 | 1 | 10 |
| 4 | 2万 | 2 | 2 |
| 5 | 2万 | 2 | 5 |
| 6 | 2万 | 2 | 10 |
| 7 | 5万 | 5 | 2 |
| 8 | 5万 | 5 | 5 |
| 9 | 5万 | 5 | 10 |
| 10 | 10万 | 10 | 2 |
| 11 | 10万 | 10 | 5 |
| 12 | 10万 | 10 | 10 |
| 13 | 15万 | 15 | 2 |
| 14 | 15万 | 15 | 5 |
| 15 | 15万 | 15 | 10 |
処理時間の取得方法
- AWS Glue コンソールで実行時間を確認
- または AWS CLI で取得:
aws glue get-job-runs --job-name consumption-xml-to-iceberg --max-results 10
検証手順
1. サンプルデータ生成
- 以下の構造で、1万件分の電力使用量データを含むXMLファイルを生成します。
<?xml version="1.0" encoding="UTF-8"?> <electricity_usage_report> <record> <external_id>CUST-001234</external_id> <target_date>2024-01-15</target_date> <updated_at>2024-01-16T06:30:00+09:00</updated_at> <usage_kwh> <slot time="00:00">1.2</slot> <slot time="00:30">1.1</slot> <!-- ... 48コマ ... --> <slot time="23:30">1.3</slot> </usage_kwh> </record> <record> <external_id>CUST-001235</external_id> <target_date>2024-01-15</target_date> <updated_at>2024-01-16T06:30:00+09:00</updated_at> <usage_kwh> <slot time="00:00">1.2</slot> <slot time="00:30">1.1</slot> <!-- ... 48コマ ... --> <slot time="23:30">1.3</slot> </usage_kwh> </record> </electricity_usage_report>
2. テスト実行
テストは以下の手順で進めます。
- S3 へアップロード
- Iceberg テーブルのクリーンアップ
- Glue Job 実行(ワーカー数を指定)
- 処理時間の計測
3. 結果記録
処理時間を記録し、結果記録テーブルを更新します。
手順1〜3を各テストケースで繰り返します。
結果記録
| # | 需要家数 | ワーカー数 | 1回目 | 2回目 | 3回目 | 平均 |
|---|---|---|---|---|---|---|
| 1 | 1万 | 2 | 122s | 63s | 65s | 83s |
| 2 | 1万 | 5 | 126s | 138s | 133s | 132s |
| 3 | 1万 | 10 | 133s | 169s | 176s | 159s |
| 4 | 2万 | 2 | 148s | 140s | 137s | 142s |
| 5 | 2万 | 5 | 132s | 141s | 130s | 134s |
| 6 | 2万 | 10 | 123s | 137s | 184s | 148s |
| 7 | 5万 | 2 | 190s | 188s | 191s | 190s |
| 8 | 5万 | 5 | 158s | 156s | 162s | 159s |
| 9 | 5万 | 10 | 149s | 201s | 154s | 168s |
| 10 | 10万 | 2 | 533s | 544s | 548s | 542s |
| 11 | 10万 | 5 | 260s | 261s | 268s | 263s |
| 12 | 10万 | 10 | 267s | 269s | 232s | 256s |
| 13 | 15万 | 2 | 753s | 777s | 749s | 760s |
| 14 | 15万 | 5 | 280s | 325s | 283s | 296s |
| 15 | 15万 | 10 | 259s | 220s | 225s | 235s |
結果サマリー
需要家数が増えるほど処理時間が増加しますが、ワーカー数を増やすことで処理時間を短縮できます。特に10万件以上ではワーカー2と、ワーカー5・10の差が顕著になります。
以下のグラフは、需要家数の増加に伴う処理時間の変化を可視化したものです。

| 需要家数 | ファイル数 | ワーカー2 | ワーカー5 | ワーカー10 | 最速 |
|---|---|---|---|---|---|
| 1万 | 1 | 83s | 132s | 159s | ワーカー2 |
| 2万 | 2 | 142s | 134s | 148s | ワーカー5 |
| 5万 | 5 | 190s | 159s | 168s | ワーカー5 |
| 10万 | 10 | 542s | 263s | 256s | ワーカー10 |
| 15万 | 15 | 760s | 296s | 235s | ワーカー10 |
考察
ファイル数とワーカー数の関係
- ファイル数 < ワーカー数 → 効果が出にくい
- ファイル数 ≥ ワーカー数 → 並列処理の効果が発揮される
推奨設定(ファイル分割あり前提)
| データ規模 | 推奨ワーカー数 |
|---|---|
| 〜2万件 | 2 |
| 2〜5万件 | 5 |
| 10万件以上 | 10 |
追加検証: ファイル分割 vs 1ファイル
検証目的
同じ需要家数でファイル数を変えた場合の処理時間を比較し、処理時間にどのような差が出るか観察する。
テストケース
| # | 需要家数 | ファイル数 | ワーカー数 |
|---|---|---|---|
| 1 | 2万 | 1 | 2 |
| 2 | 2万 | 1 | 5 |
| 3 | 2万 | 1 | 10 |
| 4 | 5万 | 1 | 2 |
| 5 | 5万 | 1 | 5 |
| 6 | 5万 | 1 | 10 |
| 7 | 10万 | 1 | 2 |
| 8 | 10万 | 1 | 5 |
| 9 | 10万 | 1 | 10 |
| 10 | 15万 | 1 | 2 |
| 11 | 15万 | 1 | 5 |
| 12 | 15万 | 1 | 10 |
結果記録
| # | 需要家数 | ファイル数 | ワーカー数 | 1回目 | 2回目 | 3回目 | 平均 |
|---|---|---|---|---|---|---|---|
| 1 | 2万 | 1 | 2 | 163s | 164s | 215s | 181s |
| 2 | 2万 | 1 | 5 | 156s | 234s | 145s | 178s |
| 3 | 2万 | 1 | 10 | 205s | 153s | 158s | 172s |
| 4 | 5万 | 1 | 2 | 312s | 223s | 274s | 270s |
| 5 | 5万 | 1 | 5 | 231s | 221s | 208s | 220s |
| 6 | 5万 | 1 | 10 | 209s | 211s | 234s | 218s |
| 7 | 10万 | 1 | 2 | 365s | 353s | 376s | 365s |
| 8 | 10万 | 1 | 5 | 320s | 319s | 317s | 319s |
| 9 | 10万 | 1 | 10 | 312s | 319s | 356s | 329s |
| 10 | 15万 | 1 | 2 | 502s | 454s | 468s | 475s |
| 11 | 15万 | 1 | 5 | 419s | 427s | 450s | 432s |
| 12 | 15万 | 1 | 10 | 448s | 428s | 440s | 439s |
比較結果
ファイル分割ありの場合、ワーカー5・10で処理時間が大きく短縮されています。一方、1ファイルの場合はワーカー数を増やしても処理時間の改善が限定的になります。
以下のグラフは、同じ需要家数での「ファイル分割あり」と「1ファイル」の処理時間を比較したものです。
- 青がファイル分割、赤が1ファイル です。

| 需要家数 | ファイル数 | ワーカー2 | ワーカー5 | ワーカー10 | 最速 |
|---|---|---|---|---|---|
| 2万 | 1 | 181s | 178s | 172s | ワーカー10 |
| 2万 | 2 | 142s | 134s | 148s | ワーカー5 |
| 5万 | 1 | 270s | 220s | 218s | ワーカー10 |
| 5万 | 5 | 190s | 159s | 168s | ワーカー5 |
| 10万 | 1 | 365s | 319s | 329s | ワーカー5 |
| 10万 | 10 | 542s | 263s | 256s | ワーカー10 |
| 15万 | 1 | 475s | 432s | 439s | ワーカー5 |
| 15万 | 15 | 760s | 296s | 235s | ワーカー10 |
追加検証のまとめ
発見事項
- ワーカー2: 10万件以上で1ファイルの方が速い
- ワーカー5: 全データサイズでファイル分割が優位
- ワーカー10: 大規模データでファイル分割の効果が顕著
推奨設定(総合判断)
| 条件 | 推奨ワーカー数 | 理由 |
|---|---|---|
| 通常運用(ファイル分割あり) | 5 | コストと速度のバランス最良 |
| 大量データ急ぎ(10万件+、分割あり) | 10 | 並列効果最大 |
| 1ファイルのみ | 5 | 10にしても速度向上しない |
Spark UI 分析
Stage 比較: 1ファイル vs 15ファイル分割
1ファイル (総処理時間: 440s)
| Stage | 時間 | タスク数 | 処理内容 |
|---|---|---|---|
| 0 | 316.0s | 1 | XMLパース (repartition) |
| 1 | 6.9s | 20 | count |
| 3 | 3.6s | 20 | DynamicFrame変換 |
| 6 | 7.2s | 20 | SQL処理 |
| 7 | 10.8s | 20 | SQL処理 |
| 10 | 1.8s | 36 | SQL処理 |
| 14 | 4.4s | 1 | MERGE書き込み |
15ファイル分割 (総処理時間: 259s)
| Stage | 時間 | タスク数 | 処理内容 |
|---|---|---|---|
| 0 | 52.6s | 15 | XMLパース (count) |
| 1 | 32.2s | 15 | DynamicFrame変換 |
| 3 | 53.4s | 15 | SQL処理 |
| 4 | 56.3s | 15 | SQL処理 |
| 6 | 2.2s | 36 | SQL処理 |
| 9 | 4.2s | 1 | MERGE書き込み |
分析結果
| 項目 | 1ファイル | 15ファイル | 改善率 |
|---|---|---|---|
| XMLパース時間 | 316s | 53s | 6倍高速 |
| XMLパースタスク数 | 1 | 15 | 15並列化 |
| 総処理時間 | 440s | 259s | 41%短縮 |
ボトルネックの特定
1ファイルの場合: Stage 0 (XMLパース) が316秒で全体の72%を占める。タスク数が1のため、ワーカーを増やしても並列化できない。
15ファイル分割の場合: Stage 0 が15タスクで並列実行され、52.6秒に短縮。各ワーカーが独立してファイルを処理できる。
根本原因:
glueContext.create_dynamic_frame.from_optionsでのXMLパースは、ファイル単位でしか並列化されない。1ファイル内のレコードは順次処理される。
参考: タスク分散の仕組み
- G.1X ワーカー = 4 vCPU = 4 cores (4並列スロット)
- 10ワーカー = 1 Driver + 9 Executor = 36並列スロット
- 15ファイル → 15タスク → 4 Executor × 4 cores で同時実行
結論
- ファイル分割が最重要: ワーカー数を増やすより、ファイル分割の方が効果大
- ワーカー数はファイル数に合わせる: ワーカー数 ≤ ファイル数 が基本(余剰ワーカーは効果が出にくい)
- ワーカー5が汎用的: ほとんどのケースで最適またはそれに近い性能
- ワーカー10は条件付き: 10ファイル以上 × 10万件以上の場合のみ有効
本番運用に向けて
今回の検証はワーカー数(2/5/10)とファイル数(1〜15)の限られた範囲で実施しました。本番環境では、実際のデータ量・ファイル数・処理時間の要件に応じて、ワーカー数やワーカータイプのチューニングが必要です。
特に以下の点を考慮してください:
- コストと速度のトレードオフ: ワーカー数やワーカータイプを上げれば速くなるがDPUコストも増加
- ファイル数との関係: ファイル数以上にワーカーを増やしても効果は限定的
- SLA要件: 処理時間の制約がある場合は、それに合わせた設定が必要
今後の課題
今回はSpark UIを用いてStage単位での処理時間を確認しましたが、正直なところSpark自体を触り始めたばかりで、まだまだ理解が浅い部分があります。
以下の点は今後学びながら深掘りしていきたいです。
- Spark UIの詳細メトリクス(Scheduler Delay、GC Time、Shuffle Read/Write など)の読み方
- ワーカー2で逆転現象が起きる原因の特定
- ワーカータイプによる違い
- Icebergのデータファイル形式(Parquet vs Avro)によるパフォーマンス差
引き続き検証を進めて、分かったことがあれば記事にしていきたいと思います。