簡體   English   中英

無法將有序數據寫入火花中的鑲木地板

[英]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的列。 您沒有使用桶裝所以bucketingIdExpressionsortColumns是不是在這個例子中有關與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.

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