Spark存储Parquet表的机制及列数对存储效率影响相关疑问
抽取10列后Parquet表大小远超预期的原因
- 未开启压缩或压缩算法选择不合理:Parquet默认未开启压缩时存储的是原始编码后的数据,占用会很高;如果仅使用Snappy这类低压缩比的算法,相比Gzip、ZSTD等高压缩比算法,存储占用会高出2~5倍。
- 抽取的10列本身是原表的高存储占用列:如果原表剩下的90多列都是低基数的枚举、时间戳、数值类小字段,90%以上的存储都被你选中的10个STRING长文本、JSON、大二进制类字段占用,那抽取出的表占原表20%的大小是完全符合预期的。
- 小文件冗余:Spark写入时如果没有配置小文件合并,大量小文件会带来重复的文件头、页脚、元数据块开销,极端情况下元数据开销能占到总存储的30%以上。
- 未做数据排序/分桶优化:Parquet的字典编码、行程编码等压缩算法高度依赖同列数据的聚集度,如果同列相似值分散存储,压缩效率会下降30%~70%,直接导致文件大小膨胀。
- 原始表大小统计偏差:如果原10TB的大小是经过了归档级高压缩、冷存储降效后的计费大小,或者你计算原表列均占比时没有考虑不同列的存储差异,也会导致预期出现偏差。
Parquet存储效率与列数的关系
Parquet的存储效率不会随列数增加而主动提升,两者没有直接的正相关,常见的认知偏差来源于以下场景:
- 列存的核心是同列数据连续存储,宽表中通常会存在大量低基数维度列,这类列的重复率极高,压缩比可达1:10甚至更高,会拉低整体的单位存储成本,让你产生“列越多越省空间”的错觉。
- 列数过多时,Parquet文件中存储的每列统计信息、索引信息等元数据也会随之增加,反而会带来额外的存储开销,列数过万的极端场景下,元数据开销占比甚至会超过实际数据的存储占比。
- 你遇到的情况就是典型的反例:你抽取的10列都是高基数、大体积的字段,本身压缩比就远低于剩下的低基数字段,自然会出现“列少了但存储占比没有按比例下降”的情况。
Spark 写入Parquet表的实现机制
- Spark首先会读取你配置的表Schema,或者从数据源中推断Schema,对齐每列的数据类型,避免类型隐式转换导致的存储膨胀。
- 之后根据你配置的分区、分桶规则,将数据分发到对应Task中,每个Task独立处理自己负责的数据分片,生成独立的Parquet文件,默认每个分片对应一个文件。
- 每个Parquet文件内部按行组分块,默认行组大小为128MB,每个行组内部按列单独存储为数据页:每列的数据先按配置的编码规则(字典编码、行程编码、增量编码等)编码,再调用配置的压缩算法(Snappy/Gzip/ZSTD等)压缩后写入。
- 单个文件写完后,会在页脚写入该文件的完整元数据,包括每列的偏移量、统计信息(最大值、最小值、null值计数等)、Schema信息等。
- 如果开启了Parquet元数据合并配置,Spark最终会汇总所有生成文件的元数据,生成统一的全局元数据,避免后续读取时扫描全量文件的元数据。
内容的提问来源于stack exchange,提问作者Murali
相关产品推荐
相关产品推荐

