一、数据结构的选择
DataFrame、Dataset和SQL在速度上都是相同的,spark会自动使用优化引擎对它们进行优化,相比于RDD的手动编写优化代码要好,同时避免使用UDF函数也是一个优化策略,UDF是昂贵的,因为它们强制将数据表示为JVM中的对象,有时在查询中要对一个记录执行多次此操作。应该尽可能地使用结构化的API来执行操作,因为他们将以更高效地方式执行转化操作。
二、分桶操作
分桶允许Spark根据可能执行的连接操作或聚合操作对数据进行“预先分区”,帮助数据在分区间持续分布,而不是倾斜到一两个分区。分桶允许Spark根据可能执行的连接操作或聚合操作对数据进行“预先分区”,帮助数据在分区间持续分布,而不是倾斜到一两个分区。
三、分区数
对于所有Spark作业来说,分区数量都很重要。如果你的分区太少,那么只有少量节点在工作,这可能会造成数据倾斜。但是如果你有太多的分区,那么启动每一个任务的需要的开销可能会占据所有的资源。为了更好地平衡,你的Shuffle中为每个输出分区设置至少几十兆字节的数据。spark.sql.shuffle.partitions
用来配置shuffle操作时的分区数量如join或者aggregate,一般减少分区数用coalesce方法,增加分区数用reparation,但reparation是shuffle操作,也有一定的计算开销,不过reparation的数据一般不存在数据倾斜的问题,同时增加了分区数,并行度更高。
四、并行度
通常建议分配集群中每个CPU核心至少有两到三个任务。你可以通过spark.default.parallelism
属性来设置它,并同时根据集群中的核心数量来调整spark.sql.shuffle.partitions属性,通常建议的并行度是总核数的2-3倍,总核数为contains * coresPerContainer,spark.default.parallelism
用来配置Rdd操作(join, reduceByKey, and parallelize)时的分区数,如果你正在操作DataFrame进行shuffle操作,那么只能通过df = df.repartition(numOfPartitions)
来调整分区,但repartition会重新清洗数据,也会有一定的网络开销。
五、过滤优化
将过滤器尽可能地移动到Spark任务的最开始,一般可以根据过滤掉的数据量,来使用coalesce方法减少分区数量,该方法不会执行数据Shuffle,而是将同一节点上的多个分区合并到一个分区中,减少分区数量可以较少任务间的网络开销。
六、缓存
如果多次重复使用某一DataFrame,数据表或RDD,应将其缓存,但如果只用一次,缓存只会降低速度。
七、广播变量
如果某一大块数据被程序中的多个UDF访问,则可以将其广播让每个节点上存储一个只读副本,并避免在每项作业
中重新发送此数据。查找表和机器学习模型作为广播变量可能很有用