基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

full-compaction 模式的 Changelog Producer 的工作机制

知识点图片

在 Apache Paimon 的官方文档中,主键表(Primary Key Table)流式读取的一个核心概念就是 Changelog Producer(变更日志生成器)。流式写入时,为了让下游流式读取(如 Flink 流读)能够获取到完整、正确的增量修改数据(特别是带有 -U / +U 即 UPDATE_BEFORE 和 UPDATE_AFTER 的回撤流),必须通过 Changelog Producer 机制来生成 Changelog。

以下基于 Apache Paimon 的 master 分支文档及底层设计,详细剖析 full-compaction(全量压缩) 模式的工作机制、配置、底层原理及应用场景:


一、 full-compaction 机制的核心概述

对于主键表,如果用户在创建表时指定了聚合引擎(如 partial-update 局部更新、aggregation 预聚合等),写入器(Writer)在刷盘时仅凭输入的单条记录是无法直接推导出“旧值”的。
full-compaction 模式的提出,就是为了在 不依赖复杂外部状态/点查索引 的情况下,生成完整正确的变更日志。

  • 基本原理:Paimon 会在全量压缩(Full Compaction)的过程中,将要合并的数据与最高层(Top Level)的历史数据进行比较,通过两者的“差分”(Difference)来推导并产生 Changelog。
  • 特点
    • 通用性强:能够为任何类型的输入数据源和任何合并引擎(Merge Engine)产生 100% 正确且完整的变更日志。
    • 高延迟解耦:将 Changelog 的生成开销从“每次写入(Write)”剥离,延迟到“压缩(Compaction)”阶段。
    • 适合高延迟场景:因为全量压缩比较消耗资源,一般建议设置较长的触发周期(如 10 分钟或 30 分钟),从而更适合对实时性要求不极端的场景。

二、 工作生命周期与技术机制

在 LSM 树存储结构中,数据按照 Level 分层组织。新数据首先写入内存(MemTable),然后刷盘到 Level 0,最后通过 Compaction 逐层下沉,最终合并到最高层。full-compaction 模式下的 Changelog 生成流程如下:

1. 触发全量压缩

当触发全量压缩时,LSM 树中所有层级(从 Level 0 到最高的 Max Level)的所有数据文件都会参与合并。由于这是一次彻底的全局合并,所有属于同一个主键的旧版本、新写入和待删除(Delete)的数据都会被聚合在一起。

2. 底层合并与变更生成(差分逻辑)

在合并过程中,底层对应的核心组件是 FullChangelogMergeTreeCompactRewriterFullChangelogMergeFunctionWrapper

  • 新增记录(INSERT):如果某个主键在最高层(Max Level)中不存在历史记录(说明这是一条全新的主键),合并后生成一条 +I(INSERT)的 Changelog。
  • 更新记录(UPDATE):如果该主键在最高层中已存在历史记录(旧值 v_old),在经历了本次 Compaction 归并后得到了最新值(新值 v_new):
    • 如果 v_oldv_new 不相等,系统会自动生成一对变更日志:-U(UPDATE_BEFORE,携带 v_old)以及 +U(UPDATE_AFTER,携带 v_new)。
    • 通过这种“最高层旧值 vs 合并后新值”的比较,完全避免了在平时写入阶段去进行点查的开销。
  • 删除记录(DELETE):当检测到墓碑标记(Delete Tombstone)与历史记录合并后,生成 -D(DELETE)的 Changelog。

3. 写入专用的 Changelog 文件

计算出的这些变更记录(+I-U+U-D)不会和普通的数据合并到最终的数据文件中,而是被独立输出并保存到专门的 Changelog File 中。
下游的流式读取器(Paimon Source)通过扫描快照元数据(Snapshot),可以直接并按序读取这些 Changelog 文件,向游提供保序的 Changelog 流。


三、 关键参数配置

在建表属性中,以下几个参数专门用于微调 full-compaction 模式下的行为:

参数名称 默认值 类型 描述
changelog-producer none Enum 设为 full-compaction 以启用此机制。
full-compaction.delta-commits 无(或 1 Integer 控制触发全量压缩的频率。在流式写入中,每经过指定次数的 delta commits(即 Flink Checkpoint 提交次数),就会触发一次 Full Compaction。
• 设为 1:表示每次 Checkpoint 都会执行一次全量压缩并产出 Changelog。虽然延迟低(等于 Checkpoint 间隔),但写放大极其严重。
• 设为较大值(如 10):表示每 10 次 Checkpoint 才全量压缩一次,虽然 Changelog 产出延迟增大,却能大幅减轻吞吐压力。
changelog-producer.row-deduplicate false Boolean 控制是否对同一记录进行去重。如果全量压缩后的新值与最高层的旧值实际上完全一样(即数值未发生改变),若设为 true 则不产生 redundant 的 -U/+U 日志。
changelog-producer.row-deduplicate-ignore-fields String row-deduplicate 搭配使用,指定在比较新旧值时,哪些字段的变化可以被忽略(不触发变更日志生成)。

四、 与其他模式的对比

为了帮助更好地理解 full-compaction 的设计意图,可以将其与常用的 lookup 模式进行简单对比:

  • lookup 模式
    • 在每次数据刷盘(Flush)提交到快照前,点查(Lookup)本地内存或本地磁盘索引来获取旧值。
    • 优点:生成 Changelog 的延迟低,紧跟 Checkpoint 周期。
    • 缺点:在内存和本地磁盘上会产生巨大的索引缓存压力(需配置额外缓存,且容易受磁盘 IO 瓶颈限制)。
  • full-compaction 模式
    • 不进行任何额外的主键点查。
    • 优点:完全避免了由于点查(Lookup)导致的复杂索引维护及本地磁盘 IO 消耗;由于 Compaction 过程本身就需要读取这些数据,因此其 Changelog 生成属于“顺带进行”,原理更为自然直接。
    • 缺点:Changelog 产生完全受限于全量压缩周期。如果周期短(如 delta-commits = 1),全量合并所有层的数据开销很大,会对系统写入吞吐带来一定限制。

五、 最佳实践建议

  1. 容忍延迟,换取吞吐:在资源有限或表极宽的情况下,建议将 full-compaction 的触发延迟放宽(例如通过控制 Checkpoint 间隔配合合理的 full-compaction.delta-commits 周期,或者通过独立进程定期触发 Compact),让其在业务低峰期或以更低频的形式工作。
  2. 在全量阶段暂时关闭:如果是进行历史数据的全量同步阶段(如首次 CDC 初始化导入),建议暂时不配置此参数,待全量历史写入完成后,再通过 ALTER TABLE 开启,进入增量流读阶段,以避免全量阶段产生严重的性能阻碍。
00:00
00:00