一、RDD持久化
1、不使用RDD持久化的問題
2、RDD持久化原理
Spark非常重要的一個功能特性就是可以將RDD持久化在內存中。當對RDD執行持久化操作時,每個節點都會將自己操作的RDD的partition持久化到內存中,並且在之后對
該RDD的反復使用中,直接使用內存緩存的partition。這樣的話,對於針對一個RDD反復執行多個操作的場景,就只要對RDD計算一次即可,后面直接使用該RDD,而不
需要反復計算多次該RDD。
巧妙使用RDD持久化,甚至在某些場景下,可以將spark應用程序的性能提升10倍。對於迭代式算法和快速交互式應用來說,RDD持久化,是非常重要的。
要持久化一個RDD,只要調用其cache()或者persist()方法即可。在該RDD第一次被計算出來時,就會直接緩存在每個節點中。而且Spark的持久化機制還是自動容錯的,
如果持久化的RDD的任何partition丟失了,那么Spark會自動通過其源RDD,使用transformation操作重新計算該partition。
cache()和persist()的區別在於,cache()是persist()的一種簡化方式,cache()的底層就是調用的persist()的無參版本,同時就是調用persist(MEMORY_ONLY),將數據持久化到內存
中。如果需要從內存中清楚緩存,那么可以使用unpersist()方法。
Spark自己也會在shuffle操作時,進行數據的持久化,比如寫入磁盤,主要是為了在節點失敗時,避免需要重新計算整個過程。
3、RDD持久化
------java實現-------- package cn.spark.study.core; import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; /** * RDD持久化 * @author Administrator * */ public class Persist { public static void main(String[] args) { SparkConf conf = new SparkConf() .setAppName("Persist") .setMaster("local"); JavaSparkContext sc = new JavaSparkContext(conf); // cache()或者persist()的使用,是有規則的 // 必須在transformation或者textFile等創建了一個RDD之后,直接連續調用cache()或persist()才可以 // 如果你先創建一個RDD,然后單獨另起一行執行cache()或persist()方法,是沒有用的 // 而且,會報錯,大量的文件會丟失 JavaRDD<String> lines = sc.textFile("D://test-file//spark.txt").cache(); long beginTime = System.currentTimeMillis(); long count = lines.count(); System.out.println(count); long endTime = System.currentTimeMillis(); System.out.println("cost " + (endTime - beginTime) + " milliseconds."); beginTime = System.currentTimeMillis(); count = lines.count(); System.out.println(count); endTime = System.currentTimeMillis(); System.out.println("cost " + (endTime - beginTime) + " milliseconds."); sc.close(); } }
4、RDD持久化策略
RDD持久化是可以手動選擇不同的策略的。比如可以將RDD持久化在內存中、持久化到磁盤上、使用序列化的方式持久化,多持久化的數據進行多路復用。
只要在調用persist()時傳入對應的StorageLevel即可。
5、如何選擇RDD持久化策略?
- 默認情況下,性能最高的當然是MEMORY_ONLY,但前提是內存必須足夠足夠大,可以綽綽有余地存放下整個RDD的所有數據。因為不進行序列化與反序列化操作,就避免了這部分的性能開銷;對這個RDD的后續算子操作,都是基於純內存中的數據的操作,不需要從磁盤文件中讀取數據,性能也很高;而且不需要復制一份數據副本,並遠程傳送到其他節點上。但是這里必須要注意的是,在實際的生產環境中,恐怕能夠直接用這種策略的場景還是有限的,如果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的級別,必須將所有數據都復制一份副本,並發送到其他節點上,數據復制以及網絡傳輸會導致較大的性能開銷,除非是要求作業的高可用性,否則不建議使用。