1.处理同一份数据的话,建议不要重复创建RDD===>复用同一个RDD(对同一个RDD要进行多次操作)===>就对RDD进行持久化
rdd.cache()//cache是persist无参的版本,也就是默认的持久化级别
rdd.persist()
rdd.unPersist()//卸载
2.持久化级别:
MEMEORY_ONLY:要有足够大的内存支撑其持久化操作,将rdd中的未经序列化的java/scala对象持久化到内存中,如果数据量大,内存不够,超出内存容纳范围的数据是不会进行持久化操作的。
需要注意的是:因为是未经序列化的数据,所以在rdd中有大量的java/scala对象,每一个java对象都有一个固定的大小数据来描述该对象(对象的元数据),如果rdd中有1亿条数据,就有1亿个Java对象,会有1亿*xByte的额外数据,会对内存造成非常大的压力,一旦jvm不够用,就要启动守护线程gc(garbage collection),gc有个特点叫"Stop the world",也就是说所有工作线程需要等待gc线程执行完毕才能开始工作,如果频繁gc操作,spark作业效率会非常低,同时内存压力比较大,还可能出现OOM(out of memory)异常。
MEMEORY_AND_DISK:和MEMEORY_ONLY不同的是,内存不够用时,会把超出的部分缓存到磁盘中,但也是未经序列化的对象。
MEMEORY_ONLY_SER:和MEMEORY_ONLY相比,只是进行了序列化,对于缓存的数据,RDD对应的每一个partition都是一个字节数组。
MEMEORY_AND_DISK_SER:在MEMEORY_AND_DISK的基础上进行了序列化
DISK_ONLY:不用,spark是基于内存的,快速,仅仅缓存在磁盘中有时还不如重新计算一遍
MEMEORY_ONLY_2:真真正正的土豪玩的,有了一份缓存后再复制一份,将该份数据发送/复制到其它节点,内存要求太高,同时对网络IO要求也高,其好处也是不言而喻的,高可靠,高容错性,一个节点挂掉,咱不怕
具体要采取何种持久化策略,需要因情况不同而实施不同策略。一般可以这么做:
1.缓存的数据量小,追求效率,可以考虑MEMEORY_ONLY(往往不会)
2.采取MEMEORY_ONLY_SER,但需要考虑序列化和反序列化开销
3.如果存不下,用MEMEORY_AND_DISK_SER,同样考虑序列化和反序列化开销,因部分缓存磁盘,访问速率变慢
4.借助第三方的软件架构来进行数据的持久化,比如hbase,redis,es,ingit
我们需要尽量避免shuffle操作,原则上能替代就替代,比如使用map-side端的操作去代替执行拥有shuffle的join操作。需要将一个rdd中的数据,存放到广播变量中,再使用map操作来模拟join,但是在涉及广播变量和持久化的时候需要注意一点:
如果要进行广播或者持久化的数据较大,则不适合此种方式,易出现频繁gc操作,也容易导致OOM异常
尽量使用高性能的算子:mapPartitions--->map foreachPartitions--->foreach repartitionAndSort---->repartition和sort操作等等。
但是同样在操作这些带有partition的算子的时候,需要注意分区数据量的问题,一旦数据量过大极容易出现OOM异常
5.spark.serializer对应的解决方案:
默认是org.apache.spark.serializer.JavaSerializer,但是Java这种序列化有优点,但也有缺点
优点是:稳定;
缺点就是序列化的效率比较低,同时序列化之后数据体积还是比较大的,比较占用空间
设置conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
使用Kryo的序列化方案:
org.apache.spark.serializer.KryoSerializer。它解决了java的序列化的缺点,也就是说能给序列化带来高效率和小体积,序列化之后的体积要比Java小10倍。但是在带来高性能,低体积的同时也就伴随着一个缺点,不稳定,并不是所有的数据都可能被序列化成功。
设置conf.registerKryoClasses(Array(classOf[MyStudent], classOf[MyClass]))
在选择使用哪个序列化的时候,就考虑:稳定,效率,体积。
6.并行度---->就在一般算子的第二个参数,就是设置当前RDD的partition个数,需要几个task去执行(默认partition---task----thread一对一)
7.数据的本地性
PROCESS ----->(数据和计算代码在同一个JVM)
NODE ----->(数据和计算代码在同一个节点)
RACK ----->(数据和计算代码在同一个机架/机柜)
8.优化结构,还在努力中
9.数据倾斜(data skew)
谈到大数据计算,不得不说的一个问题,也是大数据计算中一个最棘手的问题之一,此时Spark作业的性能会比期望差很多。数据倾斜调优,就是使用各种技术方案解决不同类型的数据倾斜问题,以保证Spark作业的性能。
数据倾斜只会发生在shuffle过程中,比如join、reduceByKey、groupByKey等等
数据倾斜的原因:
1.数据倾斜的原理:其实就是在shuffle过程中,将各个节点上相同的key拉去到同一节点的一个task处理的时候,由于一个key 对应的数据量非常大,就会发生数据倾斜,这个key对应有的task就会运行特别慢,点像木桶原理,整个spark作业是由这个最慢的task决定的。
2.数据倾斜,spark作业变慢,还可能导致OOM异常
比如下图hello7次在一个task,word和you分别在一个task,整个spark作业速度取决于hello这个task的速度
10.一些常用的并且可能会触发shuffle操作的算子:
distinct ------->就是sql中的distinct去重
groupByKey ------->就是sql中的groupBy
reduceByKey ------->groupBy之后对key对应的value执行reduce操作
aggregateByKey ------->groupBy之后对key对应的value执行aggregate聚合操作
join ------->就是sql中join
cogroup ------->
repartition ------->重分区,常用在filter之后,和coalesce一样