Skip to main content

kafka

📅 2026-03-19 ✏️ 2026-04-18 CS INFRA
No related notes

1 · kafka#

分布式事件流平台

S 大规模分布式系统中,各服务之间需要实时传递海量事件/数据(日志、指标、用户行为等),且数据需要被多个下游系统独立消费。
C 传统点对点通信导致系统紧耦合、难以扩展;同步调用在高峰期造成级联故障;数据一旦消费就丢失,无法回放或审计。
Q 如何在解耦生产者与消费者的同时,实现高吞吐、可持久化、可回放、可水平扩展的数据传输?
A Kafka 以分布式追加写日志为核心抽象:生产者只管写,消费者按自己的速度读并记录 offset,数据持久化在多副本分区中,可回放、可复制、可水平扩展。

问题:规模系统之间的数据传输
方法:以追加写日志为核心抽象,构建分布式提交日志;消费者只记录自己读到哪儿

Kafka 把数据的生产存储消费解耦,并让数据像日志一样可回放、可复制、可扩展

1.1 · 核心概念

数据流角度:Producer 写消息 -> Broker 存储并复制消息 -> Consumer 消费消息。
业务角度:Topic 是逻辑上的消息分类;Partition 是 Topic 下的实际分片,也是实际读写单位;Broker 是 Kafka 集群中的服务器节点,负责保存多个 Partition 副本,并参与副本复制。

  1. Topic:逻辑上的消息分类,表示一类业务日志或事件
  2. Partition:一个 Topic 可以拆成多个分区;它是实际的读写单位
    • 是扩展性、并行消费、分区内有序的基础
    • 数据会分布到不同 Broker 上
  3. Offset:分区内消息的编号,消费者用它记录读到哪里
  4. Broker:Kafka 集群中的单个服务器节点,负责保存多个 Partition 副本
  5. Producer:生产者,负责写消息
  6. Consumer:消费者,负责读消息
  7. Consumer Group:共同消费一个 Topic 的一组消费者
    • 一个 Partition 可以被多个消费组消费
    • 但在同一个消费组内,一个 Partition 同一时刻只能分配给一个消费者
    • 消费者组本质上就是一套独立的阅读进度
    • Kafka 不会因为已消费就立刻删除消息,而是按保留策略保存一段时间

1.2 · 数据组织与扩展性

核心靠 Partition

  • Partition 决定了 Kafka 的吞吐上限、并行消费能力和水平扩展能力
  • 分区越多,通常越容易横向扩展,但管理成本和重平衡成本也会上升
  • 消费者组内的并发上限受 Partition 数限制
    • 分区数 < 消费者数:多出来的消费者会空闲
    • 分区数 = 消费者数:通常能充分利用组内并发
    • 分区数 > 消费者数:单个消费者需要处理多个分区

1.3 · 顺序性(分区内有序)

  • Kafka 只保证分区内有序
  • 不保证一个 Topic 的全局有序
  • 如果业务要求同一类对象的消息有序,通常做法是让同一个 key 始终进入同一个分区

1.4 · 生产者设计

如何稳定高效地发?

分区策略:决定消息写入哪个分区?

  • 指定 key:同一 key 进入同一分区,适合做局部有序
  • 轮询:更均匀地打散流量
  • 自定义:按业务规则路由

生产者常见关注点:

  • 幂等性:避免因为重试导致重复写入
  • 重试:临时失败时自动重发,但要结合幂等性一起看

1.5 · 消费者设计

Pull 模型:消费者主动拉取数据。

好处:

  • 消费速度由消费者自己控制
  • 更适合批量拉取
  • 可以回放历史数据
    • 消费者可以通过指定 offset,重新读取之前已经消费过的消息

消费者需要决定何时提交 offset:

  • 自动提交:简单,但可能导致重复消费或丢消息窗口变大
  • 手动提交:更可控,通常在业务处理成功后再提交

Rebalance:消费者加入、离开、心跳超时时,Kafka 会重新给组内消费者分配分区;期间整个组可能短暂停顿。

  • 常见触发:消费者崩溃、新消费者加入、处理太慢导致心跳超时
  • 缓解方式:Cooperative Rebalance(增量再平衡,只迁移受影响分区)、Static Group Membership(固定成员 ID,短暂重启不触发)

1.6 · 可靠性(单 leader + followers 拉取)#

  • 每个 Partition 可以有多个副本
    • Leader:负责对外读写
    • Follower:同步 Leader 数据
  • ISR(In-Sync Replicas):当前跟得上 Leader 的副本集合
  • acks:生产者写入确认机制
    • acks=0:最快,但可能丢
    • acks=1:Leader 写入成功就返回
    • acks=all / -1:等待 ISR 中足够副本确认后返回

投递语义

  • at most once:最多一次,可能丢,不重复
  • at least once:至少一次,可能重复
  • exactly once:恰好一次

1.7 · 为什么快:设计选择

  • 顺序 IO,而不是随机 IO
  • 零拷贝:减少数据在内核态、用户态之间的复制
  • 批量处理:批量收发,而不是一条条处理
  • Page Cache:充分利用操作系统页缓存
    • 写路径:Producer -> Broker -> Page Cache(写入内存后较快返回)-> 异步刷盘
    • 读路径:Consumer 拉取时,如果数据还在 Page Cache,可通过零拷贝直接从内存发到网卡

1.8 · 常见问题

问题:为什么追随者不提供服务?

主要是为了简化一致性模型,保证写后立刻读、单调读等语义更容易成立。

问题:如何实现 topic 内消息的有序?

单分区内天然有序;如果要保证某类业务消息有序,本质上是分区策略问题,让同一业务 key 始终进入同一分区。

问题:如何实现不丢失数据?

生产者侧通常要求 acks=all;再结合副本机制与消费者正确提交 offset,才能把丢失风险压低。

问题:如何保证消息不被重复消费?

重复通常很难彻底避免,实际做法是让重复无害

  • 生产者侧:开启幂等,减少因重试导致的重复发送
  • 消费者侧:做消费幂等,比如唯一 ID、Redis 近期处理 key、数据库唯一索引等

问题:消息积压怎么处理?生产太快?消费太慢?

  • 增加消费者,但不能超过分区数,否则多出来的消费者会空闲
  • 如果分区数不够,需要扩分区,再增加消费者
  • 紧急情况可以起临时消费组把积压数据转储出来,再慢慢处理