Kafka 分层存储(Tiered Storage)原理、配置、快速上手与生产落地
一、为什么需要分层存储?
在大多数业务场景中,Kafka 的消费是尾部读取(tail reads):新写入的数据直接命中 OS 页缓存(page cache),极少访问磁盘;只有在回补(backfill)或故障恢复时,才偶尔回读很久以前的历史数据。
因此,把热数据留在 broker 本地磁盘,把冷数据(已完成的日志段)转移到廉价的远程存储(如 HDFS、S3)可以显著降低成本,同时保留回放能力,这就是 Tiered Storage 的核心价值。
二、工作机制一图观(简述)
-
Local(本地层):与传统 Kafka 相同,活跃段(active segment)仍在本地滚动写入。
-
Remote(远程层):当一个段滚动完成后,被上传到远程存储系统;随后,本地副本可根据
local.retention.*
策略删除。 -
读取路径:
- 尾部消费 → 命中页缓存或本地活跃段
- 回放早期数据 → 按需从远程拉取段并提供给客户端(对消费者透明)
规范与背景可参考 KIP-405(分层存储提案)。
三、关键组件与配置项
1)Broker 级总开关
remote.log.storage.system.enable
(默认 false):
设为true
才会在 broker 层启用分层存储功能。
2)远程段生命周期:RemoteStorageManager(RSM)
-
职责:管理远程日志段与索引的生命周期(上传、下载、删除等)。
-
注意:Kafka 不提供开箱即用实现,你必须配置:
remote.log.storage.manager.class.name
remote.log.storage.manager.class.path
教学/本地演示可使用官方集成测试实现
LocalTieredStorage
(下文快速上手示例包含)。
3)远程段元数据:RemoteLogMetadataManager(RLMM)
-
职责:以强一致语义维护远程段的元数据(位于何处、生命周期状态等)。
-
默认实现:使用一个 Kafka 内部主题来存储元数据;如需替换,可设置:
remote.log.metadata.manager.class.name
remote.log.metadata.manager.class.path
-
使用默认实现时必填:
remote.log.metadata.manager.listener.name
(指定 RLMM 内部客户端与 broker 通信使用的 listener 名称)
4)Topic 级别的开关与保留策略
-
remote.storage.enable
(默认 false):开启该主题的分层存储。 -
两组保留配置(启用分层存储后务必确认):
- 时间:
local.retention.ms
与retention.ms
- 大小:
local.retention.bytes
与retention.bytes
- 时间:
-
语义:
local.*
用于控制本地日志副本的保留阈值(在已成功上传远程后方可删除);如未设置local.*
,将沿用retention.*
的值。
四、快速上手:用 LocalTieredStorage 本地预演
目的:不接入 HDFS/S3,也能在本地验证分层存储的行为与指标。
1)构建测试库
# 在构建前切到你使用的版本 tag,例如:
git checkout 4.1.0./gradlew clean :storage:testJar
# 生成的测试 Jar 位于 storage/build/libs/kafka-storage-4.1.0-test.jar(文件名以实际版本为准)
2)Broker 示例配置(KRaft 模式,PLAINTEXT://:9092)
# 开启分层存储能力(broker 级)
remote.log.storage.system.enable=true# RLMM 默认实现的客户端要用哪个 listener 与 broker 通信
remote.log.metadata.manager.listener.name=PLAINTEXT# 指定 RSM 实现(此项为分层存储的刚需)
remote.log.storage.manager.class.name=org.apache.kafka.server.log.remote.storage.LocalTieredStorage
remote.log.storage.manager.class.path=/PATH/TO/kafka-storage-4.1.0-test.jar# 以下前缀为默认值,可自定义
remote.log.storage.manager.impl.prefix=rsm.config.
remote.log.metadata.manager.impl.prefix=rlmm.config.# LocalTieredStorage 的“远程目录”(确保 broker 有权限)
rsm.config.dir=/tmp/kafka-remote-storage# 多 broker 时需要合理设置副本因子
rlmm.config.remote.log.metadata.topic.replication.factor=1# 为观察效果,缩短保留检查周期
log.retention.check.interval.ms=1000
3)创建启用分层存储的主题
bin/kafka-topics.sh --create --topic tieredTopic --bootstrap-server localhost:9092 \--config remote.storage.enable=true \--config local.retention.ms=1000 \--config retention.ms=3600000 \--config segment.bytes=1048576 \--config file.delete.delay.ms=1000
含义速读:
remote.storage.enable=true
:该 topic 使用分层存储local.retention.ms=1000
:段上传远程后,本地最多保留 1s(仅测试用)retention.ms=3600000
:远程段保留 1hsegment.bytes=1MiB
:加速滚段,便于观察file.delete.delay.ms=1000
:加快本地删除延迟(测试用)
4)写入触发滚段
bin/kafka-producer-perf-test.sh \--topic tieredTopic --num-records 1200 --record-size 1024 \--throughput -1 --producer-props bootstrap.servers=localhost:9092
5)验证远程目录出现“冷段”
ls /tmp/kafka-remote-storage/kafka-tiered-storage/tieredTopic-0-<random-dir-id>
# 可见 .log / .index / .timeindex / .leader_epoch_checkpoint / .snapshot 等文件
6)从起始位置消费,确认 offset=0 可读(远程回放)
bin/kafka-console-consumer.sh \--topic tieredTopic --from-beginning --max-messages 1 \--bootstrap-server localhost:9092 --property print.offset=true
五、如何安全地“停用/冻结”分层存储(KRaft)
场景 A:让远程日志变为只读(停止继续拷贝)
-
需求:历史远程段保留,但不再从本地复制新段到远程。
-
设置:
remote.storage.enable=true
remote.log.copy.disable=true
- 并将
local.retention.ms / local.retention.bytes
设置为与retention.ms / retention.bytes
相同,或设为-2
(表示不再应用本地保留策略) - 原因:禁用远程复制后,本地保留策略将不生效,若不对齐可能导致本地磁盘写满。
bin/kafka-configs.sh --bootstrap-server localhost:9092 \--alter --entity-type topics --entity-name tieredTopic \--add-config 'remote.storage.enable=true,remote.log.copy.disable=true,local.retention.ms=-2,local.retention.bytes=-2'
场景 B:彻底禁用并删除所有远程日志
-
需求:不再使用分层存储,且清理该主题的远程段。
-
设置:
remote.storage.enable=false
remote.log.delete.on.disable=true
bin/kafka-configs.sh --bootstrap-server localhost:9092 \--alter --entity-type topics --entity-name tieredTopic \--add-config 'remote.storage.enable=false,remote.log.delete.on.disable=true'
重要提示:集群级禁用的前置条件
若要在broker 层关闭分层存储(remote.log.storage.system.enable=false
),必须先删除所有启用了分层存储的主题,否则 broker 启动会抛异常:
bin/kafka-topics.sh --delete --topic tieredTopic --bootstrap-server localhost:9092
# 删除完所有相关主题后,再修改 broker 配置关闭系统级开关
六、局限与已知限制
- 不支持 compacted 主题。
- 先按主题禁用:在 broker 层禁用分层存储之前,必须逐一在所有启用的 topic 上禁用。
- 分层存储相关的管理操作仅在 3.0+ 客户端上受支持。
- 不支持缺失 producer snapshot 文件的日志段(常见于v2.8.0 之前创建的主题)。
- 一次 Fetch 请求仅可服务一个分区(当来自远程存储时):这可能限制消费者吞吐,可适当调大
max.partition.fetch.bytes
。 - 更多细节可参考 Tiered Storage 的 GA Release Notes。
七、生产落地建议与最佳实践
-
选型与实现
- RSM 是关键:选择与你的对象存储/HDFS 生态匹配、吞吐稳定、具备重试与一致性保障的实现。
- 元数据(RLMM)默认用内部主题即可;跨集群/跨域场景再考虑替换。
-
保留策略设计
-
用
segment.bytes
控制滚段频率,平衡上传开销与延迟。 -
local.retention.*
与retention.*
分层明确:local.*
→ 控本地磁盘占用(仅限“已上传远程”的段)retention.*
→ 控远程历史保留(生命周期与合规要求)
-
从“极致低成本”与“回放时延”之间找平衡。
-
-
容量与成本
- 本地磁盘:按
local.*
预留足够空间,确保滚段与上传窗口不被突发流量击穿。 - 远程存储:关注小对象放大效应(对象存储最小计费单元)、清理滞后与生命周期策略(Lifecycle)。
- 本地磁盘:按
-
可观测性与告警
- 关键指标:上传/下载失败率、远程读延迟、段回放命中率、RLMM lag、对象存储 4xx/5xx。
- 告警阈值:远程拉取超时、
max.partition.fetch.bytes
命中频度、磁盘水位、本地段滞留时间异常等。
-
兼容性与灰度
- 客户端需 3.0+ 才支持全部管理操作。
- 老主题(<2.8.0)可能存在 producer snapshot 缺失问题,建议新建主题迁移数据或完成一次“补快照”的重建流程。
八、运维清单(拿去即用)
启用前检查
- 明确 RSM 实现与依赖(凭证、网络、带宽、限速策略)
- 评审
local.retention.*
与retention.*
策略 - 设定
segment.bytes
、file.delete.delay.ms
、log.retention.check.interval.ms
- 客户端版本 ≥ 3.0
上线步骤
- Broker 级开启:
remote.log.storage.system.enable=true
- 配置 RSM/RLMM 相关项(尤其
remote.log.metadata.manager.listener.name
) - 选定主题灰度开启:
remote.storage.enable=true
+ 合理的local.*
/retention.*
- 写入/滚段/验证远程目录与回放(从 offset=0 消费)
日常运维
- 监控上传/下载错误与延迟
- 观察远程读带宽与账单
- 定期核对远程 Lifecycle 与实际数据保留策略一致性
下线(禁用)
-
逐主题执行:
- 只读冻结:
remote.log.copy.disable=true
+ 对齐local.* = retention.*
或设-2
- 彻底禁用并删除远程:
remote.storage.enable=false, remote.log.delete.on.disable=true
- 只读冻结:
-
删除所有启用过分层存储的主题
-
Broker 级关闭:
remote.log.storage.system.enable=false
九、常见问题(FAQ)
Q1:分层存储与 compacted 主题能否共用?
A:不支持 compacted 主题。需要压缩日志的场景暂不适合启用 Tiered Storage。
Q2:回放吞吐上不去怎么办?
A:远程读取时一个 fetch 只能服务一个分区,可能成为瓶颈。可适当调大 max.partition.fetch.bytes
,并并发多分区消费。
Q3:为什么我禁用 broker 级开关后 Kafka 起不来?
A:必须先禁用/删除所有启用了分层存储的主题,再关闭 broker 级开关;否则启动会报错。
Q4:老主题(2.8.0 之前创建)的段为何无法纳入远程?
A:这些段可能缺少 producer snapshot 文件。建议新建主题迁移或重建以补齐快照。
结语
分层存储把 Kafka 的“热写热读”与“冷数据回放”彻底解耦:
热留本地、冷上云(或 HDFS),在成本、可靠性与回放能力之间找到最佳平衡。
按照本文的配置要点 → 快速上手 → 冻结/禁用策略 → 生产最佳实践一步步落地,你就能在保障稳定性的同时,有序地把 Kafka 存储成本打下来。祝上线顺利!