![](/img/trans.png)
[英]OutOfMemoryError when using PySpark to read files in local mode
[英]Overwrite mode in loop when partition using pyspark
#Start and End is a range of dates.
start = date(2019, 1, 20)
end = date(2019, 1, 22)
for single_date in daterange(start, end):
query = "(SELECT ID, firstname,lastname,date FROM dbo.emp WHERE date = '%s' ) emp_alias" %((single_date).strftime("%Y-%m-%d %H:%M:%S"))
df = spark.read.jdbc(url=jdbcUrl, table=query, properties=connectionProperties)
df.write.format("parquet").mode("ignore").partitionBy("Date").save("/mnt/data/empData.parquet")
我有一個表中的天數數據,我需要按日期分區的鑲木地板文件。 我必須按天循環保存,因為數據很大,而且我不能將所有日子都像年份數據一樣放在一個數據框中。 我嘗試了所有保存模式。 在“忽略”模式下,它會保存第一天。 在“覆蓋”模式下,它保存最后一天。 在“追加”模式下,它添加數據。 我需要的是,如果當天的數據可用,則應忽略當天的數據並保留已有的數據,但如果數據不可用,則在按日期分區的鑲木地板文件中創建。 請幫忙。
當前沒有 PySpark SaveMode 允許您在插入新分區的同時保留現有分區,如果您還想使用 Hive 分區(這是您在調用partitionBy
方法時所要求的)。 請注意,有一個選項可以做相反的事情,即覆蓋某些分區中的數據,同時保留 DataFrame 中沒有數據的那些(將配置設置"spark.sql.sources.partitionOverwriteMode"
設置為"dynamic"
並在寫入數據集時使用SaveMode.Overwrite
)。
您仍然可以通過首先創建一組所有現有分區來實現您想要的。 您可以使用 PySpark 或使用任何允許您在文件系統(如 Azure Data Lake Storage Gen2)或鍵值存儲(如 AWS S3)中執行列表操作的庫來做到這一點。 獲得該列表后,您可以使用它來過濾新數據集以查找仍要寫入的數據。 這是一個只有 PySpark 的例子:
In [1]: from pyspark.sql.functions import lit
...: df = spark.range(3).withColumn("foo", lit("bar"))
...: dir = "/tmp/foo"
...: df.write.mode("overwrite").partitionBy("id").parquet(dir) # initial seeding
...: ! tree /tmp/foo
...:
...:
/tmp/foo
├── id=0
│ └── part-00001-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet
├── id=1
│ └── part-00002-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet
├── id=2
│ └── part-00003-5d14d286-81e1-4eb1-969e-c0d8089712ce.c000.snappy.parquet
└── _SUCCESS
3 directories, 4 files
In [2]: df2 = spark.range(5).withColumn("foo", lit("baz"))
...: existing_partitions = spark.read.parquet(dir).select("id").distinct()
...: df3 = df2.join(existing_partitions, "id", how="left_anti")
...: df3.write.mode("append").partitionBy("id").parquet(dir)
...: spark.read.parquet(dir).orderBy("id").show()
...:
...:
+---+---+
|foo| id|
+---+---+
|bar| 0|
|bar| 1|
|bar| 2|
|baz| 3|
|baz| 4|
+---+---+
如您所見,只添加了 2 個分區。 已經存在的那些被保留了下來。
現在,獲取existing_partitions
DataFrame 需要讀取數據。 不過,Spark 實際上不會讀取所有數據,只會讀取分區列和元數據。 如前所述,您也可以使用與數據存儲位置相關的任何 API 來獲取這些數據。 在我和你的特殊情況下,看到你如何寫入 Databricks 上的/mnt
文件夾,我可以簡單地使用內置的 Python 函數os.walk
: dirnames = next(os.walk(dir))[1]
,並從中創建了一個 DataFrame 。
順便說一下,你得到你所看到的行為的原因是:
忽略模式
在“忽略”模式下,它會保存第一天。
因為您使用的是 for 循環並且輸出目錄最初可能不存在,所以將寫入第一個日期分區。 在 for 循環的所有后續迭代中,DataFrameWriter 對象將不再寫入,因為它認為那里已經有一些數據(一個分區,第一個日期)。
覆蓋模式
在“覆蓋”模式下,它保存最后一天。
實際上,它在 for 循環的每次迭代中都保存了一個分區,但是因為您指示 DataFrameWriter 進行覆蓋,它將刪除目錄中所有先前存在的分區。 所以看起來只寫了最后一個。
追加模式
在'append'模式下,它添加數據。這個不需要進一步解釋。
一個建議:可能不需要多次從數據庫讀取(使用 for 循環創建多個不同的查詢和 jdbc 連接)。 您可能會更新查詢以包含WHERE date BETWEEN %(start) AND %(end)
,完全刪除 for 循環並享受高效寫入。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.