Databricks写入Azure Blob Storage生成空parquet文件的解决方法
问题原因
这不是Databricks平台bug,是Spark分布式写入的原生机制导致的:
- 调用
df.write.parquet()时传入的路径,Spark会默认将其作为输出目录处理,而非单文件路径 - 写入时Spark会在该路径下创建同名文件夹,根据DataFrame的分区数量,在文件夹内生成若干带随机文件名的parquet分片(这些分片才是存储实际数据的文件),最后写入
_SUCCESS状态标记文件 - 你看到的路径下那个空的
agg_DF.parquet文件,是写入前残留的路径占位文件,不包含任何实际数据
相关异常表现参考:

实现固定名单文件输出的方案
根据数据量大小二选一即可:
方案1:小数据量场景(单文件<10GB,日常作业首选)
如果聚合结果数据量不大,单Executor内存可以承载,就通过「单分区写入临时目录->重命名分片文件->清理临时文件」的方式实现固定名单文件输出,可直接替换原有代码:
# 配置参数 OUTPUT_PARQUET_FILENAME = 'agg_DF.parquet' container_name = 'xxxxx' account_name = 'yyyy' base_path = f"wasbs://{container_name}@{account_name}.blob.core.windows.net/" final_file_path = base_path + OUTPUT_PARQUET_FILENAME temp_dir_path = base_path + f"temp_{OUTPUT_PARQUET_FILENAME}" # 提前清理残留文件 dbutils.fs.rm(temp_dir_path, recurse=True) dbutils.fs.rm(final_file_path, recurse=True) # 合并为1个分区写入临时目录,数据量过大会触发OOM spark_DF = spark.createDataFrame(agg_df).repartition(1) spark_DF.write.parquet(temp_dir_path, mode="overwrite") # 找到临时目录内的parquet分片,移动并重命名为目标文件名 for file_info in dbutils.fs.ls(temp_dir_path): if file_info.path.endswith(".parquet"): dbutils.fs.mv(file_info.path, final_file_path) break # 删除临时目录 dbutils.fs.rm(temp_dir_path, recurse=True)
注意事项:
- 必须加
repartition(1)将所有数据合并到单个分区,才能保证临时目录下只有1个parquet分片,重命名后数据完整 - 如果单parquet文件超过10GB,不建议用这个方案,容易触发Executor OOM,写入和读取性能都会明显下降
方案2:大数据量场景
如果聚合后数据量很大,单分区无法承载,不要强行输出单个大文件:
- 直接将
agg_DF.parquet作为目录名,保留原有分区写入逻辑即可,不需要额外改文件名规则 - 下游读取时直接指向这个目录路径即可,Spark、Pandas等主流计算框架都原生支持直接读取目录下所有parquet分片,读取代码和读取单个parquet文件完全一致,不需要额外遍历文件:
# 直接读目录即可拿到全量数据 res_df = spark.read.parquet(final_file_path)
- 如果下游系统不支持读取目录,建议按业务维度(比如日期、区域)将文件拆分为多个固定命名的小文件,不要强行输出几十GB以上的单parquet文件。
你原有代码里用
.repartition('blob_date')会按blob_date字段做哈希分区,最终生成的parquet分片数等于blob_date的去重值个数,本身就是多分区分布式写入,不可能直接生成单个固定命名的parquet文件,这是Spark的底层设计决定的,不是配置错误或者平台故障。
内容的提问来源于stack exchange,提问作者user9532692
相关产品推荐
相关产品推荐

