




















当前文章整理关于 Apache Hudi 时间旅行(Time Travel)、增量查询(Incremental Query)、压缩(Compaction)和清理(Cleaner)的关键内容,重点说明它们的语义区别、底层关系,以及对历史版本可用性的影响。
时间旅行和增量查询都依赖 Hudi 的 Timeline、MVCC 和 File Slice 机制,但查询目标不同:
| 功能 | 核心问题 | 返回结果 |
|---|---|---|
| 时间旅行 | 某个历史时刻,整张表长什么样? | 指定 instant 对应的完整快照 |
| 增量查询 | 某个时间范围内,哪些记录发生过变化? | 指定 begin/end 范围内的变更记录 |
简单理解如下:
时间旅行通过 as.of.instant 指定一个历史 instant,查询表在该 instant 的逻辑状态。
代码示例:
val df = spark.read.format("hudi")
.option("as.of.instant", targetInstant)
.load(basePath)
典型用途如下:
时间旅行返回的是目标 instant 下的完整表快照,而不是变化记录。
增量查询通过 begin/end instant 指定一个时间范围,只读取该范围内发生变化的数据。
示例代码:
val df = spark.read.format("hudi")
.option("hoodie.datasource.query.type", "incremental")
.option("hoodie.datasource.read.begin.instanttime", beginCompletionTime)
.option("hoodie.datasource.read.end.instanttime", endCompletionTime)
.load(basePath)
典型用途如下:
增量查询默认通常返回增量范围内每个 key 的最新状态,不一定等同于完整 CDC 明细;如果需要完整 insert/update/delete 事件,需要结合 Hudi CDC 能力和对应配置。
在 Hudi 1.0.x 的 timeline layout v2 中,需要区分两个时间:
| 时间 | 含义 | 常见位置 |
|---|---|---|
| requested time | instant 被请求创建的时间 | 记录字段 _hoodie_commit_time 通常是这个值 |
| completion time | commit 真正完成的时间 | timeline 中 HoodieInstant.getCompletionTime |
关键点:
_hoodie_commit_time 适合展示和排查。begin.instanttime / end.instanttime 应优先使用 completion time。_hoodie_commit_time 当成增量查询边界。推荐通过 HoodieTableMetaClient 获取 completed timeline:
case class CommitTime(requestedTime: String, completionTime: String)
val commits = metaClient.getCommitsTimeline
.filterCompletedInstants()
.getInstants
.asScala
.map(instant => CommitTime(instant.requestedTime(), instant.getCompletionTime))
.toSeq
Compaction 不是时间旅行,也不是增量查询。它是 MOR 表的 table service,用于把 log files 合并进新的 base parquet 文件。
关系可以理解为:
Timeline / MVCC / File Slices
├── Time Travel 按某个 instant 读取历史快照
├── Incremental Query 按 instant 范围读取变化
└── Compaction 合并 MOR 表的 base + log,生成新的 base file
Compaction 的主要作用:
COW 是 Copy-on-Write。更新时直接生成新的 parquet base file。
示例:
t1: base parquet v1: A=10
t2: update A=20
生成 base parquet v2: A=20
旧 base parquet v1 暂时保留
特点如下:
MOR 是 Merge-on-Read。更新通常先写入 log file,后续可通过 compaction 合并成新的 base file。
示例:
t1: base parquet: A=10
t2: log file: A=20
t3: compaction
生成新的 base parquet: A=20
旧 base/log 暂时保留
特点如下:
Compaction 通常不会影响时间旅行功能。
因为 Compaction 一般不是原地覆盖旧文件,而是生成新的 file slice。旧的 base/log 文件通常会暂时保留,用于支持 MVCC、长查询和历史版本读取。
例如:
t1: A=10
t2: A=20
t3: B=30
t4: compaction
Compaction 后可能形成:
旧 file slice:
base/log 可以重建 t1/t2/t3 的历史状态
新 file slice:
compacted base file,包含 t4 时刻的 A=20, B=30
所以此时:
as.of.instant = t1 -> 理论上仍可看到 A=10
as.of.instant = t4 -> 看到 A=20, B=30
所以真正可能让 as.of.instant = t1 不可用的是 cleaner 或 timeline/file retention,而不是 compaction 本身。
Compaction 通常不会改变增量查询的逻辑结果。
前面提到过 Compaction 会把文件变更合并到 base file,但是原来的文件并不会立即删除,所以增量查询仍然可以按照 timeline 的 instant 范围判断哪些记录发生变化。
但 compaction 可能影响读取的文件路径、查询性能以及 Read Optimized Query 的可见数据范围。
但是如果 cleaner 已经清理了相关的历史文件,那么增量查询就会出现问题。
Cleaner 参数负责删除超过保留策略的旧文件版本。
所以 Compaction 和 Cleaner 的区分如下:
如果 cleaner 还没有清理支持 t1 的旧文件,那么指定 as.of.instant = t1 就可以查询历史版本。
如果 cleaner 已经清理了支持 t1 的旧文件,那么查询 t1 的版本就会出现失败。
COW 和 MOR 都受 cleaner 管理,对比如下:
| 表类型 | 是否有 compaction | 是否有 cleaner | 历史文件清理 |
|---|---|---|---|
| COW | 否 | 是 | cleaner |
| MOR | 是 | 是 | cleaner |
假设有一张 MOR 表时间线如下:
t1 delta/base: A=10
t2 delta: A=20
t3 delta: B=30
t4 compaction: 生成新 base: A=20, B=30
t5 delta: B=40
时间旅行结果为:
as.of t1 -> A=10
as.of t2 -> A=20
as.of t3 -> A=20, B=30
as.of t5 -> A=20, B=40
增量查询结果:
begin=t1, end=t3 -> 返回 t2~t3 范围内变化的数据
begin=t3, end=t5 -> 返回 t5 范围内变化的数据,例如 B=40
Compaction t4 的作用是在压缩前 Snapshot 可能需要读取 base + 多个 log,但是压缩后 t4 之后的读取可以更多依赖新的 compacted base file。
但 t4 之后 t1 的历史语义不会立即消失,t1 是否还能查,取决于文件是否仍在 cleaner 策略保留范围内。
Compaction 会生成新的合并后 base file,但旧 base/log 通常由 cleaner 后续按策略清理。
COW 更新时也会生成新的 base file version,旧版本也可能暂时保留,因此也可以支持时间旅行。区别是 COW 没有 compaction。
在 Hudi 1.0.x 中不推荐使用 _hoodie_commit_time。因为 _hoodie_commit_time 通常是 requested time,而增量查询 begin/end 边界应优先使用 timeline 中的 completion time。
时间旅行能力受 cleaner、archival 和文件保留策略影响。旧文件被清理后,对应历史快照可能无法重建。
如果依赖时间旅行比较强,需要配置足够的历史保留:
hoodie.cleaner.commits.retained
hoodie.keep.min.commits
hoodie.keep.max.commits
建议配置如下:
如果对增量查询依赖比较多,需要管理好时间线参数,重点如下:
需要同时关注 compaction 和 cleaner,compaction 主要影响查询性能和 read optimized 可见性,cleaner 影响历史版本和增量查询可追溯性。
建议配置通过 compaction 控制 log 文件数量,通过 cleaner retention 控制历史可查时间。
注意不要把 compaction 误认为历史清理机制。
综上,对时间旅行和增量查询的主要区别总结如下。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。