[英]How do I reduce the number of files in my foundry dataset?
我的數據集有 20000 個文件,每個文件都很小。 我將如何減少文件數量以及最佳數量是多少?
執行此操作的最直接方法是在轉換結束時顯式執行repartition()
(如果分區計數從原始數字嚴格減少,則為coalesce()
。
這需要是您返回/寫出結果之前的最后一次調用。
這看起來像:
# ...
@transform_df(
# ... inputs
)
def my_compute_function(my_inputs):
# ... my transform logic ...
df = df.coalesce(500)
# df = df.repartition(500) # this also works but is slightly slower than coalesce
return df
這是稱為“bucketing”以供參考的先行步驟。
最佳存儲桶數取決於您操作的數據規模。 成功構建后,通過觀察磁盤上數據集的總大小來計算最佳存儲區數量有些簡單。
如果您的數據集大小為 128GB,您最終會希望得到 128MB 的文件,因此您的存儲桶數為:
128 GB * (1000 MB / 1 GB) * (1 file / 128MB) -> 1000 files
注意:這不是一個精確的計算,因為由於 Snappy + Parquet 寫出中使用的數據壓縮,更改存儲桶計數后的最終數據集大小會有所不同。 您會注意到文件大小與您預期的略有不同,因此在上述示例中您可能最終需要 1100 或 900 個文件
由於這是一個我不得不多次解決的問題,因此我決定編寫一份更詳細的指南,其中包含許多不同的技術、優缺點以及存在的理由。
避免使用包含許多文件的數據集有幾個很好的理由:
以包含許多文件的數據集結束通常是由以下三個原因之一引起的:
groupBy
時(這意味着 shuffle),spark 默認選擇將數據重新分區為 200 個新分區,這對於增量轉換來說太多了。 由於分區不當(下面討論),轉換也可能產生過多的輸出文件。接下來,我將列出我所知道的減少數據集中文件數的所有方法,以及它們的缺點和優點,以及適用時的一些特征。
最好的選擇之一是首先避免擁有許多文件。 當從類似文件系統的源中攝取許多文件時,像“連接轉換器”這樣的 magritte 轉換器可能有助於將許多 CSV、JSON 或 XML 文件組合成一個單一的文件。 在適用時,串聯然后應用 gzip 轉換器是一種特別有效的策略,因為它通常可以將 XML 和類似文本格式的大小減少 94% 左右。
主要的限制是要應用這個,你需要
也可以將許多文件壓縮成更少的文件(使用 .tar.bz2、.tar.gz、.zip、.rar 等格式),但這隨后需要知道這種文件格式的下游轉換並手動解包(文檔中提供了一個示例),因為代工廠無法透明地提供這些檔案中的數據。 但是,沒有預制的 magritte 處理器可以執行此操作,並且在我應用此技術的情況下,我會在攝取之前使用 bash 腳本來執行此任務,這無疑不太理想。
Foundry 中有一種新機制可以將您寫入的數據集與讀取的數據集分離。 本質上有一個后台作業在運行,它在您追加文件時將文件洗牌到優化的索引中,以便數據集的讀取(大部分)可以轉到這個優化的索引,而不是作者留下的(通常有點隨意)數據布局。
這具有多種好處(例如自動生成針對最常見讀取模式優化的數據布局),其中之一是它可以在后台“壓縮”您的數據集。
從這樣的數據集中讀取時,您的讀取基本上會命中索引以及輸入數據集(其中包含尚未被后台進程合並到索引中的任何文件。)
最大的優點是這會在后台自動發生,無論您的數據攝取或轉換有多混亂,您都可以簡單地寫出數據(在寫入時沒有性能命中並盡快將數據提供給消費者),同時仍然結束一個很好的分區數據集,文件很少(最終。)
這里的主要限制是,這僅適用於 Spark 本身可以理解的格式的數據集,例如 parquet、avro、json、csv ......在攝入前將它們打包成例如鑲木地板。 這樣,隨着時間的推移,鑄造廠仍然可以合並多個這些鑲木地板文件。
此功能尚未對最終用戶完全可用(但計划默認為所有內容啟用。)如果您認為這是您的管道之一最理想的解決方案,您的 palantir POC 可以與團隊一起開票啟用此功能。
合並是 spark 中的一個操作,它可以減少分區的數量而沒有廣泛的依賴(spark 中唯一的這樣的操作)。 合並很快,因為它最大限度地減少了混洗。 它的工作方式與以前的 spark 版本相比發生了變化(並且那里有很多相互矛盾的信息),但它通常比repartition
快。 但是,它有一個很大的警告:它減少了整個 transform 的並行度。
即使您在寫入數據之前的最后coalesce
,spark 也會調整整個查詢計划以使用更少的分區,從而減少使用的執行程序,這意味着您獲得的並行度更低。
重新分區是類似的,但它插入了一個完整的洗牌階段。 這帶來了更高的性能成本,但這意味着從這個階段出來的數據基本上可以保證分區良好(無論輸入如何)。 雖然repartition
本身有點昂貴,但它不會遇到在整個轉換過程中減少並行度的問題。
這意味着總體而言,如果您最終寫出的數據量與您之前所做的工作量相比不是那么大,那么使用repartition
不是coalesce
通常會獲得更好的性能,因為處理數據的能力更多的執行者最終超過了shuffle的缺點。 根據我的經驗,除非您的轉換非常簡單,否則repartition
通常會在這里勝出。
一個值得討論的特定用例是增量管道。 如果您的增量管道相對簡單並且只執行例如映射和過濾,那么進行coalesce
就可以了。 然而,許多增量管道也會讀取非常大的數據集的快照視圖。 例如,增量管道可能會接收一行新數據,並讀取整個先前的輸出數據集(可能數百萬行),因此請查看該行是否已存在於輸出數據集中。 如果它已經存在,則不發出任何行,如果它不存在,則附加該行。 將一小段增量數據與大型靜態數據集等連接起來時,會發生類似的情況。
在這種情況下,轉換是增量的,但它仍然受益於高並行性,因為它仍然處理大量數據。
我的粗略指導方針是:
repartition
到一個合理的數字coalesce(1)
repartition(1)
如果寫入速度/流水線延遲非常重要,那么這些選項都不能接受。 在這種情況下,我會考慮背景壓縮。
作為前一點的擴展,為了保持增量管道的高性能,我喜歡在它們上安排定期快照,這允許我每隔一段時間重新分區數據集,執行基本上是“壓縮”的操作。
我已經在這里描述了如何設置它的機制: 如何強制增量 Foundry Transforms 作業以非增量方式構建而不影響語義版本?
我通常會在周末安排快照。 在整個星期中,管道中的每個數據集(可能有數百個數據集)將累積數千或數萬個事務和文件。 然后在周末,隨着計划的快照在管道中滾動,每個數據集將被重新分區,例如,一百個文件。
最近,AQE 在代工廠中可用。 AQE 本質上(出於本次討論的目的)將coalesce
操作注入到您已經進行了混洗操作的階段,具體取決於前一個操作的結果。 這通常會改善分區(以及文件數量),但據報道在極少數情況下也會使情況變得更糟(但我自己沒有觀察到這一點)。
默認情況下啟用 AQE,但如果您想嘗試禁用它,您可以將其應用到轉換中。
分桶和分區與此討論有些相關,因為它們主要是關於布置數據以優化讀取數據的特定方法。 這些技術目前都不適用於增量管道。
一個常見的錯誤是寫出由高基數列分區的數據集,例如時間戳。 在具有 1000 萬個唯一時間戳的數據集中,這將導致(至少)輸出數據集中有 1000 萬個文件。
在這些情況下,應該修復轉換,並通過應用保留刪除舊事務(包含數百萬個文件)。
其他壓縮數據集的技巧也是可能的,例如創建“環回”轉換以讀取先前的輸出並將其重新分區,或者手動打開數據集上的事務以重新寫入它。
這些是非常hacky的,但在我看來是不可取的,應該避免。 如今,背景壓縮主要以更優雅、更可靠和更少黑客的方式解決了這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.