Paimon 提供了哪些内置机制(如自动合并、专门的 Compact 任务等)来持续治理和预防小文件?
根据 Apache Paimon(master 分支)的官方文档,Paimon 针对流式和批式数据写入时极易产生的小文件问题,设计了一套体系化的预防(预防生成)与治理(后期合并)机制。
这些机制涵盖了从写入前的拓扑优化、写入过程中的自动合并、到独立的离线合并任务,形成了闭环的治理方案。以下是具体的内置机制:
1. 写入过程中的自动合并机制 (Auto Compaction)
在数据持续写入(Streaming Write)的过程中,Paimon 的 Writer 算子会根据不同的表类型在后台自动触发局部合并,以控制文件的数量与层级:
- 主键表 (Primary Key Table) 的 LSM 树合并:
主键表在每个 Bucket 内构建 LSM 树。当 Flink Checkpoint 触发数据刷写(Flush)产生 L0 文件后,Paimon 会在后台采用类似 RocksDB 的 Universal Compaction 策略,自动将多个有序数据集(Sorted Run)合并为更大的文件。- 可以通过控制参数(如
num-sorted-run.compaction-trigger,默认 5)来调优触发合并的文件数量门槛。 - 异步合并 (Asynchronous Compaction):为了不阻塞主流写入并最大化写入吞吐,合并默认采用异步模式。如果需要,还可以通过调整
num-sorted-run.stop-trigger等配置使其更加温和,减少高并发写期间的 CPU 和 IO 争抢。
- 可以通过控制参数(如
- 仅追加表 (Append-Only Table) 的自动合并:
在 Flink 引擎的流模式下,追加表同样默认支持自动合并小文件。- 无 Bucket 的 Append 表 (Unaware-Bucket):提供了诸如
compaction.min.file-num(触发合并的最少文件数,默认 5)和compaction.small-file-ratio(当文件大小小于target-file-size * small-file-ratio时会被判定为小文件并被选中进行重写合并,默认 0.5)等精细化配置。 - 有 Bucket 的 Append 表 (Bucketed Append):自动合并仅在 Bucket 内部进行,以保证数据的顺序性。
- 无 Bucket 的 Append 表 (Unaware-Bucket):提供了诸如
2. 全量合并机制 (Full Compaction)
局部合并有时无法彻底清除所有小文件(例如历史分区、或者因业务逻辑导致的残留)。为此,Paimon 引入了全量合并:
full-compaction.delta-commits参数:
此选项多用于 Flink 流式写入。通过配置该参数(例如设为数个 Checkpoint 周期),Paimon 会在经历指定次数的提交后,在后台不断触发全量合并,将所有小文件彻底融合成大文件。- 由于全量合并对系统资源的消耗较大,官方不建议设置过小,通常建议配置为使全量合并每小时执行 1~2 次为佳。
- Copy On Write 模式 (COW):
通过将full-compaction.delta-commits设置为1,可以使每次写入提交都伴随着全量合并。这能保证读端永远没有多余的小文件,但写放大极其严重。
3. 专用的独立 Compact 任务 (Dedicated Compaction Job)
在某些生产场景下,流式 Writer 节点同时负责写入和合并,可能会在吞吐高峰期因 Compaction 占用过多 CPU/IO 导致反压,或在多并发写入同一分区时引发 commit 冲突导致作业重启。对此,Paimon 提供了“读写分离”的独立治理机制:
write-only机制:
将表属性write-only设置为true,Writer 节点将仅负责接收数据并快速刷写到磁盘,完全跳过 Compaction 和 Snapshot 过期逻辑,确保极高的写入吞吐和零冲突。- 提异步专有 Compaction 任务:
在write-only模式下,用户可以使用独立的流或批任务对表进行持续的、高效率的小文件合并。Paimon 提供了多引擎的调用方式:- Flink SQL / Procedure:支持调用系统过程,如
CALL sys.compact(table => 'default.T', partition_idle_time => '...')。该指令支持根据分区的闲置时间等条件对历史分区的小文件进行集中合并。 - Flink Action Jar:直接通过 Flink 命令行提交任务:
flink run .../paimon-flink-action.jar compact --path ...。 - Spark SQL / Procedure:使用 Spark 引擎调用
CALL sys.compact(...)进行表的小文件治理。
- Flink SQL / Procedure:支持调用系统过程,如
4. 增量聚类机制 (Incremental Clustering)
针对无 Bucket 的 Append 表,传统的 Sort Compaction 排序重写开销极大。Paimon 提供了更灵活的增量聚类机制(Incremental Clustering):
- 当开启
clustering.incremental = true时,系统在运行聚类任务时不会重写整个数据集,而是智能挑选出特定的小文件子集进行基于 SFC 曲线(如 Z-order 或 Hilbert)的重排序。 - 在重写和重新组织这些数据的同时,Paimon 会严格遵循
target-file-size的大小,完成对这些杂乱小文件的合并与重塑,从而在最小化写放大的同时解决小文件问题。
5. 源头预防与优化机制
除了生成文件后的治理,Paimon 还在写入拓扑和存储结构上提供了源头预防的手段:
- 预提交合并 (
precommit-compact):
如果在配置中开启precommit-compact = true,Paimon 会在 Flink 的 Writer 算子之后、物理 Commit 之前,在拓扑中自动加入一个 Compact Coordinator 和 Worker 算子。该算子会将来自同一个分区、刚刚生成的多个微小 Changelog 文件(主键表)或新产生的数据文件(无桶追加表)在内存/局部先合并成大文件,然后再进行物理 Commit。这能在文件真正落盘和生成元数据快照之前,从源头上截断小文件的产生。 - 动态桶管理 (Dynamic Bucket):
对于主键表,如果人工配置的 Bucket 数量过多,每个 Bucket 至少会产生一个小文件。Paimon 支持设置bucket = -1(动态桶模式),系统会维护一个索引,根据分区内的数据行数动态扩容桶数量(受dynamic-bucket.target-row-num控制),从而确保每个 Bucket 的数据量维持在健康的区间(官方推荐为 200MB - 1GB),有效避免因过度分区/分桶产生的大量碎屑文件。 - Writer 内存缓冲 (Write Buffer):
除了 Checkpoint 以外,Writer 自身的内存满溢也会强制将数据刷盘产生小文件。通过调优write-buffer-size并启用write-buffer-spillable(写入缓冲区可溢写),可以尽可能延长微小批次在内存中的驻留与合并时间,降低物理磁盘 IO 和文件的零碎度。 - 调大 Checkpoint 间隔:
Flink 每次 Checkpoint 都会强制将 Paimon Writer 中的缓存数据 Flush 到 DFS 并生成元数据快照。如果 Checkpoint 过于频繁(如 10s 一次),必然会产生海量微小文件。Paimon 官方建议根据业务容忍度适当增加 Checkpoint 间隔,从根本上降低小文件生成的频率。