a deeper understanding of spark internals (hadoop conference japan 2014)

48
A Deeper Understanding of Spark’s Internals Patrick Wendell 07/08/2014

Upload: hadoop-conference-japan

Post on 08-Sep-2014

795 views

Category:

Technology


1 download

DESCRIPTION

■Hadoop Conference Japan 2014 講演資料 https://hcj2014.eventbrite.com/ 『A Deeper Understanding of Spark Internals』 Patrick Wendell (Databricks)

TRANSCRIPT

Page 1: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

A Deeper Understanding of Spark’s Internals

Patrick Wendell 07/08/2014

Page 2: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

This Talk

• Goal: Understanding how Spark runs, focus on performance

• Major core components:

– Execution Model

– The Shuffle

– Caching

ゴール:Sparkがどのように動作するかをパフォーマンスに注目して理解

主要なコンポーネント

- 実行モデル

- シャッフル

- キャッシュ

Page 3: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

This Talk

• Goal: Understanding how Spark runs, focus on performance

• Major core components:

– Execution Model

– The Shuffle

– Caching

ゴール:Sparkがどのように動作するかをパフォーマンスに注目して理解

主要なコンポーネント

- 実行モデル

- シャッフル

- キャッシュ

Page 4: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

なぜ内部を理解するのか?

ゴール:名前の「最初の文字」の出現頻度を求める。

Page 5: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 6: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, Andy) (P, Pat) (A, Ahir)

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 7: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, Andy) (P, Pat) (A, Ahir)

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 8: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, Andy) (P, Pat) (A, Ahir)

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 9: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, Set(Ahir, Andy)) (P, Set(Pat))

(A, Andy) (P, Pat) (A, Ahir)

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 10: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, 2) (P, 1)

(A, Andy) (P, Pat) (A, Ahir)

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 11: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Why understand internals?

Goal: Find number of distinct names per “first letter”

sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues(names => names.toSet.size)

.collect()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, 2) (P, 1)

(A, Andy) (P, Pat) (A, Ahir)

res0 = [(A, 2), (P, 1)]

ゴール:名前の「最初の文字」の出現頻度を求める。

なぜ内部を理解するのか?

Page 12: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. スケジューリングを行い、各タスクを実行する。

Page 13: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 1: Create RDDs

sc.textFile(“hdfs:/names”)

map(name => (name.charAt(0), name))

groupByKey()

mapValues(names => names.toSet.size)

collect()

ステップ1: RDDを作成する

Page 14: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 1: Create RDDs

HadoopRDD

map()

groupBy()

mapValues()

collect()

ステップ1: RDDを作成する

Page 15: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, 2)

(A, Andy) (P, Pat) (A, Ahir)

res0 = [(A, 2), ...]

ステップ2: 実行計画を作成する

できる限り多くのパイプラインを作る

データ再構築の必要に応じて、「ステージ」に分割する

Page 16: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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()

Andy Pat Ahir

(A, [Ahir, Andy]) (P, [Pat])

(A, 2) (P, 1)

(A, Andy) (P, Pat) (A, Ahir)

res0 = [(A, 2), (P, 1)]

ステップ2: 実行計画を作成する

できる限り多くのパイプラインを作る

データ再構築の必要に応じて、「ステージ」に分割する

Page 17: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

• Split each stage into tasks

• A task is data + computation

• Execute all tasks within a stage before moving on

Step 3: Schedule tasks ステップ3: タスクをスケジュールする

各ステージをタスクに分割する

タスクは、データ+計算 である

あるステージのタスクを全て実行したら、次のステージに進む

Page 18: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks Computation Data

hdfs:/names/0.gz

hdfs:/names/1.gz

hdfs:/names/2.gz

Task 1

hdfs:/names/3.gz

Stage 1 HadoopRDD

map()

hdfs:/names/0.gz

Task 0

HadoopRDD

map()

hdfs:/names/1.gz

Task 1

HadoopRDD

map()

Page 19: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

/names/0.gz

HadoopRDD

map() Time

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

Page 20: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map() Time

Page 21: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

Time

Page 22: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map() Time

Page 23: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

Time

Page 24: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/2.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

Time

Page 25: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

/names/2.gz

HadoopRDD

map()

Time

Page 26: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

/names/2.gz

HadoopRDD

map()

Time

/names/3.gz

HadoopRDD

map()

Page 27: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

/names/2.gz

HadoopRDD

map()

Time

/names/3.gz

HadoopRDD

map()

Page 28: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Step 3: Schedule tasks

/names/0.gz

/names/3.gz

HDFS

/names/1.gz

/names/2.gz

HDFS

/names/2.gz

/names/3.gz

HDFS

/names/0.gz

HadoopRDD

map()

/names/1.gz

HadoopRDD

map()

/names/2.gz

HadoopRDD

map()

Time

/names/3.gz

HadoopRDD

map()

Page 29: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

The Shuffle

Stage 1

Stage 2

HadoopRDD

map()

groupBy()

mapValues()

collect()

Page 30: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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

ハッシュキーをバケットに分解する

データをパーティションに沿って再分配する

最適化:

- データが既に適切にパーティション化されていれば、可能な限りシャッフルを回避する。

- 部分集約で、データの移動を減らす。

Page 31: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

The Shuffle

Disk

Stage 2

Stage 1

• Pull-based, not push-based

• Write intermediate files to disk

プッシュ型ではなくプル型で

中間ファイルはディスクに書く。

Page 32: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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つのキーと値のペアは、メモリに収まる必要がある。

Page 33: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Done!

Stage 1

Stage 2

HadoopRDD

map()

groupBy()

mapValues()

collect()

Page 34: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

What went wrong?

• Too few partitions to get good concurrency

• Large per-key groupBy()

• Shipped all data across the cluster

どこで間違えてしまうのか?

・ 並列度をよくしたいのに、パーティション数が少なすぎる

・ groupBy()で、1つのキーが大きすぎる

・ 全データをクラスタ内に転送してしまう

Page 35: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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は、パーティション数のチューニング!

Page 36: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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はある

Page 37: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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を増やす

パーティション数を増やす

プログラム構造を見直す

Page 38: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 39: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 40: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 41: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 42: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 43: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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. 標準ライブラリを理解する

Page 44: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Fixing our mistakes

sc.textFile(“hdfs:/names”)

.distinct(numPartitions = 6) .map(name => (name.charAt(0), 1)) .reduceByKey(_ + _) .collect()

Original: sc.textFile(“hdfs:/names”)

.map(name => (name.charAt(0), name))

.groupByKey()

.mapValues { names => names.toSet.size }

.collect()

間違いを修正する

Page 45: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Demo: Using the Spark UI

デモ: Spark UI を使う

Page 46: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Tools for Understanding Low-level Performance

jps | grep Executor

jstack <pid>

jmap -histo:live <pid>

低レベルのパフォーマンスを把握するためのツール

Page 47: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

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 でジョブが書けるのなら、ジョブの最適化が可能

Page 48: A Deeper Understanding of Spark Internals (Hadoop Conference Japan 2014)

Questions?