spark學習筆記--Spark調優與除錯

zxrui發表於2018-07-12

Spark調優與除錯

使用SparkConf配置Spark

在 Scala 中使用 SparkConf 建立一個應用

// 建立一個conf物件
val conf = new SparkConf()
conf.set("spark.app.name", "My Spark App")
conf.set("spark.master", "local[4]")
conf.set("spark.ui.port", "36000") // 過載預設埠配置

// 使用這個配置物件建立一個SparkContext
val sc = new SparkContext(conf)

在執行時使用標記設定配置項的值

$ bin/spark-submit \
--class com.example.MyApp \
--master local[4] \
--name "My Spark App" \
--conf spark.ui.port=36000 \
myApp.jar

執行時使用預設檔案設定配置項的值

$ bin/spark-submit \
--class com.example.MyApp \
--properties-file my-config.conf \
myApp.jar

#Contents of my-config.conf
spark.master    local[4]
spark.app.name  "My Spark App"
spark.ui.port   36000

Spark的執行過程

  • 使用者程式碼定義RDD的有向無環圖
    • RDD上的操作會建立出新的RDD,並引用它們的父節點,這樣就建立處理一個圖
    • 排程器為有向圖中的每個 RDD 輸出計算步驟,步驟中包括 RDD 上需要應用於每個分割槽的任務。然後以相反的順序執行這些步驟,計算得出最終所求的 RDD。(RDD圖與執行步驟的對應關係並不一定是一一對應的,比如排程器進行流水線執行或者把多個RDD合併到一個步驟中)。

  • 行動操作把有向無環圖強制轉譯為執行計劃

    • Spark 排程器提交一個作業(特定的行動操作生成的步驟的集合)來計算所有必要的 RDD。這個作業會包含一個或多個步驟,每個步驟其實也就是一波並行執行的計算任務。一個步驟對應有向無環圖中的一個或多個 RDD,一個步驟對應多個 RDD 是因為發生了流水線執行。toDebugString()可以檢視RDD的譜系(RDD的依賴關係)
    • 一個物理步驟會啟動很多工,每個任務都是在不同的資料分割槽上做同樣的事情。
      1.從資料儲存(該RDD是一個輸入RDD)/已有RDD(已經快取的資料)/資料混洗的輸出中獲取輸入資料
      2.執行必要的操作來計算出這些操作所代表的RDD,如filter()和map()函式 3.把輸出寫到一個資料混洗檔案中,寫入外部儲存貨發回驅動器程式(如count()操作)
  • 任務於叢集中排程並執行

    步驟是按順序處理的,任務則獨立地啟動來計算出 RDD 的一部分。一旦作業的最後一個步驟結束,一個行動操作也就執行完畢了。

查詢資訊

Spark在執行時記錄詳細的進度資訊和效能指標,可以在Spark的網頁使用者介面以及驅動器程式和執行器程式生成的日誌檔案中找到

驅動器程式和執行器程式日誌

Spark 日誌檔案的具體位置取決於以下部署模式。

  • 在 Spark 獨立模式下,所有日誌會在獨立模式主節點的網頁使用者介面中直接顯示。這些日誌預設儲存於各個工作節點的 Spark 目錄下的 work/ 目錄中。
  • 在 Mesos 模式下,日誌儲存在 Mesos 從節點的 work/ 目錄中,可以通過 Mesos 主節點使用者介面訪問。
  • 在 YARN 模式下,最簡單的收集日誌的方法是使用 YARN 的日誌收集工具(執行 yarn logs -applicationId )來生成一個包含應用日誌的報告。這種方法只有在應用已經完全完成之後才能使用,因為 YARN 必須先把這些日誌聚合到一起。

關鍵效能考量

並行度

  • 並行度會從兩方面影響程式的效能

    • 當並行度過低時,Spark 叢集會出現資源閒置的情況
    • 當並行度過高時,每個分割槽產生的間接開銷累計起來就會更大
  • Spark 提供了兩種方法來對操作的並行度進行調優

    • 在資料混洗操作時,使用引數的方式為混洗後的 RDD 指定並行度
    • 對於任何已有的 RDD,可以進行重新分割槽(repartition())來獲取更多或者更少的分割槽數

序列化格式

當 Spark 需要通過網路傳輸資料,或是將資料溢寫到磁碟上時,Spark 需要把資料序列化為二進位制格式
序列化會在資料進行混洗操作時發生,此時有可能需要通過網路傳輸大量資料。預設情況下,Spark 會使用 Java 內建的序列化庫。Spark 也支援使用第三方序列化庫 Kryo

使用Kryo序列化工具並註冊所需類

val conf = new SparkConf()
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// 嚴格要求註冊類
conf.set("spark.kryo.registrationRequired", "true")
conf.registerKryoClasses(Array(classOf[MyClass], classOf[MyOtherClass]))

記憶體管理

Spark對於記憶體管理有幾種不同的途徑:

  • RDD儲存

    當呼叫 RDD 的 persist() 或 cache() 方法時,這個 RDD 的分割槽會被儲存到快取區中。Spark 會根據 spark.storage.memoryFraction 限制用來快取的記憶體佔整個 JVM 堆空間的比例大小。如果超出限制,舊的分割槽資料會被移出記憶體。

  • 資料混洗與聚合的快取區

    當進行資料混洗操作時,Spark 會建立出一些中間快取區來儲存資料混洗的輸出資料。這些快取區用來儲存聚合操作的中間結果,以及資料混洗操作中直接輸出的部分快取資料。Spark 會嘗試根據 spark.shuffle.memoryFraction 限定這種快取區記憶體佔總記憶體的比例。

  • 使用者程式碼

    Spark 可以執行任意的使用者程式碼,所以使用者的函式可以自行申請大量記憶體。例如,如果一個使用者應用分配了巨大的陣列或者其他物件,那這些都會佔用總的記憶體。使用者程式碼可以訪問 JVM 堆空間中除分配給 RDD 儲存和資料混洗儲存以外的全部剩餘空間。

在預設情況下,Spark 會使用 60%的空間來儲存 RDD,20% 儲存資料混洗操作產生的資料,剩下的 20% 留給使用者程式

硬體供給

提供給 Spark 的硬體資源會顯著影響應用的完成時間。影響叢集規模的主要引數包括分配給每個執行器節點的記憶體大小、每個執行器節點佔用的核心數、執行器節點總數,以及用來儲存臨時資料的本地磁碟數量。

  • 執行器節點的記憶體可以通過spark.executor.memory配置項或者spark-submit 的 --executor-memory 標記來設定

  • 執行器節點的數目以及每個執行器程式的核心數的配置選項則取決於各種部署模式。在 YARN 模式下,你可以通過 spark.executor.cores 或 --executor-cores 標記來設定執行器節點的核心數,通過 --num-executors 設定執行器節點的總數

  • 一般來說,更大的記憶體和更多的計算核心對 Spark 應用會更有用處。

    • Spark 的架構允許線性伸縮;雙倍的資源通常能使應用的執行時間減半。
    • 在調整叢集規模時,需要額外考慮的方面還包括是否在計算中把中間結果資料集快取起來。如果確實要使用快取,那麼記憶體中快取的資料越多,應用的表現就會越好。
    • Spark 使用者介面中的儲存頁面會展示所快取的資料中有哪些部分保留在記憶體中。你可以從在小叢集上只快取一部分資料開始,然後推算快取大量資料所需要的總記憶體量。
    • 除了記憶體和 CPU 核心,Spark 還要用到本地磁碟來儲存資料混洗操作的中間資料,以及溢寫到磁碟中的 RDD 分割槽資料。因此,使用大量的本地磁碟可以幫助提升 Spark 應用的效能。

“越多越好”的原則在設定執行器節點記憶體時並不一定適用。使用巨大的堆空間可能會導致垃圾回收的長時間暫停,從而嚴重影響 Spark 作業的吞吐量。有時,使用較小記憶體(比如不超過 64GB)的執行器例項可以緩解該問題

相關文章