Tavio's blog Tavio's blog
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Tavio Zhang

努力学习的小码喽
首页
  • JVM底层原理
  • 邪恶多线程
  • MyBatis底层原理
  • Spring底层原理
  • MySQL的优化之路
  • ClickHouse的高性能
  • Redis的快速查询
  • RabbitMQ的生产
  • Kafka的高吞吐量
  • ES的入门到入坑
  • MySQL自增ID主键空洞
  • 前端实现长整型排序
  • MySQL无感换表
  • Redis延时双删
  • 高并发秒杀优惠卷
  • AOP无侵入式告警
  • 长短链接跳转
  • 订单超时取消
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Kafka的深度解析
  • Kafka消息存储
    • 一、核心设计原则:支撑高吞吐的底层逻辑
      • 1. 分区隔离存储
      • 2. 日志分段存储
      • 3. 索引辅助查询
    • 二、分区存储结构:三层设计的精细化管理
      • 1. 目录层:分区的物理隔离单元
      • 2. 日志分段层:消息存储的基本单元
      • 分段核心特性:
      • 3. 消息格式:二进制的高效编码
    • 三、消息全生命周期:从生产到清理的完整流程
      • 1. 消息写入:高效持久化的底层逻辑
      • (1)内存缓冲:利用页缓存提升写入速度
      • (2)刷盘策略:平衡性能与可靠性
      • (3)顺序写入:磁盘性能最大化
      • 2. 副本同步:可靠性的核心保障
      • (1)同步流程
      • (2)ISR机制:定义"有效副本"
      • (3)acks参数:控制消息可靠性等级
      • 3. 消息消费:基于索引的快速定位
      • (1)按Offset查询(依赖.index文件)
      • (2)按时间戳查询(依赖.timeindex文件)
      • 4. 日志清理:可控的存储管理
      • (1)基于时间的清理(默认策略)
      • (2)基于大小的清理
      • (3)日志压缩(针对Key-Value消息)
      • (4)清理触发机制
    • 四、总结:设计为何支撑高吞吐与高可靠?
  • Kafka的生产消费
  • 《Kafka》笔记
Tavio
2025-02-04
目录

Kafka消息存储

Kafka作为分布式流处理平台,其高吞吐、高可靠的核心特性离不开精心设计的消息存储机制。

# 一、核心设计原则:支撑高吞吐的底层逻辑

Kafka的消息存储机制围绕"高效写入、快速查询、可控清理"三大目标设计,核心原则如下:

# 1. 分区隔离存储

每个Topic的分区(Partition)对应独立的存储目录,物理层面完全隔离。这种设计带来两大优势:

  • 避免不同分区的消息读写相互干扰,降低锁竞争;
  • 支持分区级别的并行操作(如并行写入、并行清理),提升整体吞吐量。

# 2. 日志分段存储

单个分区的消息不会存储在一个大文件中,而是拆分为多个日志分段(Log Segment)。这种分段化设计解决了三大问题:

  • 避免单文件过大导致的IO性能下降(大文件随机读写效率低);
  • 便于按分段进行过期清理,无需操作整个分区数据;
  • 简化日志压缩、数据迁移等运维操作。

# 3. 索引辅助查询

每个日志分段配套两类索引文件:偏移量索引(.index)和时间戳索引(.timeindex)。通过索引可以快速定位消息位置,避免全文件扫描,大幅提升查询效率。

# 二、分区存储结构:三层设计的精细化管理

Kafka的分区存储采用"目录-分段-索引"三层结构,每层各司其职,共同实现消息的有序存储与高效访问。

# 1. 目录层:分区的物理隔离单元

Broker启动时,会根据log.dirs配置的根目录,为每个Topic的分区创建独立目录,命名格式为{Topic名称}-{分区序号}。

示例目录结构: 若根目录为/kafka-logs,Topic为order且包含3个分区,则目录结构如下:

/kafka-logs/                # 根目录(log.dirs配置)
├─ order-0/                 # 分区0的存储目录
├─ order-1/                 # 分区1的存储目录
└─ order-2/                 # 分区2的存储目录
1
2
3
4

每个分区目录下,存放该分区的所有日志分段文件及对应的索引文件。

# 2. 日志分段层:消息存储的基本单元

每个分区由多个日志分段组成,单个分段包含三类文件:

文件类型 文件名格式 核心作用
消息日志文件 {起始Offset}.log 存储实际消息数据(二进制格式)
偏移量索引文件 {起始Offset}.index 映射Offset与.log文件内的物理偏移
时间戳索引文件 {起始Offset}.timeindex 映射时间戳与Offset的对应关系

# 分段核心特性:

  • 命名规则:文件名以分段存储的第一条消息的Offset命名(19位数字,不足补0)。例如:
    • 初始分段命名为0000000000000000000.log(存储Offset从0开始的消息);
    • 当该分段写满后,下一个分段以"上一分段最后一条消息Offset+1"命名(如0000000000000012345.log)。
  • 滚动机制:单个分段默认大小为1GB(可通过log.segment.bytes配置),当消息写入达到阈值时,自动创建新分段(当前分段变为只读)。
  • 活跃分段:分区中正在写入消息的分段称为"活跃分段"(Active Segment),其余为只读分段(避免随机写,保证顺序IO)。

# 3. 消息格式:二进制的高效编码

.log文件中的消息以固定格式的字节流存储,采用"只追加"(Append-Only)方式写入,保证顺序IO(磁盘顺序写性能接近内存)。

V2版本消息格式核心字段(Kafka 0.11+):

  • 长度字段:记录消息总字节数;
  • 属性字段:包含压缩类型、时间戳类型等元数据;
  • 时间戳:消息创建时间(可由Producer指定或Broker生成);
  • 键(Key)与值(Value):实际业务数据(支持压缩,如GZIP、Snappy);
  • 偏移量(Offset):分区内的唯一序号(由Broker分配);
  • 校验和:用于检测消息完整性。

# 三、消息全生命周期:从生产到清理的完整流程

Kafka的消息管理贯穿"生产-同步-消费-清理"四个阶段,每个阶段均有特定机制保障可靠性与效率。

# 1. 消息写入:高效持久化的底层逻辑

Producer发送的消息经分区路由(按Key哈希或轮询)后,投递到目标分区的活跃分段,写入过程分为三步:

# (1)内存缓冲:利用页缓存提升写入速度

消息并非直接写入磁盘,而是先写入操作系统的页缓存(Page Cache):

  • 页缓存是操作系统为磁盘文件分配的内存缓冲区,由OS统一管理;
  • 避免频繁磁盘IO,通过批量刷盘(异步)降低性能损耗。

# (2)刷盘策略:平衡性能与可靠性

页缓存中的数据需定期刷入磁盘(持久化),Kafka提供两种配置控制刷盘时机:

  • log.flush.interval.messages:累计写入消息数达到阈值时刷盘;
  • log.flush.interval.ms:距离上次刷盘时间达到阈值时刷盘。 (默认依赖OS自身的刷盘策略,通常为几秒到几分钟)

# (3)顺序写入:磁盘性能最大化

由于消息仅追加到活跃分段的末尾(顺序写),而磁盘的顺序写性能远高于随机写(机械盘顺序写速度可达200MB/s,随机写仅100KB/s),这是Kafka高吞吐的核心原因之一。

# 2. 副本同步:可靠性的核心保障

Kafka通过多副本(Replica)机制防止消息丢失,每个分区包含:

  • 1个Leader副本:处理读写请求,是消息的"主副本";
  • 多个Follower副本:从Leader同步消息,作为"备用副本"。

# (1)同步流程

Follower通过"拉取(Pull)"机制同步消息:

  1. Follower定期向Leader发送同步请求(包含已同步的最大Offset);
  2. Leader返回该Offset之后的消息;
  3. Follower写入消息并更新本地Offset,完成同步。

# (2)ISR机制:定义"有效副本"

只有处于ISR(In-Sync Replicas,同步副本集) 中的Follower才被视为有效副本:

  • ISR包含Leader及所有与Leader数据差距在replica.lag.time.max.ms(默认30s)内的Follower;
  • 若Follower长时间未同步(超过阈值),会被踢出ISR;
  • 当Leader故障时,仅从ISR中选举新Leader,保证数据一致性。

# (3)acks参数:控制消息可靠性等级

Producer通过acks参数配置消息确认机制,平衡可靠性与吞吐量:

  • acks=0:Producer发送后不等待确认,吞吐量最高但可能丢失消息;
  • acks=1:仅等待Leader写入页缓存后确认(无需持久化到磁盘),性能与可靠性平衡;
  • acks=all(或acks=-1):需等待ISR中所有副本写入页缓存后确认,可靠性最高但吞吐量最低。

# 3. 消息消费:基于索引的快速定位

Consumer通过Offset拉取消息时,Kafka利用索引文件快速定位消息位置,流程如下:

# (1)按Offset查询(依赖.index文件)

  1. 遍历分区的所有分段,找到"起始Offset ≤ 目标Offset ≤ 下一分段起始Offset"的目标分段;
  2. 在目标分段的.index文件中,通过二分查找找到"小于等于目标Offset的最大索引项";
  3. 索引项包含该Offset在.log文件中的物理偏移量,从该位置顺序读取即可找到目标消息。

# (2)按时间戳查询(依赖.timeindex文件)

  1. 遍历所有分段,通过.timeindex找到包含目标时间戳的分段;
  2. 在该分段内通过时间戳索引定位对应的Offset;
  3. 后续流程同"按Offset查询"。

# 4. 日志清理:可控的存储管理

Kafka不会永久存储消息,通过清理机制释放磁盘空间,主要有两种清理策略:

# (1)基于时间的清理(默认策略)

  • 触发条件:分段创建时间超过log.retention.hours(默认168小时,即7天);
  • 执行逻辑:删除整个过期分段(仅删除非活跃分段,避免影响活跃写入)。

# (2)基于大小的清理

  • 触发条件:分区总大小超过log.retention.bytes(默认-1,即不限制);
  • 执行逻辑:从最旧的分段开始删除,直到总大小低于阈值。

# (3)日志压缩(针对Key-Value消息)

  • 适用场景:需保留每个Key最新值的场景(如用户配置、状态更新);
  • 执行逻辑:对相同Key的消息,仅保留最新Offset的消息,删除历史版本;
  • 配置方式:通过log.cleanup.policy=compact启用,配合log.cleaner.min.compaction.lag.ms控制压缩延迟。

# (4)清理触发机制

由Broker后台线程(LogCleaner)定期检查(默认每30秒),符合条件则执行清理,避免阻塞正常读写。

# 四、总结:设计为何支撑高吞吐与高可靠?

Kafka的消息存储机制通过三层设计与全生命周期管理,完美平衡了性能与可靠性:

  • 高吞吐:分区隔离、顺序写入、页缓存利用,最大化磁盘IO效率;
  • 高可靠:多副本同步、ISR机制、可配置的acks策略,确保消息不丢失;
  • 可扩展:分段化存储与索引优化,支持TB级数据高效管理。

这些设计细节共同构成了Kafka作为分布式消息系统的核心竞争力,也是其在大数据场景中广泛应用的底层支撑。

编辑 (opens new window)
#Kafka消息存储
上次更新: 2026/01/21, 19:29:14
Kafka的深度解析
Kafka的生产消费

← Kafka的深度解析 Kafka的生产消费→

最近更新
01
订单超时取消
01-21
02
双 Token 登录
01-21
03
长短链接跳转
01-21
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式