spark.sql.files.maxPartitionBytes无法正确限制分区大小的原因探究
回答
核心逻辑:Spark 读取Parquet的分区规则
Spark 处理Parquet文件时,有两个关键限制和逻辑:
- 不可拆分Row Group:Parquet的Row Group(行组)是存储的最小不可拆分单元,Spark无法将单个Row Group拆分成多个分区,无论
maxPartitionBytes设得多小。 - maxPartitionBytes是建议值:该参数只是Spark分区规划的目标参考,最终分区大小还会结合
spark.sql.files.openCostInBytes(默认4MB,衡量打开文件的开销)权衡——Spark不会为了严格贴合目标值,过度增加文件打开次数。
实验1疑问解答
1. 分区大小为何超过90MB?
你的每个155MB左右的Parquet文件,内部包含两个Row Group:一个约127MB(接近Parquet默认的128MB Row Group大小),一个约29MB。由于Spark不能拆分Row Group,那个127MB的Row Group只能完整作为一个分区,自然超过了90MB的设定值。
2. 为何不合并29MB的小分区?
Spark合并小分区的前提是:合并后的总大小接近maxPartitionBytes,且不会带来过高的文件打开开销。90MB的目标下,至少需要3个29MB的小分区才能凑到接近目标的大小,但这些小分区来自不同的Parquet文件,合并它们需要打开多个文件,openCostInBytes的存在让Spark认为这种合并的开销大于收益,因此不会主动合并。
实验2疑问解答
1. 分区数量为何变为22?
原始的16个Parquet文件共包含32个Row Group(16个~127MB大Row Group + 16个~29MB小Row Group):
- 所有~127MB的大Row Group因超过120MB,只能单独成为分区(共16个);
- 小Row Group会被尝试合并:4个29MB的总和是116MB,接近120MB的目标,因此部分小Row Group被合并成单个分区。16个小Row Group按4个一组合并,可得到4个分区,但实际因Row Group大小略有差异,最终合并出6个小分区,加上16个大分区,总数量为22个。
2. 分区大小为何仍有偏差?
分区只能基于已有的Row Group组合:
- 大Row Group本身就超过120MB,无法拆分只能保留;
- 小Row Group合并时,只能按整数个组合,无法精确凑到120MB(比如3个29MB是87MB,4个是116MB),因此分区大小必然存在偏差。
优化建议
如果想要更精准的分区控制,建议在写入Parquet文件时,通过parquet.block.size参数(单位字节)调整Row Group大小,让其接近你期望的maxPartitionBytes值。这样读取时,Spark就能直接基于匹配的Row Group创建符合预期的分区。
内容的提问来源于stack exchange,提问作者Nikolaos
相关产品推荐
相关产品推荐

