Spark TeraSort 实现与调优

TeraSort简介

TeraSort是为Hadoop参加Sort Benchmark而开发的程序包。其中包含3个程序:

  • TeraGen:用来生成测试数据;
  • TeraSort:用来对生成的测试数据进行排序;
  • TeraValidate:用来校验排序结果的正确性;

Spark的TeraSort实现

参考ehiggs/spark-terasort以及RDD#sortBy的代码,我自己实现了一个Spark的TeraSort程序。具体代码在这里

TeraSort本地测试

Hadoop TeraSort测试

首先,下载并部署单机伪分布式Hadoop集群。接下来就可以测试了。

## 执行TeraGen生成测试数据
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.5.jar teragen 10000 /data/terasort/input
$ bin/hdfs dfs -ls /data/terasort/input
Found 3 items
-rw-r--r--   1 blueszheng supergroup          0 2018-01-23 20:28 /data/terasort/input/_SUCCESS
-rw-r--r--   1 blueszheng supergroup     500000 2018-01-23 20:28 /data/terasort/input/part-m-00000
-rw-r--r--   1 blueszheng supergroup     500000 2018-01-23 20:28 /data/terasort/input/part-m-00001

## 执行TeraSort进行排序
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.5.jar terasort /data/terasort/input /data/terasort/output
$ bin/hdfs dfs -ls /data/terasort/output
Found 3 items
-rw-r--r--   1 blueszheng supergroup          0 2018-01-23 20:31 /data/terasort/output/_SUCCESS
-rw-r--r--  10 blueszheng supergroup          0 2018-01-23 20:30 /data/terasort/output/_partition.lst
-rw-r--r--   1 blueszheng supergroup    1000000 2018-01-23 20:31 /data/terasort/output/part-r-00000

## 执行TeraValidate验证排序结果
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.5.jar teravalidate /data/terasort/output /data/terasort/report
$ bin/hdfs dfs -ls /data/terasort/report
Found 2 items
-rw-r--r--   1 blueszheng supergroup          0 2018-01-23 20:35 /data/terasort/report/_SUCCESS
-rw-r--r--   1 blueszheng supergroup         22 2018-01-23 20:35 /data/terasort/report/part-r-00000
$ bin/hdfs dfs -cat /data/terasort/report/part-r-00000
checksum	139abefd74b2
## 如果排序结果有误,则part-r-00000会输出错误信息

Spark TeraSort测试

首先下载并解压Spark安装包。然后对TeraSort程序进行打包,因为我是直接把代码放在examples工程里面,所以直接打包examples工程,然后将jar包扔到Spark安装目录下。接下来就可以测试了。

## 执行Spark TeraSort进行排序
$ bin/spark-submit \
> --master local \
> --class org.apache.spark.examples.terasort.TeraSort spark-examples_2.11-2.2.0.jar \
> /data/terasort/input /data/terasort/spark/output
$ bin/hdfs dfs -ls /data/terasort/spark/output
Found 2 items
-rw-r--r--   1 blueszheng supergroup          0 2018-01-23 21:18 /data/terasort/spark/output/_SUCCESS
-rw-r--r--   1 blueszheng supergroup    1000000 2018-01-23 21:18 /data/terasort/spark/output/part-r-00000

## 执行TeraValidate验证排序结果
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.5.jar teravalidate /data/terasort/spark/output /data/terasort/spark/report
$ bin/hdfs dfs -cat /data/terasort/spark/report/part-r-00000
checksum	139abefd74b2
## 输出的checksum与Hadoop的terasort结果一致

TeraSort调优实践

接下来是在集群环境里面的一点调优,这里记录一下。集群管理使用Spark的Standalone模式,集群资源如下图。网络为千兆网络,存储为HDD盘。

ps. Standalone模式记得在worker节点的spark-env.sh里面export SPARK_LOCAL_DIRS到数据盘,否则可能会影响shuffle。

第一次提交使用了下面的命令,

bin/spark-submit \
--master spark://10-215-128-78:7077 \
--driver-memory 2g \
--num-executors 24  \
--executor-cores 4 \
--executor-memory 5376m \
--conf spark.memory.fraction=0.8 \
--conf spark.memory.storageFraction=0.25 \
--conf spark.default.parallelism=200 \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.io.compression.lz4.blockSize=128k \
--conf spark.reducer.maxSizeInFlight=96m \
--conf spark.shuffle.file.buffer=128k \
--conf spark.shuffle.io.maxRetries=16 \
--conf spark.network.timeout=200s \
--conf spark.ui.enabled=false \
--class org.apache.spark.examples.terasort.TeraSort spark-examples_2.11-2.2.0.jar \
/user/root/terasort/100G-input /data/terasort/spark/output/100G

接下来说下其中一些配置的考虑,

  • 在核数与内存配置确定的情况下,每个分区的数据量应该在内存/核数之下,这样就可以尽可能不把数据spill到硬盘上。按上述配置,每个分区数据量应<5376/4m,总数据量为100G,当spark.default.parallelism配置为200时,假如数据均匀shuffle,则单个分区数据量在500M(事实证明假设数据均匀还是太理想化了);
  • spark.io.compression.lz4.blockSize=128k是为了提升lz4的压缩率,但是在压缩时会占用更多的内存;
  • spark.reducer.maxSizeInFlight=96m内存充裕的情况下可以提升shuffle read性能;
  • spark.shuffle.file.buffer=128k内存充裕的情况下可以提升shuffle write性能;
  • spark.shuffle.io.maxRetriesspark.network.timeout的配置都是为了提高shuffle read稳定性(其实当前场景下是不需要的);

需要说明的是,Spark在读取HDFS数据时,每一个block就是一个partition,当前我们的数据是800个128M的block。因此在shuffle write阶段时,最大的内存占用应该是24核 * 128M,应该来说内存是非常充裕的(感觉spark.shuffle.file.buffer可以设置更大一点跑跑看)。

整个执行过程花费了20分钟。查看HistoryServer。其中Job 0是用来执行sampling的,优化的大头不在这。

Jobs

Job 1

Stage 1

Stage 2

根据HistoryServer的展示,调整参数重跑,主要是以下3点,

  • 首先,在shuffle write的时候花费了很长的时间,见上图。看了下日志(work/app-20180122215844-0005/22/stderr),如下图,并没有什么异常。怀疑是spark.io.compression.lz4.blockSize的配置有问题,而且通过机器资源监控看到网络并没有成为瓶颈,不需要太高的压缩率,于是去掉该配置;


  • 另外在shuffle read的时候发现还是存在大量的spill,见上图。数据不算均匀,spark.default.parallelism配置为200看来还是小了点,于是调大(感觉spark.reducer.maxSizeInFlight也应该调小,mark下先),并且使用了G1GC;
  • 去掉提高稳定性的配置;

于是就用下面的命令重跑,

bin/spark-submit \
--master spark://10-215-128-78:7077 \
--driver-memory 2g \
--num-executors 24  \
--executor-cores 4 \
--executor-memory 5376m \
--conf spark.memory.fraction=0.8 \
--conf spark.memory.storageFraction=0.25 \
--conf spark.default.parallelism=256 \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.reducer.maxSizeInFlight=96m \
--conf spark.shuffle.file.buffer=128k \
--conf spark.ui.enabled=false \
--conf spark.executor.extraJavaOptions=-XX:+UseG1GC \
--class org.apache.spark.examples.terasort.TeraSort spark-examples_2.11-2.2.0.jar \
/user/root/terasort/100G-input /data/terasort/spark/output/100G

结果将执行时间缩短到了16分钟,用HistoryServer可以看到,shuffle write的性能有了很大提升,缩短的这4分钟都是在shuffle write这里。



通过上图可以看到在shuffle read的时候,尽管已经将分区扩大到256个,依旧是存在大量的spill,Shuffle spill (memory)是需要spill的数据在内存的大小,Shuffle spill (disk)是需要spill的数据序列化到硬盘之后的大小。

那么接下来的优化思路就是看看能不能完全避免spill数据到硬盘。通过查看机器资源监控可以发现,瓶颈在硬盘上(当然还有内存)。
硬盘使用率,
disk

某个CPU负载,
cpu

网络流量,
network

接下来,增大内存,调整参数重跑,

bin/spark-submit \
--master spark://10-215-128-78:7077 \
--driver-memory 2g \
--num-executors 24  \
--executor-cores 4 \
--executor-memory 7g \
--conf spark.memory.fraction=0.8 \
--conf spark.memory.storageFraction=0.25 \
--conf spark.default.parallelism=256 \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.shuffle.file.buffer=256k \
--conf spark.ui.enabled=false \
--conf spark.executor.extraJavaOptions=-XX:+UseG1GC \
--class org.apache.spark.examples.terasort.TeraSort spark-examples_2.11-2.2.0.jar \
/user/root/terasort/100G-input /data/terasort/spark/output/100G

然鹅并没有什么效果,与之前结果类似,也还是存在spill,这里就不贴了。但是当启用off-heap却有了一丢丢提升,

bin/spark-submit \
--master spark://10-215-128-78:7077 \
--driver-memory 2g \
--num-executors 24  \
--executor-cores 4 \
--executor-memory 2g \
--conf spark.memory.offHeap.size=5368709120 \
--conf spark.memory.offHeap.enabled=true \
--conf spark.default.parallelism=256 \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
--conf spark.shuffle.file.buffer=256k \
--conf spark.ui.enabled=false \
--conf spark.executor.extraJavaOptions=-XX:+UseG1GC \
--class org.apache.spark.examples.terasort.TeraSort spark-examples_2.11-2.2.0.jar \
/user/root/terasort/100G-input /data/terasort/spark/output/100G

执行时间缩短了1分钟。

Jobs
Job 1
Stage 2

看样子off-heap还是有优化的。后面把内存继续调大都没什么用了,瓶颈依旧是在硬盘上。
接下来的思路,就是用Java Flight Recorder深入挖掘一下看看有没有什么可以优化的点。

alright,今天就先到这了,have fun ^_^

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值