[英]Can't write ordered data to parquet in spark
我正在使用 Apache Spark 生成鑲木地板文件。 我可以毫無問題地按日期對它們進行分區,但在內部我似乎無法按正確的順序排列數據。
訂單似乎在處理過程中丟失了,這意味着鑲木地板元數據不正確(特別是我想確保鑲木地板行組反映排序順序,以便特定於我的用例的查詢可以通過元數據進行有效過濾)。
考慮以下示例:
// note: hbase source is a registered temp table generated from hbase
val transformed = sqlContext.sql(s"SELECT id, sampleTime, ... , toDate(sampleTime) as date FROM hbaseSource")
// Repartion the input set by the date column (in my source there should be 2 distinct dates)
val sorted = transformed.repartition($"date").sortWithinPartitions("id", "sampleTime")
sorted.coalesce(1).write.partitionBy("date").parquet(s"/outputFiles")
通過這種方法,我確實得到了正確的鑲木地板分區結構(按日期)。 更好的是,對於每個日期分區,我看到一個大的鑲木地板文件。
/outputFiles/date=2018-01-01/part-00000-4f14286c-6e2c-464a-bd96-612178868263.snappy.parquet
但是,當我查詢文件時,我看到內容亂序。 具體來說,“亂序”似乎更像是幾個有序的數據幀分區已合並到文件中。
Parquet 行組元數據顯示排序的字段實際上是重疊的(例如,特定 id 可以位於許多行組中):
id: :[min: 54, max: 65012, num_nulls: 0]
sampleTime: :[min: 1514764810000000, max: 1514851190000000, num_nulls: 0]
id: :[min: 827, max: 65470, num_nulls: 0]
sampleTime: :[min: 1514764810000000, max: 1514851190000000, num_nulls: 0]
id: :[min: 1629, max: 61412, num_nulls: 0]
我希望數據在每個文件中正確排序,以便每個行組中的元數據最小值/最大值不重疊。
例如,這是我想看到的模式:
RG 0: id: :[min: 54, max: 100, num_nulls: 0]
RG 1: id: :[min: 100, max: 200, num_nulls: 0]
...其中RG =“行組”。 如果我想要id = 75
,查詢可以在一行組中找到它。
我已經嘗試了上述代碼的許多變體。 例如有和沒有coalesce
(我知道合並是不好的,但我的想法是用它來防止改組)。 我也嘗試過sort
而不是sortWithinPartitions
(sort 應該創建一個完全有序的排序,但會導致許多分區)。 例如:
val sorted = transformed.repartition($"date").sort("id", "sampleTime")
sorted.write.partitionBy("date").parquet(s"/outputFiles")
給了我 200 個文件,太多了,而且它們仍然沒有正確排序。 我可以通過調整隨機大小來減少文件數,但我希望在寫入期間按順序處理排序(我的印象是寫入不會對輸入進行隨機排序)。 我看到的順序如下(為簡潔起見省略了其他字段):
+----------+----------------+
|id| sampleTime|
+----------+----------------+
| 56868|1514840220000000|
| 57834|1514785180000000|
| 56868|1514840220000000|
| 57834|1514785180000000|
| 56868|1514840220000000|
看起來它是交錯排序的分區。 所以我認為repartition
在這里沒有給我帶來任何好處,而且sort
似乎無法在寫入步驟中保持順序。
我讀過我想做的事情應該是可能的。 我什至嘗試過 Ryan Blue 的演示文稿“Parquet performance tuning: The missing guide”中概述的方法(不幸的是,它位於 OReily 付費牆后面)。 這涉及使用insertInto
。 在這種情況下,spark 似乎使用了舊版本的 parquet-mr,它破壞了元數據,我不知道如何升級它。
我不確定我做錯了什么。 我的感覺是我誤解了repartition($"date")
和sort
工作和/或交互的方式。
我將不勝感激任何想法。 為論文道歉。 :)
編輯:另請注意,如果我在transformed.sort("id", "sampleTime")
上執行show(n)
數據排序正確。 所以看起來問題發生在寫入階段。 如上所述,排序的輸出似乎在寫入過程中被打亂了。
問題是Spark在保存文件格式的時候,需要一定的順序,如果順序不滿足,Spark會在保存過程中根據要求對數據進行排序,忘記排序。 更具體地說,Spark 需要這個順序(這直接取自 Spark 2.4.4 的 Spark 源代碼):
val requiredOrdering = partitionColumns ++ bucketIdExpression ++ sortColumns
其中partitionColumns
是用於對數據進行partitionColumns
的列。 您沒有使用桶裝所以bucketingIdExpression
和sortColumns
是不是在這個例子中有關與requiredOrdering
將只有partitionColumns
。 所以如果這是你的代碼:
val sorted = transformed.repartition($"date").sortWithinPartitions("id",
"sampleTime")
sorted.write.partitionBy("date").parquet(s"/outputFiles")
Spark 將檢查數據是否按date
排序,而事實並非如此,因此 Spark 會忘記您的排序並按date
對其進行排序。 另一方面,如果您改為這樣做:
val sorted = transformed.repartition($"date").sortWithinPartitions("date", "id",
"sampleTime")
sorted.write.partitionBy("date").parquet(s"/outputFiles")
Spark 將再次檢查數據是否按date
排序,這次是(滿足要求),因此 Spark 將保留此順序,並在保存數據時不再進行排序。 所以我相信這種方式應該有效。
只是想法,在合並后排序:“.coalesce(1).sortWithinPartitions()”。 同樣預期的結果看起來很奇怪 - 為什么需要鑲木地板中的有序數據? 閱讀后排序看起來更合適。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.