Spark读写Parquet文件时数据格式不一致问题咨询
问题描述
使用Spark/Scala读取Parquet文件myIntialFile.parquet,其Schema如下:
val df = spark.read.format("parquet").load("/usr/sample/myIntialFile.parquet") df.printSchema root |-- details: string (nullable = true) |-- infos: array (nullable = true) | |-- element: struct (containsNull = true) | | |-- text: string (nullable = true) | | |-- id: string (nullable = true) | | |-- value: string (nullable = true)
仅执行.select("infos")操作后将DataFrame写入sparkProcessedFile.parquet,Spark显示infos列Schema未变化,但用PyArrow读取两个文件对比Schema时发现差异:
import pyarrow.parquet as pa initialTable = pa.read_table('myIntialFile.parquet') initialTable.schema infos: list<array: struct<text: string, id: string, value: string> not null> sparkProcessedTable = pa.read_table('sparkProcessedFile.parquet') sparkProcessedTable.schema infos: list<element: struct<text: string, id: string, value: string>>
核心疑问:
- 为何会出现
list<array<struct>>与list<struct>的Schema差异? - 简单的
select操作为何会改变初始嵌套结构?
原因分析与解决建议
1. Spark与PyArrow的Parquet类型映射差异
Spark的array类型与PyArrow对Parquet元数据的解析逻辑存在不一致:
- 原始文件的
infos列在Parquet元数据中可能被标记为嵌套数组(array of array),但Spark读取时会自动扁平化冗余的内层array标识,将其解析为单层array<struct>。 - 执行
select("infos")写入新文件时,Spark会严格按照自身内存中的Schema生成Parquet元数据,移除了原始文件中的冗余array标记,导致PyArrow读取时解析为list<struct>。
2. Spark的Schema兼容处理逻辑
Spark在读取Parquet时会做兼容性适配:如果Parquet元数据中存在无实际意义的嵌套array层(比如数据本身是array<struct>但元数据标记为array<array<struct>>),Spark会自动忽略该层,展示为更简洁的Schema。但写入时不会保留这种冗余的元数据标记。
3. 验证与修复方案
- 确认原始数据结构:执行
df.select("infos").take(1)查看实际数据结构,判断原始数据是否真的是嵌套数组。若数据本身是单层array<struct>,说明是Parquet元数据的标记问题。 - 显式指定Schema读取:读取原始文件时显式定义Schema,避免Spark自动解析带来的差异:
import org.apache.spark.sql.types._ val customSchema = StructType(Seq( StructField("details", StringType), StructField("infos", ArrayType(StructType(Seq( StructField("text", StringType), StructField("id", StringType), StructField("value", StringType) )), containsNull = true)) )) val df = spark.read.schema(customSchema).format("parquet").load("/usr/sample/myIntialFile.parquet")
- 统一PyArrow解析逻辑:若需要PyArrow读取的Schema与Spark一致,可在读取时使用新数据集API:
initialTable = pa.read_table('myIntialFile.parquet', use_legacy_dataset=False)
内容的提问来源于stack exchange,提问作者ilmar
相关产品推荐
相关产品推荐

