SparkTask未序列化問題

爲了執行做業,Spark將RDD操做的處理分解爲tasks,每一個task由Executor執行。在執行以前,Spark會計算task的閉包。閉包是Executor在RDD上進行計算的時候必須可見的那些變量和方法(在這種狀況下是foreach())。閉包會被序列化併發送給每一個Executor。apache

若是在涉及到的全部的變量中有任何不支持序列化或沒有指明如何序列化本身時,你就會遇到這樣的錯誤:編程

org.apache.spark.SparkException: Task not serializable

在編寫Spark程序中,因爲在map等算子內部使用了外部定義的變量和函數,從而引起Task未序列化問題。然而,Spark算子在計算過程當中使用外部變量在許多情形下確實在所不免,好比在filter算子根據外部指定的條件進行過濾,map根據相應的配置進行變換等。爲了解決上述Task未序列化問題,這裏對其進行了研究和總結。網絡

  出現「org.apache.spark.SparkException: Task not serializable」這個錯誤,通常是由於在map、filter等的參數使用了外部的變量,可是這個變量不能序列化(不是說不能夠引用外部變量,只是要作好序列化工做,具體後面詳述)。其中最廣泛的情形是:當引用了某個類(常常是當前類)的成員函數或變量時,會致使這個類的全部成員(整個類)都須要支持序列化。雖然許多情形下,當前類使用了「extends Serializable」聲明支持序列化,可是因爲某些字段不支持序列化,仍然會致使整個類序列化時出現問題,最終致使出現Task未序列化問題。閉包

引用成員變量的實例分析
  如上所述,因爲Spark程序中的map、filter等算子內部引用了類成員函數或變量致使須要該類全部成員都須要支持序列化,又因爲該類某些成員變量不支持序列化,最終引起Task沒法序列化問題。解決方法,將不須要序列化的的成員變量使用關鍵字「@transent」標註。併發

引用成員函數的實例分析
  成員變量與成員函數的對序列化的影響相同,即引用了某類的成員函數,會致使該類全部成員都支持序列化。函數

如上所述,引用了某類的成員函數,會致使該類及全部成員都須要支持序列化。所以,對於使用了某類成員變量或函數的情形,首先該類須要序列化(extends Serializable),同時須要對某些不須要序列化的成員變量標記以免爲序列化形成影響。
map等算子內部能夠引用外部變量和某類的成員變量,可是要作好該類的序列化處理。首先是該類須要繼承Serializable類,此外,對於類中某些序列化會出錯的成員變量作好處理,這也是Task未序列化問題的主要緣由。對於出現這類問題,首先查看未能序列化的成員變量是哪一個,對於能夠不須要序列化的成員變量可以使用「@transent」標註。 
此外,也不是map操做所在的類必須序列化不可(繼承Serializable類),對於不須要引用某類成員變量或函數的情形,就不會要求相應的類必須實現序列化。spa

解決辦法與編程建議
  承上所述,這個問題主要是引用了某類的成員變量或函數,而且相應的類沒有作好序列化處理致使的。所以解決這個問題無非如下兩種方法:
不在(或不直接在)map等閉包內部直接引用某類(一般是當前類)的成員函數或成員變量
若是引用了某類的成員函數或變量,則需對相應的類作好序列化處理
(一)不在(或不直接在)map等閉包內部直接引用某類成員函數或成員變量
若是程序依賴的值相對固定,可取固定的值,或定義在map、filter等操做內部,或定義在scala 
object對象中(相似於Java中的static變量),把它聲明爲一個全局靜態的變量就能夠繞過序列化
若是依賴值須要程序調用時動態指定(以函數參數形式),則在map、filter等操做時,可不直接引用該成員變量,而是在函數中根據成員變量的值從新定義一個局部變量,這樣map等算子就無需引用類的成員變量。scala


(二)若是引用了某類的成員函數或變量,則需對相應的類作好序列化處理
  對於這種狀況,則需對該類作好序列化處理,首先該類繼承序列化類,而後對於不能序列化的成員變量使用「@transent」標註,告訴編譯器不須要序列化。 
此外若是能夠,可將依賴的變量獨立放到一個小的class中,讓這個class支持序列化,這樣作能夠減小網絡傳輸量,提升效率。code

相關文章
相關標籤/搜索