背景
工作中使用Spark Streaming處理實時數(shù)據(jù)流,發(fā)現(xiàn)所處理的數(shù)據(jù)量與所消耗的時間很不對等,如下圖:

區(qū)區(qū)幾KB的數(shù)據(jù),簡單的mapToPair操作,竟然耗時4~5秒,很不合理。
于是,點擊進去看Stage詳情:

可見所有Task的耗時都是毫秒級的,怎么整個Stage就需要4秒呢?
通過查看EventTimeline,發(fā)現(xiàn)竟然有3秒的空窗期,這3秒內(nèi),沒有任何Task再執(zhí)行,而正是這3秒導致整個Stage的耗時增加。

解決方法
啟動Spark任務時,設置參數(shù)spark.locality.wait=0s即可。
什么是 locality wait ?
為什么會等待3秒呢?
原來這是Spark的一個任務管理策略。Spark把Stage拆解成N個Task,那么這N個Task要交給哪些節(jié)點去處理,就有說法了。
考慮到不同節(jié)點之間數(shù)據(jù)轉移、復制的帶寬成本會比較高,所以應盡量避免數(shù)據(jù)在不同節(jié)點之間流轉。那對應的策略就是:數(shù)據(jù)在哪個節(jié)點上,就把Task分配到對應的節(jié)點上,這樣就避免了不必要的網(wǎng)絡傳輸。
那么,這就引出另外一個問題,數(shù)據(jù)所在的節(jié)點比較忙,壓力比較大,沒有資源來執(zhí)行你分配的任務怎么辦?
Spark的策略是:等你一會兒。
這就是spark.locality.wait參數(shù)的含義了。這個參數(shù)默認值是3秒,那么就會等待數(shù)據(jù)節(jié)點3秒鐘。在3秒內(nèi)數(shù)據(jù)節(jié)點有資源并且成功創(chuàng)建了任務,那么就省去了網(wǎng)絡傳輸;如果3秒內(nèi)沒有創(chuàng)建成功,那么就把任務分派給其他有資源的節(jié)點去完成了。
如何調優(yōu)?
等待3秒,這是Spark的默認策略。實際應用中,我們應該根據(jù)具體的數(shù)據(jù)情況來適當調整這個參數(shù)。
以本文背景為例,每次處理就區(qū)區(qū)幾KB的數(shù)據(jù),就算走網(wǎng)絡和很快就完成了,肯定是遠小于3秒,那么這種情況就可以調到0秒,也就是不要管帶寬壓力,直接給按資源情況分配任務。
而如果這個數(shù)據(jù)很大,網(wǎng)絡傳輸?shù)脑捄臅r遠超3秒,這是可以適當調大spark.locality.wait,已避免網(wǎng)絡傳輸。
按照官方的建議,如果Jobs處理時間很短(小于2秒),就把這個值調小,甚至是0;如果Jobs處理時間很長,也不在乎多等幾秒,那么就適當調大。