簡體   English   中英

Spark RDD:多個reducebykey或僅一次

[英]Spark RDD: multiple reducebykey or just once

我有如下代碼:

// make a rd according to an id
def makeRDD(id:Int, data:RDD[(VertexId, Double)]):RDD[(Long, Double)] = { ... }  
val data:RDD[(VertexId, Double)] = ... // loading from hdfs
val idList = (1 to 100)
val rst1 = idList.map(id => makeRDD(id, data)).reduce(_ union _).reduceByKey(_+_)
val rst2 = idList.map(id => makeRDD(id, data)).reduce((l,r) => (l union r).reduceByKey(_+_))

rst1和rst2獲得樣本結果。 我以為rst1需要更多的內存(100次),但只有一個reduceByKey轉換; 但是,rst2需要較少的內存,但需要更多的reduceByKey轉換(99次)。 那么,這是時間與空間的權衡游戲嗎?

我的問題是:我上面的分析是正確的,還是Spark對待以內部相同的方式轉換動作?

PS:rst1聯合所有子rdd然后reduceByKey,其中reduceByKey reduce 之外 rst2 reduceByKey一一對應,其中reduceByKey reduce 里面

長話短說,這兩種解決方案效率都相對較低,但第二種解決方案比第一種解決方案差。

讓我們從回答最后一個問題開始。 對於低級RDD API,只有兩種類型的全局自動優化(代替):

  • 使用顯式或隱式緩存的任務結果,而不是重新計算完整沿襲
  • 將不需要隨機播放的多個轉換組合到單個ShuffleMapStage

其他所有內容幾乎都是定義DAG的順序轉換。 這與限制性更高的高級DatasetDataFrame )API DataFrame ,后者對轉換進行了特定假設並執行了執行計划的全局優化。

關於您的代碼。 第一個解決方案的最大問題是,當您應用迭代union時,沿襲越來越大。 它使某些事情(例如,故障恢復成本很高)以及由於RDD是遞歸定義的,可能會因StackOverflow異常而失敗。 較不嚴重的副作用是分區數量的增加,但在隨后的減少中似乎沒有得到補償*。 由於較長的RDD沿襲,您會在我對Stackoverflow的回答中找到更詳細的解釋,但是您真正需要的是這樣的單個union

sc.union(idList.map(id => makeRDD(id, data))).reduceByKey(_+_)

假設您應用了真正的歸約功能,這實際上是一個最佳解決方案。

第二種解決方案顯然也遇到了同樣的問題,但是情況變得更糟。 盡管第一種方法只需要兩個階段並進行一次混洗,但這需要為每個RDD進行混洗。 由於分區的數量在增加,並且您使用默認的HashPartitioner每條數據都必須多次寫入磁盤,並且很可能在網絡上多次洗牌。 忽略低級計算,每條記錄會被洗凈O(N)次,其中N是您合並的RDD數。

關於內存使用情況,在不了解更多數據分布的情況下並不明顯,但是在最壞的情況下,第二種方法可能表現出明顯更差的行為。

如果+在恆定的空間上工作,則減少的唯一要求是一個哈希圖,用於存儲地圖端合並的結果。 由於分區是作為數據流處理的,而沒有將完整的內容讀取到內存中,因此這意味着每個任務的總內存大小將與唯一鍵的數量成正比,而不與數據量成正比。 由於第二種方法需要執行更多任務,因此總體內存使用量將高於第一種情況。 平均而言,由於數據是部分組織的,因此可能會稍微好一些,但不太可能補償額外的費用。


*如果您想了解它如何影響整體性能,您可以看到使用連接時Spark迭代時間呈指數增長。這是一個稍有不同的問題,但是應該讓您了解為什么控制分區數很重要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM