Spark SQL调优篇-入门

742 阅读4分钟

最近在学习Spark SQL调优,找了一些任务和SQL进行优化,任务调度系统用的是azkaban,在组件中指定Spark运行时的参数,调优过程中通过Spark UI查看任务运行情况,反复试验控制变量法得出最短运行时间,本次调优只做初步优化,欢迎探讨。

调优原则

(1)尽量让每个核处理2~3个任务,避免任务处理出现数据倾斜,默认最大cpu为200,每个cpu默认3核。

(2)尽量保证数据写入磁盘的大小接近128M,因为HDFS默认块大小是128M。

(3)利用控制变量法调节spark.num.output.files参数和spark.sql.shuffle.partitions参数大小,得到最优运行时间。

例子:spark版本是2.2.1,SQL的主要功能是针对ETL之后的数据做初步清洗,存在大表join小表,本次调优来源数据367.1GB,文件数量1896个,maxExecutors=200,executor-cores=3,driver-memory=6G(本来默认是2G,但是任务最后一阶段执行失败遂改成6G,后续证明不是主要原因),executor-memory=6G。

image.png

image.png

调优过程

  1. 首先不设置spark.num.output.files参数和spark.sql.shuffle.partitions参数,观察任务运行时间, 尤其是任务运行每个阶段的耗时。

image.png

从Stages页面可以看到任务运行主要耗时在于读取文件时和shuffle过程,并行度是200,文件数1896。 可能是数据量太大任务在最后一阶段执行通不过,报错Container killed by YARN for exceeding memory limits. 7.4 GB of 7 GB physical memory used. Consider 如下:

image.png 看网上的说法是数据倾斜导致某一个container负载太高,内存超出限制,我看了任务运行参数yarn.executor.memoryOverhead=1024,将该参数设置为4096,堆外内存容量增大一些。任务还是失败报错Container killed on request. Exit code is 143 如下所示,原因还是因为内存不足:

image.png 这里直接增大shuffle partition数,将并行度增大到1000,设置 sql.shuffle.partitions = 1000,先保证任务能运行成功。

image.png 从任务最后阶段输出日志来看,最后是将结果数据写到磁盘。但是运行时间同样太长了,虽然任务可能不会失败,直接设置输出文件数为1200,num.output.files = 1200.

image.png 至此,任务运行成功,上一步最后一阶段之所以运行时间长是因为结果数据太大了,应该是将结果写到一个文件了导致运行时间过长。

  1. 通过Hadoop命令查看结果表中文件大小,数据总量不变,spark.num.output.files将会决定文件大小,参数越大,文件越小,参数越小,文件越大,调整文件大小直到接近128M。

image.png 结果数据189.9GB.

image.png 结果文件数1200,与上一步设置output.files参数对应。

image.png 单个文件的大小是162MB左右,大于HDFS默认文件块大小,应该设置189.9GB/127MB约1530,127MB是为了留一点空间。

image.png 结果文件大小约128MB,初步符合预期。这里主要是为了调整文件大小符合预期,其实最好是保证写文件数的任务是600的倍数,确保每个任务处理2~3个任务,所以可以设置1800.

  1. 最后调整shuffle.partitions参数,确保每个核处理2~3个task,也就是6002/6003/600*4,之前是设置的1000,部分core任务负载不均。

image.png shuffle.partitions参数设置为1200,shuffle阶段运行时间为1.6分钟。

image.png shuffle.partitions参数设置为1800,shuffle阶段运行时间为1.2分钟,运行时间缩短。很显然1800比1200更加合理。这里不再做更多的尝试了,有兴趣的读者可以自行试验。

至此,初步调优完毕,当然还有很多参数设置比如内存等是否最优解同样可以多次试验得出。

总结:先说明一下,因为任务数据量不是固定的,所以设置参数需要经过多次试验找到平均值,这里得出的结论是某一次的最优结果,生产环境中的任务需要观察数据量变化并经过多次试验调优找到均值。