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操作時,進行數據的持久化,比如寫入磁盤,主要是爲了在節點失敗時,避免需要重新計算整個過程。RDD持久化是可以手動選擇不同的策略的。比如可以將RDD持久化在內存中、持久化到磁盤上、使用序列化的方式持久化,多持久化的數據進行多路複用。只要在調用persist()時傳入對應的StorageLevel即可

持久化級別 含義
MEMORY_ONLY 以非序列化的Java對象的方式持久化在JVM內存中。如果內存無法完全存儲RDD所有的partition,那麼那些沒有持久化的partition就會在下一次需要使用它的時候,重新被計算。
MEMORY_AND_DISK 同上,但是當某些partition無法存儲在內存中時,會持久化到磁盤中。下次需要使用這些partition時,需要從磁盤上讀取。
MEMORY_ONLY_SER 同MEMORY_ONLY,但是會使用Java序列化方式,將Java對象序列化後進行持久化。可以減少內存開銷,但是需要進行反序列化,因此會加大CPU開銷。
MEMORY_AND_DSK_SER 同MEMORY_AND_DSK。但是使用序列化方式持久化Java對象。
DISK_ONLY 使用非序列化Java對象的方式持久化,完全存儲到磁盤上
MEMORY_ONLY_2 MEMORY_AND_DISK_2 等等 如果是尾部加了2的持久化級別,表示會將持久化數據複用一份,保存到其他節點,從而在數據丟失時,不需要再次計算,只需要使用備份數據即可。
MEMORY_AND_DSK_SER 同MEMORY_AND_DSK。但是使用序列化方式持久化Java對象。
MEMORY_AND_DSK_SER 同MEMORY_AND_DSK。但是使用序列化方式持久化Java對象。

Spark提供的多種持久化級別,主要是爲了在CPU和內存消耗之間進行取捨。下面是一些通用的持久化級別的選擇建議:
1、優先使用MEMORY_ONLY,如果可以緩存所有數據的話,那麼就使用這種策略。因爲純內存速度最快,而且沒有序列化,不需要消耗CPU進行反序列化操作。
2、如果MEMORY_ONLY策略,無法存儲的下所有數據的話,那麼使用MEMORY_ONLY_SER,將數據進行序列化進行存儲,純內存操作還是非常快,只是要消耗CPU進行反序列化。
3、如果需要進行快速的失敗恢復,那麼就選擇帶後綴爲_2的策略,進行數據的備份,這樣在失敗時,就不需要重新計算了。
4、能不使用DISK相關的策略,就不用使用,有的時候,從磁盤讀取數據,還不如重新計算一次。

簡單代碼驗證

  // cache()或者persist()的使用,是有規則的
            // 必須在transformation或者textFile等創建了一個RDD之後,直接連續調用cache()或persist()纔可以
            // 如果你先創建一個RDD,然後單獨另起一行執行cache()或persist()方法,是沒有用的
            // 而且,會報錯,大量的文件會丟失
            JavaRDD lines = sc.textFile("E:\\server.log").cache();
            long beginTime = System.currentTimeMillis();
            System.out.println(lines.count());
            long endTime = System.currentTimeMillis();
            System.out.println("cost " + (endTime - beginTime) + " milliseconds.");
            beginTime = System.currentTimeMillis();
            System.out.println(lines.count());
            endTime = System.currentTimeMillis();
            System.out.println("cost " + (endTime - beginTime) + " milliseconds.");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章