订阅更新

Spark 为什么会写出大量小文件?怎么控制?

面试问 126 约 3 分钟读完 3 次阅读

本文目录
  1. 面试怎么回答
  2. 一个 Task 通常对应一个输出文件
  3. 动态分区会再次放大
  4. 流式与频繁微批也会累积
  5. 写前收敛,写后治理
  6. 为什么不能只回答概念?
  7. 落地时我会检查什么?
  8. 一个典型场景

面试怎么回答

Spark 输出文件通常和写入 Task、动态分区数及写入批次有关。我会先看目标分区基数和数据量,再按目标分区 Repartition,合理设置 Shuffle 分区和文件大小;流式任务还要控制微批频率。冷分区可以定期 Compaction,但不会简单 Coalesce(1),因为它会损失并行度。最终用单分区文件数、平均大小和扫描开销做持续监控。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

一个分区一天只有几 GB,落盘后却出现上万个文件。上游任务看起来很快,下游列表、元数据和扫描性能却越来越差。

小文件数量通常由输出分区数、动态分区和写入批次共同决定。

治理 Spark 小文件要从写入并行度、分区基数和提交频率三处下手,而不是落盘后无限 Compaction。

一个 Task 通常对应一个输出文件

写入某个表分区时,每个执行 Task 都可能产生文件。Shuffle 分区设置过大、上游分区未收敛,会直接把文件数放大。

动态分区会再次放大

一个 Task 如果同时写多个业务分区,可能在每个分区下生成文件。高基数分区字段和大量稀疏分区,会让“Task 数乘分区数”迅速膨胀。

小文件放大链路

流式与频繁微批也会累积

即使每批文件不多,高频写入也会在同一分区持续追加。需要控制触发频率、目标文件大小,或使用支持自动优化与 Compaction 的表格式。

写前收敛,写后治理

写前可按目标分区 Repartition,并根据数据量估算分区数;写后定期合并冷分区。不能盲目 Coalesce(1),否则会牺牲并行度并制造单点。

为什么不能只回答概念?

“误区:写完后统一合并就够了”之所以常见,是因为它在理想数据和单次执行里往往说得通。但生产环境会加入数据规模、异常值、并发、版本、失败重试和下游口径,这些条件一变化,只记住术语就很容易给出错误结论。

所以我会把 一个 Task 通常对应一个输出文件、动态分区会再次放大、流式与频繁微批也会累积、写前收敛,写后治理 串成一条完整链路。最终判断标准不是某个函数或配置是否出现,而是能否稳定得到可解释、可验证、可恢复的业务结果。

落地时我会检查什么?

  • 输入边界:确认数据范围、时间口径、主键和异常值是否符合假设。
  • 执行过程:观察分区、并行度、状态、Shuffle 或资源变化,验证方案是否真正生效。
  • 输出结果:用行数、唯一性、关键指标和样本明细证明结果正确,而不是只看任务成功。
  • 失败恢复:说明重跑、回滚、版本兼容和责任人,避免一次成功后无人维护。

这些检查不一定全部写进一条 SQL 或一个任务,但面试时能主动补充,说明你回答的不是背诵题,而是在描述一套可上线的工程方案。

一个典型场景

一个任务按国家、日期、小时三层动态分区,国家字段又包含大量长尾值。即使 Shuffle 只有几百个分区,每个 Task 仍打开多个分区文件。团队最初只加合并任务,结果合并速度赶不上新增速度。最终减少过细分区,写前按核心分区重分布,并设置目标文件大小。

小文件先治理分区设计和写入方式,再谈 Compaction。

刷题进度保存在当前浏览器