PySpark加载单分区数据至Hive表时的S3异常问题咨询
问题
在PySpark中基于Amazon S3创建Parquet分区表并加载数据时出现异常:创建分区表后尝试加载单个分区的数据,却发现S3源路径下的所有数据(包括未指定的分区)都被移动到表指定路径,且表中无数据加载成功,同时源目录文件被移动/删除。疑问如下:
- 这是加载分区Parquet文件的预期行为吗?
- 源文件是否应该被移动/删除?
操作步骤及代码
1. 生成并保存分区数据到S3
# 创建带分区字段的DataFrame df = spark.createDataFrame([("a", 1, "x"), ("b", 2, "y"), ("c", 3, "y")], ["c1", "c2", "p"]) # 以Parquet格式分区保存到S3 df.write.format("parquet").mode("overwrite").partitionBy("p").save("s3://bucket/data/")
此时S3目录结构:
- s3://bucket/data/p=x/
- part-00001.snappy.parquet
- s3://bucket/data/p=y/
- part-00002.snappy.parquet
- part-00003.snappy.parquet
2. 创建Hive表并加载指定分区数据
# 创建分区表,指定存储位置 CREATE TABLE src (c1 string,c2 int) PARTITIONED BY (p string) STORED AS parquet LOCATION 's3://bucket/new/' # 尝试加载p=x分区的数据 LOAD DATA INPATH 's3://bucket/data/' INTO TABLE src PARTITION (p='x') # 查询表数据 SELECT * FROM src
查询输出为空:
+---+---+---+ | c1| c2| p| +---+---+---+ +---+---+---+
3. 执行LOAD DATA后的S3目录结构
- s3://bucket/new/
- p=x/
- p=x/
- part-00001.snappy.parquet
- p=y/
- part-00002.snappy.parquet
- part-00003.snappy.parquet
源路径s3://bucket/data/下的文件已被全部移动。
- p=x/
- p=x/
解答
1. 这不是预期行为,问题出在路径指定错误
LOAD DATA INPATH 's3://bucket/data/'会递归移动该路径下的所有内容(包括子目录p=y)到目标分区p=x的目录中,导致:
- 原本的
p=y分区文件被嵌套在p=x目录下,Spark/Hive无法识别这种不符合规范的分区结构,因此查询不到数据 - 源路径下的所有文件被移动,这是
LOAD DATA针对分布式存储(如S3)的默认行为
2. 源文件被移动是LOAD DATA的默认行为
LOAD DATA命令在处理分布式存储时,默认执行移动操作而非复制,目的是避免大量数据复制带来的开销。如果需要保留源文件,不能使用LOAD DATA,需改用其他方案。
正确加载单个分区数据的方式
方式一:指定具体分区子路径
直接把INPATH指向目标分区的子目录,仅移动指定分区的文件:
LOAD DATA INPATH 's3://bucket/data/p=x/' INTO TABLE src PARTITION (p='x')
此方式会将p=x的文件移动到表的对应分区目录,源路径的p=y分区会被保留,且表能正常识别数据。
方式二:用INSERT INTO + SELECT保留源文件
如果需要保留源文件,先创建外部表指向源数据,再插入目标表:
# 创建外部表指向源数据位置 CREATE EXTERNAL TABLE src_temp (c1 string,c2 int) PARTITIONED BY (p string) STORED AS parquet LOCATION 's3://bucket/data/' # 修复分区,让Hive识别已存在的分区 MSCK REPAIR TABLE src_temp # 插入指定分区数据到目标表 INSERT INTO src PARTITION (p='x') SELECT c1,c2 FROM src_temp WHERE p='x'
此方式不会移动源文件,而是复制数据到目标表位置。
方式三:直接添加分区(无数据移动)
如果无需复制数据,仅让目标表访问源分区数据,可直接添加分区指向源路径:
ALTER TABLE src ADD PARTITION (p='x') LOCATION 's3://bucket/data/p=x/'
此方式完全不会修改源文件,目标表会直接读取源路径的数据。
内容的提问来源于stack exchange,提问作者Geruh
相关产品推荐
相关产品推荐

