首页/数据/数据工程与 ETL 速查/小文件问题与治理
管道基础52 条

小文件问题与治理

大量小文件会抬高元数据压力与扫描开销:每读一个文件都有打开、寻址与统计成本。治理思路是写入侧控制文件数量,读侧用 AQE/合并任务兜底。

小文件问题是什么

分布式计算的每个文件都要经历打开、寻址、读取元数据这几步固定开销,文件越小,这些开销占的比例越高。当成千上万个几 KB 的文件堆在一个分区里,任务启动会变慢、NameNode 或元数据服务压力上升、扫描效率明显下降。成因通常是流式小批写入、写并行度远大于数据量、分区粒度过细。治理分两头:写入侧控制文件数量,读侧用合并与自适应机制兜底。

基本信息

成因流式小批写入、并行度过多、分区粒度过细
影响元数据压力、任务启动慢、扫描效率低
写入侧手段控制并行度、按目标文件大小重分区
读侧手段AQE 自动合并、定期 compaction、小文件合并任务
理想大小按引擎与存储权衡,通常控制在百兆量级

Spark AQE 合并小分区

Spark AQE 合并小分区
spark.sql.adaptive.enabled=true
spark.sql.adaptive.coalescePartitions.enabled=true
spark.sql.adaptive.advisoryPartitionSizeInBytes=64MB
spark.sql.shuffle.partitions=200          # 写前按目标文件大小反推
# 写出时控制文件数:df.repartition(n).write.partitionBy("dt").parquet(path)

Hive ORC 合并相邻小文件

Hive ORC 合并相邻小文件
-- 合并单分区内的相邻小文件(仅支持 ORC 表)
ALTER TABLE dwd_order PARTITION (dt='2026-10-11') CONCATENATE;

-- 写入侧自动合并:小文件在 map 端先合并再落盘
SET hive.merge.mapfiles = true;
SET hive.merge.mapredfiles = true;
SET hive.merge.size.per.task = 134217728;
SET hive.merge.smallfiles.avgsize = 67108864;

典型使用场景

  • 流式接入落湖:每分钟写一次会累积大量小文件,需要周期性 compaction
  • 批量回补历史:并行度高导致单分区文件数暴涨,回补后要合并
  • 查询变慢排查:先统计目标分区的文件数与平均大小,确认是不是小文件问题
  • 分区粒度评估:按业务查询模式确认是否需要小时级分区,避免过度细分
  • 表格式维护:Iceberg、Hudi、Delta 都提供小文件合并动作,需要纳入日常调度

治理手段对照

手段位置适用
降低并行度/重分区写入任务批任务写出文件数过多
AQE 自动合并分区Spark 3.x 查询计划聚合后分区碎小
ORC CONCATENATEHive 表维护ORC 格式的静态分区
表格式 compactionIceberg/Hudi/Delta流式持续写入的表
定期合并任务调度层需要跨引擎统一的兜底方案
按「天+小时+分钟」过多粒度分区是常见制造小文件的方式,分区粒度要与查询模式匹配。
合并带来额外读写,会短暂放大集群负载;流式表通常按写入节奏安排 compaction 周期,并避开业务高峰与关键报表时间点。

易错点

  • 用简单的 getmerge 合并大目录会单点拷贝,成本高且难增量
  • 流式写入不做 compaction,小文件数量会随时间线性增长
  • 只调查询参数指望读侧变快,根因在写入侧时效果极其有限
  • 合并时并发写入同一分区,可能产生重复数据或覆盖掉新写入
  • 把文件数当作唯一指标,忽略单文件过小与分区数量过多的叠加影响

数据来源与参考

  • Apache Spark 官方文档:Adaptive Query Execution
  • Apache Hive LanguageManual:ORC CONCATENATE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌