01. Kafka 核心概念与架构

Kafka 基础: Topic、Partition、Broker、偏移量与日志存储结构

Kafka 最初由 LinkedIn 开发,后捐献给 Apache 基金会,已成为现代分布式系统中事实标准的流处理平台。它不仅仅是一个消息队列,更是一个分布式日志系统——核心理念是将所有数据流视为不可变的顺序日志(Log)。理解这一设计哲学,是掌握 Kafka 高吞吐、高容错和高一致性能力的起点。


1. Kafka 架构全景

┌─────────────────────────────────────────────────────────────────┐
│                        Kafka Cluster                             │
│  ┌────────┐    ┌────────┐    ┌────────┐                        │
│  │Broker 1│    │Broker 2│    │Broker 3│   ← Controller 选举     │
│  │(Leader P0)│ │(Leader P1)│ │(Leader P2)│                      │
│  │(Replica P2)││(Replica P0)││(Replica P1)│                     │
│  └────┬───┘    └────┬───┘    └────┬───┘                        │
│       └─────────────┴─────────────┘                              │
│                   ZooKeeper / KRaft                               │
└─────────────────────────────────────────────────────────────────┘
        ↑                                    ↓
   ┌────┴────┐                        ┌────┴────┐
   │Producer │                        │Consumer │  (Consumer Group)
   └─────────┘                        └─────────┘

Kafka 的核心角色包括:

  • Producer(生产者):向 Topic 发送消息的客户端,可选择性地指定 key 以控制分区路由
  • Consumer(消费者):从 Topic 订阅消息的客户端,通过 Consumer Group 实现并行消费和负载均衡
  • Broker(代理):Kafka 服务器进程,负责存储和转发消息,一个集群可包含数十甚至上百个 Broker
  • Topic(主题):逻辑上的消息分类,生产者按 Topic 发送,消费者按 Topic 订阅
  • Partition(分区):Topic 的物理分片,每个分区是一个有序、不可变的消息日志
  • Offset(偏移量):消息在分区内的唯一位置标识,消费者通过偏移量追踪读取进度
  • ISR(In-Sync Replicas):与 Leader 保持同步的副本集合,是 Kafka 高可用的核心机制

2. Topic 与 Partition 设计

为什么需要分区? 分区是 Kafka 实现水平扩展的核心机制。每个 Topic 可以划分为多个 Partition,每个 Partition 独立存储在不同的 Broker 上。

Topic: orders (3 分区)

Partition 0         Partition 1         Partition 2
┌────────┐         ┌────────┐         ┌────────┐
│offset 0│         │offset 0│         │offset 0│
│msg_001 │         │msg_002 │         │msg_003 │
├────────┤         ├────────┤         ├────────┤
│offset 1│         │offset 1│         │offset 1│
│msg_004 │         │msg_005 │         │msg_006 │
├────────┤         ├────────┤         ├────────┤
│  ...   │         │  ...   │         │  ...   │
└────────┘         └────────┘         └────────┘
  Leader@B1          Leader@B2          Leader@B3
  Replica@B2         Replica@B3         Replica@B1

分区的关键特性:

特性说明
顺序性单个 Partition 内消息严格有序,Partition 之间无全局顺序
并行度Partition 数量决定了单 Consumer Group 的消费并行度上限
持久化消息保留策略(retention)按 Partition 独立管理
副本机制每个 Partition 有 N 个副本(replication.factor),Leader 负责读写,Follower 同步

3. 偏移量与消费模型

Kafka 使用"拉取(Pull)“模型而非"推送(Push)",消费者主动从 Broker 拉取消息。这种设计赋予消费者完全的控制权:

Consumer Group: order-processors
  ┌─────────────────────────────────────┐
  │  Consumer 1  ──→ Partition 0 (offset=1024)  │
  │  Consumer 2  ──→ Partition 1 (offset=856)   │
  │  Consumer 3  ──→ Partition 2 (offset=1201)  │
  └─────────────────────────────────────┘

offset 存储在 __consumer_offsets Topic 中(Kafka 0.9+ 之前存储在 ZooKeeper)

偏移量管理策略:

自动提交(默认):

props.put("enable.auto.commit", true);
props.put("auto.commit.interval.ms", 5000); // 每 5 秒自动提交

手动提交(精确控制):

props.put("enable.auto.commit", false);
// 处理完一批消息后精确提交
try {
    consumer.commitSync(); // 同步提交,阻塞直到成功
    // consumer.commitAsync(); // 异步提交,不阻塞
} catch (CommitFailedException e) {
    // 处理提交失败(如 Rebalance 期间)
}

4. Kafka 为什么快?底层原理

Kafka 的高性能不是偶然,而是多项精妙设计的叠加:

4.1 顺序写磁盘

传统数据库的随机写磁盘是性能杀手。Kafka 将消息追加写入日志文件的末尾,磁盘的顺序写入性能接近内存写入。

随机读写 vs 顺序读写(普通 HDD):
  - 随机读写:约 100 KB/s
  - 顺序读写:约 100 MB/s(1000x 差距)

4.2 零拷贝(Zero-Copy)

Kafka 使用 Linux 的 sendfile() 系统调用,将数据直接从 Page Cache 发送到网络 Socket,避免了传统的用户态/内核态数据拷贝。

传统流程:磁盘 → 内核缓冲区 → 用户缓冲区 → Socket 缓冲区 → 网卡
零拷贝流程:磁盘 → Page Cache → 网卡(sendfile)
节省:2 次数据拷贝 + 2 次上下文切换

4.3 批量处理与压缩

生产者将多条消息批量发送(batch),减少网络往返。同时支持多种压缩算法:

压缩算法CPU 开销压缩比建议场景
none无1xCPU 敏感场景
gzip高高网络带宽受限
snappy低中默认推荐(Kafka 官方)
lz4极低中低极致低延迟
zstd中最高Kafka 2.1+ 推荐

5. KRaft:去 ZooKeeper 的新纪元

Kafka 3.0+ 引入了 KRaft(Kafka Raft)模式,彻底移除了对 ZooKeeper 的依赖:

维度ZooKeeper 模式KRaft 模式
元数据存储ZooKeeper Ensemble内部 Raft 日志
架构复杂度双系统运维单一系统
Controller 选举依赖 ZK内部 Quorum 选举
性能元数据操作有 ZK 延迟更高效的元数据传播
可扩展性分区数上限约 100K分区数上限显著提升(测试达 2M+)

KRaft 的核心是将 Topic、Partition、Broker 等元数据以 Raft 日志的形式在 Controller Quorum 中复制,避免了 ZK 的瓶颈。


6. 日志存储细节

Kafka 将每个 Partition 视为一个目录,内部由多个 Segment 文件组成:

$KAFKA_LOG_DIR/orders-0/
├── 00000000000000000000.log      # 消息数据(第 0~999 条)
├── 00000000000000000000.index    # 稀疏索引(offset → 文件位置)
├── 00000000000000000000.timeindex # 时间索引
├── 00000000000000001000.log      # 消息数据(第 1000~1999 条)
├── 00000000000000001000.index
└── 00000000000000001000.timeindex

日志清理策略:

  • 基于时间(默认):log.retention.hours = 168(7 天),超过时间的 Segment 被删除
  • 基于大小:log.retention.bytes,超过总大小的旧 Segment 被删除
  • Log Compaction:保留每个 key 的最新值,用于 KTable 等场景

7. 生产环境关键配置

# Broker 配置
broker.id=1
listeners=PLAINTEXT://:9092
log.dirs=/var/lib/kafka/data
num.network.threads=8          # 网络线程数
num.io.threads=16              # IO 线程数
log.retention.hours=168        # 消息保留 7 天
log.segment.bytes=1073741824   # Segment 大小 1GB
log.retention.check.interval.ms=300000  # 5 分钟检查一次

# Topic 默认配置
num.partitions=3               # 默认分区数
default.replication.factor=3   # 默认副本数
min.insync.replicas=2          # 最小 ISR 大小(acks=all 时生效)

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「kafka」更多文章

  1. 集群升级与滚动重启实践
  2. Kafka 应用测试策略:Testcontainers 与集成测试
  3. 压缩算法选型:lz4、zstd、snappy 与 gzip