PySpark写入CSV至S3时生成_$folder$对象问题咨询
PySpark写入S3生成
{bucket-name}_$folder$对象的问题解决 问题原因
这是Hadoop S3A客户端与S3交互时的已知行为,并非PySpark CSV写入逻辑的问题。早期S3不原生支持目录结构,Hadoop为了模拟传统文件系统的目录概念,会创建$folder$后缀的占位符对象来标识“目录存在”。即使现在S3已支持通过前缀模拟目录,部分版本的Hadoop S3A客户端仍会默认生成这类对象。
解决办法
- 通过Spark配置禁用标记生成(推荐):
在Spark会话初始化时添加以下配置,阻止S3A客户端创建目录标记对象:
注意:from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("WriteCSVToS3") \ .config("spark.hadoop.fs.s3a.directory.marker.create", "false") \ .config("spark.hadoop.fs.s3a.directory.marker.retention", "never") \ .getOrCreate()fs.s3a.directory.marker.create参数在Hadoop 3.2及以上版本可用,低版本可仅使用fs.s3a.directory.marker.retention=never。 - 写入后手动清理:
用AWS CLI或boto3删除冗余对象,示例:
或Python代码(boto3):aws s3 rm s3://{bucket-name}/{bucket-name}_$folder$import boto3 s3 = boto3.client('s3') s3.delete_object(Bucket='{bucket-name}', Key='{bucket-name}_$folder$') - 调整写入路径格式:
尝试去掉路径末尾的斜杠,将写入路径改为s3://{bucket-name}/{path}(而非带斜杠的s3://{bucket-name}/{path}/),部分场景下可避免生成根目录级的标记对象。
更稳定的替代方案
- 使用湖仓格式(Delta Lake/Iceberg):
这类格式对S3的适配更成熟,不会生成冗余的目录标记对象,还支持ACID事务、版本控制等特性,适合频繁更新的场景。示例(Delta Lake):df.write.format("delta").mode("overwrite").save("s3://{bucket-name}/{path}/") - 本地临时目录中转上传:
先将DataFrame写入集群本地临时目录,再用boto3或AWS CLI将文件上传到S3,完全控制文件生成逻辑:# 写入本地临时目录 df.coalesce(1).write.csv("/tmp/temp_csv/", mode="overwrite", header=True) # 上传到S3 import boto3 s3 = boto3.resource('s3') s3.meta.client.upload_file('/tmp/temp_csv/part-00000.csv', '{bucket-name}', '{path}/output.csv') - 使用Spark SQL外部表:
通过创建外部表的方式写入,可避免冗余对象生成:CREATE EXTERNAL TABLE IF NOT EXISTS csv_table (col1 string, col2 int) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION 's3://{bucket-name}/{path}/' TBLPROPERTIES ('skip.header.line.count'='1'); INSERT OVERWRITE TABLE csv_table SELECT * FROM your_df;
内容的提问来源于stack exchange,提问作者user1554876
相关产品推荐
相关产品推荐

