对共用的RDD进行持久化
这种情况是一定要避免的,一旦出现一个RDD重复计算的情况,就会导致性能急剧降低。比如:HDFS-->RDD1-->RDD2的时间是15分钟,那么此时就要执行两遍,变成30分钟
可以使用 cache()和 persist()对RDD进行持久化操作
其实 cache()就是 persist(MEMORY_NOLY),只使用内存进行持久化,但在实际的生产环境中,恐怕能够直接用这种策略的场景还是有限的,如果RDD中数据比较多时(比如几十亿),直接用这种持久化级别,会导致JVM的OOM内存溢出异常。
如果使用MEMORY_ONLY级别时发生了内存溢出,那么建议尝试使用MEMORY_ONLY_SER级别。该级别会将RDD数据序列化后再保存在内存中,此时每个partition仅仅是一个字节数组而已,大大减少了对象数量,并降低了内存占用。这种级别比MEMORY_ONLY多出来的性能开销,主要就是序列化与反序列化的开销。但是后续算子可以基于纯内存进行操作,因此性能总体还是比较高的。此外,可能发生的问题同上,如果RDD中的数据量过多的话,还是可能会导致OOM内存溢出的异常。
如果纯内存的级别都无法使用,那么建议使用MEMORY_AND_DISK_SER策略,而不是MEMORY_AND_DISK策略。因为既然到了这一步,就说明RDD的数据量很大,内存无法完全放下。序列化后的数据比较少,可以节省内存和磁盘的空间开销。同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘。
通常不建议使用DISK_ONLY和后缀为_2的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD。后缀为_2的级别,必须将所有数据都复制一份副本,并发送到其他节点上,数据复制以及网络传输会导致较大的性能开销,除非是要求作业的高可用性,否则不建议使用。
避免使用 shuffle类算子
使用广播变量来模拟使用join,使用情况:一个RDD比较大,一个RDD比较小。
join算子=广播变量+filter、广播变量+map、广播变量+flatMap
使用预聚合类算子
使用reduceByKey替代groupByKey
使用mapPartition替代map
使用foreachPartition替代foreach
filter后一定使用coalesce减少分区数
使用repartition和coalesce算子操作分区。