12. Spark Execution Model
1. Create DAG of RDDs to represent
computation
2. Create logical execution plan for DAG
3. Schedule and execute individual tasks
Spark の実行モデル
1. 計算を表現するRDDのDAGを作成する。
2. DAGに対する論理的な実行計画を作成する。
3. スケジューリングを行い、各タスクを実行する。
15. Step 2: Create execution plan
• Pipeline as much as possible
• Split into “stages” based on need to
reorganize data
Stage 1 HadoopRDD
map()
groupBy()
mapValues()
collect()
AndyPatAhir
(A, [Ahir, Andy]) (P, [Pat])
(A, 2)
(A, Andy)(P, Pat)(A, Ahir)
res0 = [(A, 2), ...]
ステップ2: 実行計画を作成する
できる限り多くのパイプライ
ンを作る
データ再構築の必要に応じて、「ステージ」に
分割する
16. Step 2: Create execution plan
• Pipeline as much as possible
• Split into “stages” based on need to
reorganize data
Stage 1
Stage 2
HadoopRDD
map()
groupBy()
mapValues()
collect()
AndyPatAhir
(A, [Ahir, Andy]) (P, [Pat])
(A, 2) (P, 1)
(A, Andy)(P, Pat)(A, Ahir)
res0 = [(A, 2), (P, 1)]
ステップ2: 実行計画を作成する
できる限り多くのパイプライ
ンを作る
データ再構築の必要に応じて、「ステージ」に
分割する
17. • Split each stage into tasks
• A task is data + computation
• Execute all tasks within a stage before
moving on
Step 3: Schedule tasks
ステップ3: タスクをスケジュールする
各ステージをタスクに分割
する
タスクは、データ+計算 で
ある
あるステージのタスクを全て実行したら、次の
ステージに進む
30. The Shuffle
Stage 1
Stage 2
• Redistributes data among partitions
• Hash keys into buckets
• Optimizations:
– Avoided when possible, if
data is already properly
partitioned
– Partial aggregation reduces
data movement
ハッシュキーをバケットに分解する
データをパーティションに沿って再分配する
最適化:
- データが既に適切にパーティション化されていれば、可能な限りシャッフ
ルを回避する。
- 部分集約で、データの移動を減らす。
31. The Shuffle
Disk
Stage 2
Stage 1
• Pull-based, not push-based
• Write intermediate files to disk
プッシュ型ではなくプル型で
中間ファイルはディスク
に書く。
32. Execution of a groupBy()
• Build hash map within each partition
• Note: Can spill across keys, but a single key-
value pair must fit in memory
A => [Arsalan, Aaron, Andrew, Andrew, Andy, Ahir, Ali, …],
E => [Erin, Earl, Ed, …]
…
groupBY() の実行
各パーティションごとにハッシュマップを構築する。
注意:
複数のキーに振り分けることはできる
しかし、1つのキーと値のペアは、メモリに収まる必要がある。
34. What went wrong?
• Too few partitions to get good concurrency
• Large per-key groupBy()
• Shipped all data across the cluster
どこで間違えてしまうのか?
・ 並列度をよくしたいのに、パーティション数が少なすぎる
・ groupBy()で、1つのキーが大きすぎる
・ 全データをクラスタ内に転送してしまう
35. Common issue checklist
1. Ensure enough partitions for concurrency
2. Minimize memory consumption (esp. of
sorting and large keys in groupBys)
3. Minimize amount of data shuffled
4. Know the standard library
1 & 2 are about tuning number of partitions!
よくある問題のチェックリスト
1. 十分なパーティション数を設定し、並列にする
2. メモリ消費量を最小化する
(特にソートとgroupByの巨大なキー)
3. シャッフルするデータ量を最小化する
4. 標準ライブラリを理解する
1と2は、パーティション数のチューニング!
36. Importance of Partition Tuning
• Main issue: too few partitions
– Less concurrency
– More susceptible to data skew
– Increased memory pressure for groupBy,
reduceByKey, sortByKey, etc.
• Secondary issue: too many partitions
• Need “reasonable number” of partitions
– Commonly between 100 and 10,000 partitions
– Lower bound: At least ~2x number of cores in
cluster
– Upper bound: Ensure tasks take at least 100ms
パーティションチューニングの重要性
一番の問題:パーティション数が少なすぎる
並列度が下がる
データの偏りに影響が出やすい
groupBy, reducebyKey,
sortBykey等のメモリを圧迫する
二番目の問題:パーティション数が多すぎる
一般には 100 から 10,000 パーティションがよい
下限:少なくともクラスタのコア数の2倍
上限:各タスクの実行時間が少なくとも100msはある
37. Memory Problems
• Symptoms:
– Inexplicably bad performance
– Inexplicable executor/machine failures
(can indicate too many shuffle files too)
• Diagnosis:
– Set spark.executor.extraJavaOptions to include
• -XX:+PrintGCDetails
• -XX:+HeapDumpOnOutOfMemoryError
– Check dmesg for oom-killer logs
• Resolution:
– Increase spark.executor.memory
– Increase number of partitions
– Re-evaluate program structure (!)
メモリ問題
症状:
不可解なほどパフォーマンスが悪い
不可解なほどエグゼキュータや
ノードで失敗が起こる(シャッフルファイルが多くなりすぎる)診断:
spark.executor.extraJavaOptionsに以下の値を設定
dmesgでOOMキラーの
ログを確認する
解決策:
spark.executor.memoryを増やす
パーティション数を増やす
プログラム構造を見直す
38. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.map(name => (name.charAt(0), name))
.groupByKey()
.mapValues { names => names.toSet.size }
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
39. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.repartition(6)
.map(name => (name.charAt(0), name))
.groupByKey()
.mapValues { names => names.toSet.size }
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
40. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.repartition(6)
.distinct()
.map(name => (name.charAt(0), name))
.groupByKey()
.mapValues { names => names.toSet.size }
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
41. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.repartition(6)
.distinct()
.map(name => (name.charAt(0), name))
.groupByKey()
.mapValues { names => names.size }
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
42. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.distinct(numPartitions = 6)
.map(name => (name.charAt(0), name))
.groupByKey()
.mapValues { names => names.size }
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
43. Fixing our mistakes
sc.textFile(“hdfs:/names”)
.distinct(numPartitions = 6)
.map(name => (name.charAt(0), 1))
.reduceByKey(_ + _)
.collect()
1. Ensure enough partitions for
concurrency
2. Minimize memory consumption (esp. of
large groupBys and sorting)
3. Minimize data shuffle
4. Know the standard library
間違いを修正する
1. 十分なパーティション数を設定し、
並列度を良くする
2. メモリ消費量を最小化する
(特に大きな groupBy とソート)
3. データシャッフルを最小にする
4. 標準ライブラリを理解する
47. Other Thoughts on Performance
Start small and without caching, then scale
your workload
If you can write your jobs in terms of
SparkSQL, we can optimize them for you
パフォーマンスに関する他の考え
キャッシュなしで小さく初めて、その後負荷を大きくしていく
SparkSQL でジョブが書けるのなら、ジョブの最適化が可能