面试知识库
进阶

Kafka日志清理与Compaction#

一句话答案#

Kafka 有两种清理策略:delete 按时间/大小删除整段旧日志(默认,适合普通消息流);compact 按 key 只保留每个 key 的最新值(适合”快照”语义,如 __consumer_offsets、KTable 状态)。由 log.cleanup.policy 控制,可叠加。

核心要点

两种清理策略#

策略行为删什么典型场景
delete(默认)删除过期/超量的整个 segment整段消息普通消息流、日志、事件
compact每个 key 只保留最新一条 value同 key 的旧值维护”最新状态”快照
delete,compact两者叠加先压缩再按时间删既要状态又要兜底过期
log.cleanup.policy=delete          # 或 compact / "delete,compact"
properties

delete 策略:按什么删#

# 按时间:消息保留多久(三选一,优先级 ms > minutes > hours)
log.retention.hours=168            # 默认 7 天

# 按大小:单个分区日志超过此值就删旧段(-1 表示不限)
log.retention.bytes=-1

# 检查周期
log.retention.check.interval.ms=300000   # 默认 5 分钟扫一次
properties

要点:

  • 以 segment 为最小删除单位——即使段内只有一条没过期,整段也不会被删;所以”保留 7 天”实际可能多留一个段的量。
  • active segment(正在写的段)永不被删。
  • 时间判断依据段内最大时间戳,而非创建时间。

compact 策略:Log Compaction 原理#

压缩前(同一 key 的多次更新都在):
  offset:  0    1    2    3    4    5
  key:     K1   K2   K1   K1   K3   K2
  value:   v1   v2   v3   v4   v5   v6

压缩后(每个 key 只留最后一次):
  offset:  3    4    5
  key:     K1   K3   K2
  value:   v4   v5   v6          ← K1 只剩 offset=3 的 v4,旧的 v1/v3 被清掉
plaintext
  • 目标:日志体积只与 key 的数量有关,而非更新次数 → 适合存”最新状态”。
  • offset 不连续:旧 offset 被清掉后会出现空洞,消费端不能假设 offset 连续。
  • 墓碑消息(tombstone):value=null 表示删除该 key,保留 delete.retention.ms(默认 24h)后彻底清除,让下游消费者有时间感知删除。
  • 由后台 Log Cleaner 线程异步执行,只压缩 inactive segment。

经典应用:__consumer_offsets#

  • Kafka 把消费位移存在内部 topic __consumer_offsets,采用 compact 策略:每个 <group, topic, partition> 只需保留最新提交的 offset,历史提交无意义 → 天然适合 compaction。
  • KTable / Kafka Streams 的状态存储、CDC 的”最新行快照”也都依赖 Log Compaction。

和存储结构的关系#

无论哪种策略都以 segment 为操作单位(delete 删整段、compact 重写段),所以理解清理必须先理解 Kafka存储与日志结构

面试回答(2分钟版)

Kafka 有两种日志清理策略,由 log.cleanup.policy 控制。默认是 delete,按时间或大小删除旧数据:时间上由 log.retention.hours 默认 7 天控制,大小上由 log.retention.bytes 控制,后台每隔几分钟扫描一次。关键点是删除以 segment 段为最小单位,整段消息都过期才会删,而且正在写的 active segment 永远不删,所以实际保留量会比配置略多一点。第二种是 compact 日志压缩,它不按时间删,而是对相同 key 只保留最新的那条 value,旧值被清理掉,这样日志大小就只和 key 的数量有关而不是更新次数,特别适合维护”最新状态”的快照语义。压缩后 offset 会变得不连续出现空洞,消费端不能假设 offset 连续;如果要删除某个 key,就发一条 value 为 null 的墓碑消息,保留一段时间让下游感知后再彻底清除。最典型的应用就是 Kafka 存储消费位移的内部 topic __consumer_offsets,每个消费组对每个分区只需要最新提交的 offset,所以用 compact 再合适不过,Kafka Streams 的状态存储也是同理。两种策略还能叠加成 delete,compact,先压缩再按时间兜底过期。

追问与易错

追问方向:

  • “compact 之后 offset 还连续吗?”→ 不连续,旧值被清理后留下空洞,消费端必须能容忍 offset 跳跃,不能用”下一条 offset = 当前+1”的假设
  • “怎么用 compact 删除一个 key?”→ 发送 value 为 null 的墓碑消息,Log Cleaner 会清掉该 key 的所有旧值,墓碑本身保留 delete.retention.ms 默认 24 小时后删除
  • __consumer_offsets 为什么用 compact 不用 delete?”→ 位移只关心每个消费组每个分区的最新值,历史提交无意义;若用 delete 按时间删可能把还需要的最新位移删掉导致消费组丢失进度
  • “保留 7 天是不是精确 7 天后就删?”→ 不是,删除以段为单位且只删整段过期的,加上检查间隔,实际会略晚于 7 天

易错点:

  • ❌ “retention 到期立刻按条删除”——按 segment 整段删,且 active 段不删
  • ❌ “compact 会保证 offset 连续”——压缩后 offset 有空洞
  • ❌ “compact 和 delete 只能二选一”——可配置 delete,compact 同时生效
  • ❌ “Log Compaction 是压缩算法(如 gzip)“——是按 key 去重保留最新值,和 Snappy/LZ4 那种数据压缩完全是两回事