相信不少朋友和我一样——对 Kafka 的逻辑模型(如 Broker、Topic、Partition)已经比较熟悉,但一谈到数据在磁盘上到底怎么存的,心里就有点“雾里看花”。这种“知道大概、说不清楚”的状态,在技术讨论中常常让人略显尴尬。
今天,我们就一起动手,揭开 Kafka 底层存储的神秘面纱,真正搞清楚:消息写进去后,到底变成了哪些文件?它们又各自承担什么作用?
一、动手实践:搭建环境并写入测试数据
要理解存储机制,光看理论不够,必须“眼见为实”。我们先快速搭建一个单机 Kafka 环境,并写入一批真实数据。
1. 下载与初始化
前往 Apache Kafka 官网 下载最新版(以 4.1.1 为例):
tar -xzf kafka_2.13-4.1.1.tgz
cd kafka_2.13-4.1.1
执行初始化命令生成集群 ID 并格式化存储目录(KRaft 模式):
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
bin/kafka-storage.sh format --standalone -t $KAFKA_CLUSTER_ID -c config/server.properties
小技巧:为了更直观地观察 Segment 切分,建议将
log.segment.bytes调整为 1MB(默认是 1GB),修改config/server.properties即可。
启动 Kafka 服务:
bin/kafka-server-start.sh config/server.properties
确认日志中无报错,服务正常启动。
2. 创建 Topic 并灌入数据
创建一个名为 test-topic 的 Topic,包含 2 个 Partition:
bin/kafka-topics.sh --create \
--bootstrap-server localhost:9092 \
--topic test-topic \
--partitions 2 \
--replication-factor 1
使用 Kafka 自带的压测工具写入 10 万条记录(每条 100 字节,总计约 10MB):
bin/kafka-producer-perf-test.sh \
--topic test-topic \
--num-records 50000 \
--record-size 100 \
--throughput -1 \
--producer-props bootstrap.servers=localhost:9092
# 执行两次,共 10 万条
二、定位数据目录
Kafka 默认将数据存储在 /tmp/kraft-combined-logs(KRaft 模式下)。进入该目录,你会看到类似这样的结构:
__cluster_metadata-0/
bootstrap.checkpoint
cleaner-offset-checkpoint
log-start-offset-checkpoint
meta.properties
recovery-point-offset-checkpoint
replication-offset-checkpoint
test-topic-0/
test-topic-1/
这些内容大致可分为三类:
1. 集群元数据
meta.properties:记录当前 Broker 的 Cluster ID 和 Node ID,相当于它的“身份证”。bootstrap.checkpoint:保存集群首次启动的关键信息。__cluster_metadata-0/:特殊 Partition,用于存储集群级别的元数据(如 Topic 配置、ACL 等)。
2. 数据目录
test-topic-0/和test-topic-1/:分别对应test-topic的两个 Partition。
Kafka 的命名规则非常直观:<topic-name>-<partition-id>。
3. Checkpoint 文件(用于故障恢复)
log-start-offset-checkpoint:记录每个 Partition 当前有效的起始 Offset。recovery-point-offset-checkpoint:已持久化到磁盘的最大 Offset。replication-offset-checkpoint:记录各 Partition 的 High Watermark(HW),即已成功复制的偏移量。cleaner-offset-checkpoint:仅在启用cleanup.policy=compact时生效,记录 Log Compaction 的进度。
三、深入 Partition 目录:核心文件解析
进入任意一个 Partition 目录(如 test-topic-0),你会发现一组以数字命名的文件,例如:
00000000000000009472.log
00000000000000009472.index
00000000000000009472.timeindex
...
这些文件构成了 Kafka 存储的“黄金三角”。
🔑 核心三件套
1. .log —— 消息数据本体
这是真正的消息存储文件,所有 Producer 发送的消息都顺序追加写入其中。
可通过 Kafka 工具查看内容:
bin/kafka-dump-log.sh --files test-topic-0/00000000000000009472.log --print-data-log
输出会显示每条消息的 Offset、时间戳及序列化后的 payload。
2. .index —— 偏移量稀疏索引
这是一个稀疏索引文件,记录了 Offset 到 .log 文件物理位置(字节偏移)的映射。
默认每写入 4KB 数据 就记录一条索引项(可通过 log.index.interval.bytes 调整)。
查看方式:
bin/kafka-dump-log.sh --files test-topic-0/00000000000000009472.index --deep-iteration
当 Consumer 请求某个 Offset 时,Kafka 先通过二分查找在 .index 中定位最接近的位置,再从 .log 中读取具体数据。
3. .timeindex —— 时间戳索引
支持按时间范围查询消息(如“过去 5 分钟的数据”)。它建立了 时间戳 → Offset 的映射。
使用方式与 .index 类似:
bin/kafka-dump-log.sh --files test-topic-0/00000000000000009472.timeindex --deep-iteration
典型流程:先查 .timeindex 得到近似 Offset,再用 .index 定位到 .log 中的精确位置。
🛠️ 辅助文件(你可能没注意但很重要)
.snapshot:事务快照文件,用于实现 Exactly-Once 语义。Broker 重启后可据此恢复 Producer 状态,避免重复发送。leader-epoch-checkpoint:记录每次 Leader 变更时的起始 Offset,保障副本一致性,防止“脑裂”导致的数据不一致。partition.metadata:存储该 Partition 所属 Topic 的唯一 ID(Topic ID),用于内部标识。
四、性能之谜:为什么 Kafka 写磁盘还这么快?
很多人疑惑:Kafka 明明是“写磁盘”,为何吞吐量却能高达百万级?
答案在于:它几乎完全依赖操作系统的 Page Cache。
- Producer 写入时,数据先写入 Page Cache(内存),由 OS 异步刷盘;
- Consumer 读取时,若数据仍在 Page Cache 中(通常是热数据),直接从内存返回,零磁盘 I/O;
- 这种设计避免了 JVM 堆内缓存带来的 GC 压力,同时利用了 OS 对内存管理的高度优化。
为什么不自己实现缓存?
- 避免 JVM GC 开销;
- 进程重启后 Page Cache 仍有效(只要系统未重启);
- 架构更简洁,把复杂性交给操作系统。
总结
通过本次实践,我们清晰地看到:
- Kafka 的物理存储以 Partition 为单位,每个 Partition 是一个独立目录;
- 每个 Segment 包含三大核心文件:
.log(数据)、.index(Offset 索引)、.timeindex(时间索引); - 多种 Checkpoint 和元数据文件协同工作,保障高可用与一致性;
- 高性能的秘密藏在 Page Cache + 顺序写 的组合拳中。
现在,当你再聊 Kafka 存储时,不仅能说出“Partition 分片”,还能精准指出 .timeindex 的作用、.snapshot 的意义,甚至解释为何“写磁盘比写内存还快”——这才是真正的知其然,更知其所以然。
希望这篇文章帮你打通了 Kafka 存储的最后一环!
共同学习,写下你的评论
评论加载中...
作者其他优质文章