1、背景+方法
1、为什么要持久化
action操作触发,会触发RDD的重算,对于相同数据执行多次action操作,要多次从FS中读取数
据,这就降低了性能。因而我们可以通过cache和persist方法将RDD缓存到内存或者磁盘中。
重算:重新将FS上的数据读入RDD再根据依赖关系推导出所需要的RDD。
2、cache() && persist()
cache:
底层调用了persist方法
默认StorageLevel为MEMORY_ONLY且不可更改
persist:
默认StorageLevel为MEMORY_ONLY,可以自由设置StorageLevel
注意:
懒执行,持久化的最小单位为分区
spark会自动监控各节点的缓存情况,请采用LRU移除一些数据
应用程序所有job结束,持久化数据丢失
也可以通过unpersist()方法手动移除缓存,它是立即执行的
持久化可以赋值给一个变量,下个job中使用这个变量就是使用的持久化的数据。
注意:
sparkssql中的cache是不是transformation,所以它是立即执行的。
3、checkpoint()
执行流程:
1、设置文件保存目录,sc.SetCheckpointDir(“外部目录”)
2、执行job时对要执行checkpoint的rdd做标记
3、job执行完回溯查看哪些rdd有chekc标记,
4、重新执行一次job用来计算checkpoint的RDD的数据。
特点:
懒执行,基于DISK_ONLY
可以将数据存在外部文件系统的磁盘中
应用程序所有job执行完数据不丢失(和cache和persist的区别),用来保存rdd状态,在
sparkstreaming应用很多
优化:
在需要checkpoint()的rdd上执行cache,重新job时速度会加快
2、StorageLevel:
Storage Level | Meaning |
---|---|
MEMORY_ONLY | Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, some partitions will not be cached and will be recomputed on the fly each time they're needed. This is the default level .将RDD以未被序列化java对象方式存储在jvm中。如果RDD在内存中存不下了,则它的某一些分区在被用到的时候会被重新计算而不存储在内存中。这是默认的存储级别。 |
MEMORY_AND_DISK | Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, store the partitions that don't fit on disk, and read them from there when they're needed .将RDD以未被序列化java对象方式存储在jvm中。如果RDD在内存中存不下了,则它的某一些分区会被存储在磁盘中,需要用到的时候在磁盘中读取即可。 |
MEMORY_ONLY_SER (Java and Scala) | Store RDD as serialized Java objects (one byte array per partition). This is generally more space-efficient than deserialized objects, especially when using a fast serializer, but more CPU-intensive to read .将RDD以序列化java对象方式存储在jvm中。序列化对象存储比反序列化对象存储更节省空间,特别是使用了快速的序列化程序之后,但它更消耗cpu。 |
MEMORY_AND_DISK_SER (Java and Scala) | Similar to MEMORY_ONLY_SER, but spill partitions that don't fit in memory to disk instead of recomputing them on the fly each time they're needed .和 MEMORY_ONLY_SER类似,只不过对于溢出的分区并不是重新计算而是将其缓冲到磁盘中。 |
DISK_ONLY | Store the RDD partitions only on disk 只将数据存在磁盘中。. |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | Same as the levels above, but replicate each partition on two cluster nodes .和以上提到的级别都类似,只不过会在两个集群节点间复制每一个分区,即每个分区都有两个副本。 |
OFF_HEAP (experimental) | Similar to MEMORY_ONLY_SER, but store the data in off-heap memory. This requires off-heap memory to be enabled 和.MEMORY_ONLY_SER类似,但是是将数据存储在堆外内存中,这需要对堆外内存设置为可以。 |
注:
spark也会自动地将shuffle过程中的一些中间数据进行缓存,即使用户没有执行persist操作,这样设置的原因在于防止在shuffle过程中数据丢失或者节点宕机而导致RDD重算从而影响性能。但我们仍然建议用户使用persist等操作。
3、如何选择
A。如果默认的存储级别是ok的,就不要更改。
B。如果内存不太够,则可以使用MEMORY_ONLY_SER加上一个快速的序列化框架,比如avro。
C。不要轻易将数据写到磁盘,除非对数据的计算成本十分高昂,列如数据计算要过滤掉大量数据。
重算RDD和从磁盘读取RDD速度差不多。
D。可以使用副本。原则虽然说每一个存储级别都提供了完善的RDD重算机制,但存在副本发生错误时不用等待重算。
=====================================================================================================
Spark分区原理
1、分区的作用:
有助于并分布式数据处理和节点间数据的传输。
2、分区的方式
每个HDFS的block对应一个数据切片,一个数据切片对应一个RDD分区。
rdd会默认进行分区,你也可以手动指定。
默认一个分区对应一个task,分区数=min{默认的并行度,2}
task的默认并行度如下:
参数:spark.default.parallelism
该参数用于设置应用程序的默认task数量。
这个参数极为重要,如果不设置可能会直接影响你的Spark作业性能。
如果是local模式,则取决于local[n]中的n
如果是mesos模式,则并行度为8
如果是其他模式,则取决于max{所有executor节点总核数,2}
3、获取rdd分区数
rdd.getNumPartitions
rdd.partitions.size
4、分区对性能的影响
分区太小,task太少,执行慢
分区太大,task太多,执行慢
设置分区的最佳实践:
CPU数量的2到3倍
至少executor的数量
5、指定分区数
scala>sc.textFile("hdfs://Linux001/data/a.txt", 20)
如果a.txt是压缩文件,则应该通过重分区算子指定分区
重分区算子:
coalesce算子:
参数1:分区数 参数2:是否开启shuffle,默认false不开启
coalsece算子设置的分区大于原分区数,则第二个参数必须指定为true
repartition算子:
参数1:分区数 参数2:是否开启shuffle,默认true开启
repartition算子设置的分区小于原分区数,则第二个参数必须指定为false
一般:
coalesce算子用于减少分区,repartition用于增多分区