1.1避免创建重复RDD
通常指,在开发spark作业中,首先基于某个数据源,(如hive或hdfs文件)创建一个初始RDD,接着对这个RDD进行某个算子操作,然后得到下一个RDD,以此类推,循环往复,直到计算出我们需要的结果;在此过程中多个RDD会通过不同算子操作串起来。这个RDD串 `RDD lineage` 也就是RDD的血缘关系链。
**在我们开发中注意,对于同一份数据,只创建一个RDD,不能创建多个RDD来代表同一份据。
1.2尽可能复用同一个RDD
1.我们除了在开发中避免对同一份完全相同数据创建多个RDD之外,与此同时在数据执行算子还要尽可能复用一个RDD;
2.比如有一个RDD数据格式为k-v类型,另一个是单v类型,这两个RDD的value数据完全一样,那么此时我们可以只使用k-v类型的那个RDD,因为其中已经包含了另一个的数据,对于类似这种多个RDD有重叠或包含的情况,我们尽量复用一个RDD,这样可以减少算子执行次数,也可以减少RDD的数量。
1.3对于多次使用RDD进行持久化
1.3.1概述
1.当我们在spark代码中多次对一个RDD做算子操作后,就实现了spark作业的第一步优化,也即是尽可能复用RDD,此时就该在这个基础之上进行第二步优化,
也就是保证对一个RDD进行执行多次算子操作时,这个RDD本身仅仅被计算一次。
2.spark中对于一个RDD执行多次算子的默认原理是这样,每次你对一个RDD执行一个算子操作时,都会重新从源头计算一遍,计算出那个RDD来然后进行对这个RDD执行你的算子操作,这种方式性能很差的。
3.因此对于这种情况,我们建议是对多次使用RDD进行持久化,此时spark会根据你的持久化策略,将RDD中数据保存到内存或磁盘中,以后每次对这个RDD进行算子操作时,都会直接从内存或磁盘中提取持久化的RDD数据,然后执行算子,而不会从源头重新计算一遍这个RDD,再执行算子操作。
1.3.2持久化策略
1.3.3选择合适的持久化策略
1.默认情况下,性能最高当然是MEMORY_ONLY,但是前提是你的内存足够大,可以绰绰有余存放整个RDD的所有数据因为不进行序列化和反序列化操作就避免了这部分的性能开销;对这个RDD的后续算子操作都是基于内存的数据操作,不需要从磁盘文件中读取数据,性能也很高,而且不需要复制一份数据副本,并远程传送到其他节点上。但是这里必须要注意的是实际的生产环境恐怕能够直接用这种策略场景有限,如果RDD中数据比较多时,直接用这种持久化级别,会导致jvmdeOOM内存溢出异常。
2.如果使用MEMORY_ONLI级别时发生内存溢出,那么建议尝试使用MEMORY_ONLY_SER级别,该级别会将RDD数据序列化后再保存内存中,此时每个partition仅仅是一个字节数组而已,大大减少对象数量,并降低内存占用,这种级别比MEMORY_ONLY多出来性能开销,主要就是序列化和反序列化的开销,但后续算子可以基于纯内存进行操作,因此性能总体来说还是比较高的,此外可能发生的问题同上,如果RDD中数量过多的话,还是可能导致OOM异常。
3.如果纯内存级别都无法使用,而不MEMORY_AND_DISK策略,因为既然到了这一步就是说明RDD数据量大,内存无法完全放下,序列化后的数据比较少,可以节省内存和磁盘空间开销,同时该策略会优先尽量数据比较少,可以节省内存和磁盘空间开销,同时该策略会优先尽量尝试将数据缓存内存中,内存缓存不下会写入磁盘。
4.通常不建议使用DISK_ONLY和后缀为2的级别,因为完全基于磁盘文件进行数据读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD,后缀为2级别,必须将所有数据都复制一份副本,并且发送到其它节点上,数据复制以及网络传输会导致较大性能开销,除非要求作业高可用性,否则不建议使用。
1.3.4 gc回收过程