为什么调优?
由于Spark的计算本质是基于内存的,所以Spark性能程序的性能可能因为集群中的任何因素出现瓶颈:CPU、网络带宽、或者是内存。
情况一:内存大能够容纳所有数据,那么网络传输和通信就会导致性能出现瓶颈。
情况二:内存小不能容纳所有数据(10亿级以上数据量),则需要对内存的使用进行性能优化。
调优:将重复使用的RDD进行持久化
Spark单个RDD多次执行原理:每次执行RDD进行算子操作时,都会重新从源头处计算一遍,计算出RDD,再对RDD执行算子操作。
调优建议:
对多次使用的RDD进行持久化,Spark会根据持久化策略将RDD中的数据保存到内存或磁盘中,以后对该RDD进行算子操作时,都是直接从内存或磁盘中提取持久化的RDD数据,然后执行算子。(减少多次计算该RDD)
cache机制:每计算出一个要cache的partition就直接cache到内存。(计算一次)
checkpoint:第一次计算时不会存储,会等job结束后启动专门的job去完成checkpoint。(计算两次)
改进:
使用checkpoint时加上cache,这样第二次运行的job则不再计算该RDD,而是直接读取cache可写入磁盘。
persist:cache则是persist的简化方式,cache底层是调用persist的无参构造器,无参构造器调用的是persist(StorageLevel.MEMORY_ONLY)。
persist的存储级别:
MEMORY_ONLY | 默认选项,RDD的(分区)数据直接以Java对象的形式存储于JVM的内存中,如果内存空间不足,某些分区的数据将不会被缓存,需要在使用的时候根据世代信息重新计算。 |
MYMORY_AND_DISK | RDD的数据直接以Java对象的形式存储于JVM的内存中,如果内存空间不中,某些分区的数据会被存储至磁盘,使用的时候从磁盘读取。 |
MEMORY_ONLY_SER | RDD的数据(Java对象)序列化之后存储于JVM的内存中(一个分区的数据为内存中的一个字节数组),相比于MEMORY_ONLY能够有效节约内存空间(特别是使用一个快速序列化工具的情况下),但读取数据时需要更多的CPU开销;如果内存空间不足,处理方式与MEMORY_ONLY相同。 |
MEMORY_AND_DISK_SER | 相比于MEMORY_ONLY_SER,在内存空间不足的情况下,将序列化之后的数据存储于磁盘。 |
DISK_ONLY | 仅仅使用磁盘存储RDD的数据(未经序列化)。 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | 以MEMORY_ONLY_2为例,MEMORY_ONLY_2相比于MEMORY_ONLY存储数据的方式是相同的,不同的是会将数据备份到集群中两个不同的节点,其余情况类似。 |
OFF_HEAP(experimental) | RDD的数据序例化之后存储至Tachyon。相比于MEMORY_ONLY_SER,OFF_HEAP能够减少垃圾回收开销、使得Spark Executor更“小”更“轻”的同时可以共享内存;而且数据存储于Tachyon中,Spark集群节点故障并不会造成数据丢失,因此这种方式在“大”内存或多并发应用的场景下是很有吸引力的。需要注意的是,Tachyon并不直接包含于Spark的体系之内,需要选择合适的版本进行部署;它的数据是以“块”为单位进行管理的,这些块可以根据一定的算法被丢弃,且不会被重建。 |
cache与checkpoint的区别
rdd.persist(StorageLevel.DISK_ONLY) 与 checkpoint 也有区别。前者虽然可以将 RDD 的 partition 持久化到磁盘,但该 partition 由 blockManager 管理。
一旦 driver program 执行结束,也就是 executor 所在进程 CoarseGrainedExecutorBackend stop,blockManager 也会 stop,被 cache 到磁盘上的 RDD 也会被清空(整个 blockManager 使用的 local 文件夹被删除)。
而 checkpoint 将 RDD 持久化到 HDFS 或本地文件夹,如果不被手动 remove 掉,是一直存在的,也就是说可以被下一个 driver program 使用,而 cached RDD 不能被其他 dirver program 使用。
案例:
package com.kevin.scala.tuning
import org.apache.spark.{SparkConf, SparkContext}
/**
* 将重复使用的RDD进行持久化
*/
object Persistence {
def main(args: Array[String]): Unit = {
val file = "DTSparkCore\\src\\main\\resources\\records.txt"
// 1.创建SparkConf
val conf = new SparkConf().setAppName("Persistence").setMaster("local")
// 2.创建SparkContext
val sc = new SparkContext(conf)
// 3.checkpoint持久化的存放路径
sc.setCheckpointDir("C:\\Users\\caonanqing\\Desktop\\persistence")
// 4.textFile读取文件数据,flatMap对每行数据初始化次数值为1,以K,V的形式返回,将数据放到缓存
val rdd = sc.textFile(file).flatMap(value => (List(value,1))).cache()
// 5.将rdd存储起来
rdd.checkpoint()
// 6.cache和checkpoint都是转换算子,需要执行算子来执行该操作
rdd.collect()
println("isCheckPointed: "+rdd.isCheckpointed)
println("checkpoint: "+rdd.getCheckpointFile)
// 7.关闭
sc.stop()
}
}