一、Kafka 架构与基本原理
1. Kafka 是什么?
Kafka 是⼀个分布式流式处理平台。
-
流式处理:数据以事件的形式持续不断地产生,不是像传统批处理那样一次性导入全部数据。
-
分布式:
-
Kafka 可以运行在多个服务器节点(Broker)上。
-
每个主题(Topic)可以分为多个分区(Partition),分布在不同节点。
-
它的 Producer 和 Consumer 也支持水平扩展。
-
特点:高吞吐、低延迟、可扩展、高容错(持久化)。
主要的使用场景:
-
消息队列
-
日志收集(通过 Kafka 将日志传递到监控系统)
-
流计算:实时数据传递给实时计算系统
Go 中使用的是什么 Kafka 客户端?
使用的是 Shopify 开源的 Sarama 客户端。
2. 核心组件有哪些?各自的作用是什么?
Producer、Consumer、Broker、协调器
-
Producer 生产者:将数据发布到指定的 Topic 上面。Kafka 会根据要求将数据分散到 Topic 的不同分区之中去。
-
Consumer 消费者:从 Topic 中读取数据。消费者可以属于某个消费组,可以让多个消费者负载均衡地读取数据。
-
Broker 消息代理:Kafka 的核心,消息在这里存储,每个 Kafka 集群可以包含多个 Broker,负责接收、存储、发送数据。
-
协调器
-
Zookeeper 协调器:用于 Kafka 的分布式协调和管理,比如存储 Broker 的元数据信息、分区列表、Loader 等。Kafka 3.0 之后可选择移除。
-
Kafka Controller: Kafka 的内部组件,负责管理分区的 leader 分配等,Kafka 3.0 起逐步替代 ZooKeeper。
-
Zookeeper 被移除了吗?
截至 Kafka 3.7(2024 年)及之前版本,Kafka 仍然支持 ZooKeeper 模式:
3. 🌟 Kafka 的 Topic、Partition、Offset 是什么?它们之间的关系?
Topic 是什么有什么作用?
用于区分不同类型的消息。每个 Topic 都有一个名称,生产者发消息到特定的 Topic 上,消费者从特定的消费者消费。例如,点赞事件可以创建一个 Topic 叫 "like_topic"。
如何创建一个 Topic?
- 使用命令行创建。使用
kafka-topics.sh
来创建。指定 Broker 的地址、副本数、分区数、Topic 名。这里指定副本后,Kafka 会自动将这些副本分散到不同的 broker 上(前提是有 3 个及以上的 Broker)。
kafka-topics.sh --create \
--bootstrap-server localhost:9092 \
--replication-factor 3 \ 设置副本数量
--partitions 4 \
--topic my-topic
- 使用 API 创建。例如 Sarama 中,使用 CreateTopic 函数。
// Topic 名称
topicName := "my-sarama-topic"
// Topic 配置
topicDetail := &sarama.TopicDetail{
NumPartitions: 3, // 分区数
ReplicationFactor: 2, // 副本数
ConfigEntries: map[string]*string{ // 可选配置项
"retention.ms": strPtr("604800000"), // 保留 7 天
},
}
// 创建 Topic
err = admin.CreateTopic(topicName, topicDetail, false)
查看已经创建的 Topic、详情。
列出所有 Topic:kafka-topics.sh --list --bootstrap-server localhost:9092
展示 Topic 详情:kafka-topics.sh --describe --topic my-topic --bootstrap-server localhost:9092
删除 Topic:kafka-topics.sh --delete --topic my-topic
Partition 是什么有什么作用?
每个 Topic 会被划分为一个或多个分区,每个分区就是一个有序的、不可变的消息队列(日志文件)。
-
每条消息会被顺序地追加到某个分区的末尾。
-
消息在分区内具有严格的顺序,但不同分区之间顺序不保证。
Topic: order-events
Partition 0 --> msg1, msg2, msg3, ...
Partition 1 --> msg4, msg5, msg6, ...
Partition 2 --> msg7, msg8, msg9, ...
为什么要多个分区?如果只有一个分区,只能一个 Consumer 顺序消费,就会导致性能瓶颈。
怎么指定消息去哪个分区?
-
使用 Send 函数显式指定:
send(topic, partition, key, value)
-
按 key 进行 hash 分配:同一个 key 的消息会被发送到同一个分区(保持有序)
-
轮询(默认):没有 key 时轮询分配给不同分区,负载均衡
Offset 是什么?
Offset(偏移量) 是用于标识和追踪消息在分区中的位置的核心机制。Offset 是 Kafka 中每条消息在某个分区(Partition)中的唯一编号,从 0 开始递增。
offset: 0 1 2 3 4
message: login click buy logout pay
-
每条消息被追加时,自动分配一个顺序递增的 offset。
-
消费者(Consumer) 在消费完一条消息后,会提交它的 offset,表示“我已经处理完了这个位置”。
如何追踪消费进度?
Kafka 消费者客户端(如 Java、Go)默认会定时提交 offset 到 Kafka 的内置 topic:__consumer_offsets
。
可以使用 kafka-consumer-groups.sh
查看:
kafka-consumer-groups.sh \
--bootstrap-server localhost:9092 \
--describe \
--group my-consumer-group
TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAG CONSUMER-ID
user-events 0 350 360 10 consumer-1
-
CURRENT-OFFSET
: 消费者组消费到哪了 -
LOG-END-OFFSET
: 分区中最新的 offset -
LAG
: 当前滞后数量(未消费的消息数)
支持重置 Offset 吗?
支持,使用
kafka-consumer-groups.sh
:kafka-consumer-groups.sh \ --bootstrap-server localhost:9092 \ --group my-group \ --topic user-events \ --reset-offsets --to-earliest --execute
支持的选项包括:
--to-earliest
(从最早开始)
--to-latest
(从最新开始)
--shift-by -5
(回滚 5 条)
--to-offset 1234
(跳到固定位置)
联系
Topic 是分类,Partition 是分布式存储单元,Offset 是分区内的消息索引。
4. 🌟 Kafka 为什么能做到高吞吐量?有哪些关键设计?
-
顺序 I/O。 顺序追加到磁盘的日志文件中,避免了随机磁盘写入带来的性能瓶颈。
-
零拷贝(Zero-copy)。使用 Linux 的
sendfile()
系统调用,将数据从磁盘直接传输到网络缓冲区,无需应用程序层拷贝,提高了 I/O 效率,降低了 CPU 开销。 -
生产者和 Broker 支持批量发送和批量写入,一个批次可以包含多条消息,减少网络调用次数。消费者也支持批量拉取。
-
分区。将 Topic 划分为多个 Partition,天然支持 并行读写。
-
Kafka 的日志文件由多个 Segment 文件 构成,便于管理和清理。使用页缓存(Page Cache)提升读性能。
5. 🌟 Kafka 的存储机制是怎样的?怎么做到高效读写?(日志段 + 索引 + 顺序写)
在 Apache Kafka 中,消息默认是被写入磁盘的。
Kafka 中的每个 Partition 对应一个目录,该目录下是按照时间或大小分割的多个 segment 文件,每个 segment 包含:
-
一个
.log
文件:保存真实的消息数据(顺序写入) -
一个
.index
文件:保存 offset 到文件位置的索引 -
一个
.timeindex
文件:记录时间戳与位置关系(用于时间查找)
写入流程:
-
Producer 发送消息到 Broker
-
Kafka 会将消息 顺序写入到当前 Partition 的活跃 segment 文件中(.log)
-
同时更新内存页缓存与 offset 索引文件
-
根据配置定期落盘(flush)到磁盘
读取流程:
-
Consumer 向 Broker 发送 Fetch 请求,指定 partition 和 offset
-
Kafka 通过
.index
快速定位到消息在.log
文件中的物理位置 -
利用操作系统页缓存直接读取内存或触发顺序磁盘读
-
使用 零拷贝(sendfile) 直接将数据从磁盘传到 socket,提升效率
6. 🌟 Kafka 是如何实现消息的持久化和可靠性的?
通过日志文件 (log files) 进行持久化。
日志写入流程:
-
Producer 发送消息 到某个 Topic 的某个 Partition。
-
Broker 接收消息,先写入Page Cache(操作系统缓存)。
-
Kafka 使用 顺序写入磁盘 的方式,把数据持久化到 log segment 文件中(.log)。
-
如果
acks=all
且消息同步到所有 ISR 副本,则返回 ACK 给 Producer。
为什么 Kafka 写磁盘还这么快?
-
顺序写入
-
使用操作系统的页缓存进行优化。
-
零拷贝 (zero-copy)。Kafka 使用 sendfile 系统调用直接从文件发送给客户端,绕过应用层。
Kafka 每个 Partition 都对应一个目录,内容如下:
/tmp/kafka-logs/user-events-0/
├── 00000000000000000000.log ← 消息日志(实际数据)
├── 00000000000000000000.index ← 位移索引
├── 00000000000000000000.timeindex← 时间索引
Broker 崩溃的时候,重启后,使用本地日志恢复
-
Broker 重启时,会扫描其本地的所有分区目录:
-
恢复
.log
文件(即消息本体) -
恢复
.index
/.timeindex
(索引文件)
-
-
如果检测到某个 segment 没有正确关闭(如异常宕机),Kafka 会:
-
重建 segment 索引
-
截断未完成写入的部分(删除末尾可能写坏的部分)
-
-
完成本地日志恢复后,Broker 会向 Controller 报告其状态。
7. Kafka Controller 的作用
Kafka Controller 是 Kafka 集群中负责管理元数据和集群协调 的角色(在 2.8 版本之前是 Zookeeper)。在一个 Kafka 集群中,只会有一个 Broker 被选为 Controller(主控制器),其余是候选。
-
分区领导者 Partition Leader 选举:Kafka 中的每个 topic 都会被分成多个 partitions,每个 partition 有一个 leader 和多个 follower。Controller 负责为每个 partition 选举一个 leader。这个 leader 负责读写请求,其它 follower 跟随它的数据变化。相当于 Redis 中的主从复制。
-
Broker 监控与管理:Kafka Controller 会监听其他 broker 的状态。如果发现有 broker 加入或离开集群,Controller 会触发重新分配分区领导者和副本(replica)。
-
Replica (副本)状态管理:控制副本(replica)的同步状态(In-Sync Replicas,ISR)。管理 partition 的副本是否仍然可用、是否落后等。
-
处理集群变更事件:比如 topic 创建/删除、分区数量变更等,Controller 都要做出相应处理并广播更新信息给所有 broker。
二、生产者(Producer)相关
8. 🌟 消息是如何从 Producer 发送到 Kafka 的?批量发?
-
消息为字节数组。
-
选择一个分区。
-
把消息发送到缓冲区。当缓冲区达到一定的大小(或者时间超过阈值),将消息批量发到 Brokers。
通过批量发送机制,可以减少网络的调用,减小 Broker 的压力。
9. 🌟 Kafka 中 acks=0/1/all 分别表示什么?影响可靠性的哪些方面?Acks 的值如何影响性能?
acks
定义了 Producer 在写入消息时,要求多少副本确认成功 才认为这条消息写入成功。
-
acks=0,极速但是不可靠。都不一定写入 Leader,一旦 Broker 异常,可能导致消息 直接丢失。
-
acks=1,性能和可靠性的折中。Producer 等待 Leader Broker 写入磁盘成功 即可返回。但是可能会丢消息(可能没有副本备份)
-
acks=all/-1,性能最差但是最可靠。Producer 只有在 ISR 中所有副本都确认写入成功后才返回。如果 ISR 中某个副本滞后太久、不可达,Kafka 会拒绝写入。
10. 🌟 Kafka Producer 如何保证幂等性?幂等性是如何实现的?
“幂等性” 指的是生产者发送消息到 Kafka 集群时,无论发送多少次相同的消息,Kafka 集群最终只会保存一条该消息,且仅会处理一次。
Kafka 的幂等性是通过启用 幂等生产(idempotent producer) 实现的。
enable.idempotence=true
🌟 怎么实现的幂等性:Kafka 会自动为每个 Producer 分配一个唯一的 ID,也就是 Producer ID。同时,它会为每个分区维护一个消息的序列号,每发一条消息,序号都会递增。Kafka 的 Broker 会记录每个 Producer 最后一次写入的序列号。这样如果 Producer 因为网络原因重试了某条消息,Broker 就能通过序列号识别出这是一条重复的消息,从而丢弃它。
11. Kafka 的分区策略是怎样的?如何自定义消息的分区?
每个 Topic 会被划分为一个或多个分区,每个分区就是一个有序的、不可变的消息队列(日志文件)。
-
每条消息会被顺序地追加到某个分区的末尾。
-
消息在分区内具有严格的顺序,但不同分区之间顺序不保证。
Topic: order-events
Partition 0 --> msg1, msg2, msg3, ...
Partition 1 --> msg4, msg5, msg6, ...
Partition 2 --> msg7, msg8, msg9, ...
为什么要多个分区?如果只有一个分区,只能一个 Consumer 顺序消费,就会导致性能瓶颈。
怎么指定消息去哪个分区?
-
使用 Send 函数显式指定:
send(topic, partition, key, value)
-
按 key 进行 hash 分配:同一个 key 的消息会被发送到同一个分区(保持有序)
-
轮询(默认):没有 key 时轮询分配给不同分区,负载均衡
自定义分区策略
生产中如果需要更复杂的分区逻辑,比如根据用户类型、地理位置等分区,我们可以实现 Partitioner 接口,定义自己的分区规则,然后在 Producer 配置中指定这个类名即可。
三、消费者(Consumer)相关
12. Kafka 的消费模型是怎样的?消费者组(Consumer Group)如何工作?
每个 Kafka 消费者(Consumer)会从指定的 topic 的分区(partition) 中拉取消息。
Kafka 消费模型支持:
-
点对点消费(每条消息只被一个消费者组里面的唯一消费者处理),适用于任务处理、日志入库等场景。
-
发布订阅(每个消费者组都能消费所有消息),适用于需要广播的场景,例如数据同步。
Kafka 的“消费模型”不是通过某个设置项来配置的,而是通过“消费者组的组织方式”天然决定的。取决于我们怎么用而已。例如我们项目中实现的浏览量统计就属于的是点对点消费。
一个 Consumer Group 是由多个消费者实例组成的组,用于协调消费任务,实现消息的负载均衡处理。
-
Kafka 将一个 topic 的 多个分区平均分配给消费者组中的不同消费者。
-
同一组内,每个分区只能被 一个消费者消费。
-
不同组之间可以并行消费 相同的消息。
什么是 再平衡 Rebalance ?
当消费者组中有消费者加入、离开或挂掉时,Kafka 会触发 rebalance,重新分配分区。
🌟 消费者是怎么做到批量消费的?
在 Kafka 中的消费者(Consumer)实现批量消费,是通过 一次 poll 拉取多个消息 实现的,不是一条条单独处理,而是批量拉取 + 批量处理。
13. Kafka 的分区分配策略?(如何将分区分配给消费组中的消费者)
分区分配策略:当多个消费者加入同一个 Consumer Group 时,Kafka 如何把 Topic 的分区分配给这些消费者。
-
Range 范围:按照范围将分区分配给消费者。
-
RoundRobin 轮询:均匀分配的方式进行分配。
假设:
-
topicA: P0, P1, P2
-
topicB: P0, P1, P2
-
两个消费者,分配:
C1 → topicA-P0, topicB-P1, topicA-P2 C2 → topicA-P1, topicB-P0, topicB-P2
-
-
Sticky 粘性(2.4 版本起成为默认策略):在轮询的基础上,还能保证尽可能保持上一次分配的结果。减少分区重新平衡导致的延迟和性能损失。
-
协作粘性分配(2.4 + 版本引入)
当使用消费者组时,Kafka 会把分区平均分配给消费者。但如果:
-
有新消费者加入(扩容)
-
有消费者掉线(故障)
-
有 Topic 变化(新增分区)
Kafka 就会触发 再平衡 Rebalance,把分区重新分配一遍。传统 Rebalance 是“暴力式”的:所有消费者 先丢掉全部分区(revoke),然后再统一重新分配(assign)。
然后就引入了协作粘性分配:能实现“渐进式分区迁移”,不必每次都 revoke 所有分区。
14. Kafka 如何管理 offset?自动提交 vs 手动提交,有什么区别?
Kafka 使用 offset 来确保 消费者知道上次消费到哪了,从而实现可控的消费进度和容错恢复。
Kafka 会把 offset 存储在一个特殊的内部 topic 中:
__consumer_offsets
这个 topic 会记录每个 (consumer group, topic, partition)
的 offset 值。
注意,每个 cosumer group 都会有一个关于这个分区的 offset 记录。
Kafka 的消费者有两种提交 offset 的方式:
- 自动提交,在配置文件中开启。
enable.auto.commit=true
auto.commit.interval.ms=5000
Kafka 每隔 auto.commit.interval.ms
毫秒自动提交当前消费的 offset。适合对“消息不重要 / 允许重复消费”的场景。但是可能会导致消息没有真正消费成功却自动提交了,造成消息的丢失。
- 手动提交,即关闭自动提交
enable.auto.commit=false
消费失败用户可以不提交 Offset,那么这个分区后面的消息是否会被阻塞?
kafka 的 offset 是按 partition 和 group 串行推进的,如果消费失败但没有提交 offset,后续的消息在该消费者组内会被阻塞,因为 Kafka 要求每个分区内的消息顺序消费。
15. 一个 partition 会被多个 consumer 消费吗?为什么?
不会。一个 partition 在同一个 Consumer Group 内只能被一个 consumer 实例消费。
不同的 Consumer Group 里面的 Consumer 可以消费同一个分区吗?
Consumer Group A 和 Consumer Group B 都订阅了 Topic T。
Partition P0 的消息,会分别被 Group A 和 Group B 中的消费者消费一次。这属于发布订阅模式。
每个 cosumer group 都会有一个关于这个分区的 offset 记录,因此 kafka 天然支持多个 group 消费同一个消息。
16. 如何处理消费失败的消息?Kafka 有死信队列机制吗?
消费失败的消息可以创建重试机制完成。尝试重新处理失败的消息 N 次。如果超过重试次数就记录错误日志,人工介入或手动消费处理。
死信队列(Dead Letter Queue,DLQ)是一种用于处理无法正常处理的消息的机制。在消息队列系统中,当消息因为各种原因(如格式错误、处理逻辑异常、目标系统不可用等)无法被成功消费或处理时,这些消息就会被发送到死信队列。死信队列可用于人工核查。
和 RabbitMQ 不同,Kafka 本身没有内建死信队列(DLQ)机制。为什么?Kafka 最初是为构建高吞吐量的日志收集系统而设计的,在这种场景下,消息丢失比消息出错更严重 ,所以 Kafka 更关注的是不丢消息、可回溯消费等能力。
而且 kafka 允许用户自己控制 Offset 的提交,那么消费失败用户可以不提交 Offset,然后重新拉取消息重试。RabbitMQ 消息是否被确认是由 broker 控制的。
但是我们可以手动实现死信队列。在应用层实现。
做法是在消费失败后,手动将消息 produce 到一个名为 dlq-xxx
的 Kafka topic 中。
然后消费者再消费这个 Topic 达成重试的效果。
四、可靠性与顺序性
17. 🌟 Kafka 是如何保证消息顺序的?
Kafka 是在单个分区内保证消息顺序的。只要生产者发送消息到同一个分区,Kafka 保证这些消息按照发送顺序被写入、被拉取、被消费。跨分区的顺序是不保证的。
某个 Topic 有多个分区,但是我想保证 Topic 内消息有序,怎么做?
-
最简单的就是减少到一个分区,但是会导致性能问题
-
Producer 发送消息时带上 key(如用户 ID、订单号等)。这样相同的 Key 的消息会进入同一个分区。最常见的场景就是,同一个用户的消费需要有序,方便扣款。
topic := "user-events"
key := "user123" // 👈 同样的 key 保证进入同一个分区
err = p.Produce(&kafka.Message{
TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
Key: []byte(key),
Value: []byte("some message for user123"),
}, nil)
此外,写入有序后,需要保证每个分区只能由一个消费者消费。否则仍然不能保证顺序。
18. 🌟 Kafka 是如何保证消息“不重复” 或者 “不丢失”的?
-
如何保证不重复的?使用生产者幂等性 + Exactly Once 来实现不重复。
-
什么情况下消息会丢失?
-
例如 ack 设为 0 或者 1 的时候
-
设置自动提交 Offset,但是消费者并没有 正确处理的时候
-
-
如何保证不丢失?
-
生产端
-
使用 ACKS = all
-
设置重试机制
-
-
Broker
-
持久化到日志
-
每个分区创建多个副本
-
配置最小 ISR 数量
-
-
消费端
- 手动提交 offset
-
19. 🌟 Kafka 能做到 exactly-once 吗?具体怎么实现?
使用 Kafka 实现 Exactly Once 语义(EOS),也就是确保:
每条消息只被处理一次,既不重复、也不丢失,即使在重试、故障等情况下也能保持数据一致性。
Exactly Once 意味着:
-
Producer 只写入一次
-
Consumer 只处理一次
-
状态系统(如数据库、状态存储)保持一致
Kafka 的 Exactly Once 是通过幂等性 + 事务机制实现的。
-
幂等:生产者幂等(见上面)
-
事务机制:将批量发送消息到 Broker 作为一个原子事件处理。实现原子写入。
Consumer 端也要配合事务才能实现 EOS:
isolation.level=read_committed # 读已提交
这样 Consumer 只会读到已经 commit 的事务性消息,不会读到中间状态的数据。
事务怎么实现的?
事务机制通过事务管理器、生产者、消费者进行实现。
事务使用两阶段提交协议,第一阶段生产者发送消息但不提交,第二阶段,事务管理器确定事务是否提交或中止,通知生产者执行最终提交或者回滚。
五、扩展与高可用
20. 🌟 Kafka 的副本机制是怎样的?ISR 是什么?AR、OSR 呢?
Kafka 中每个分区(Partition)都可以配置多个副本(Replica),包括 1 个 Leader 副本和多个 Follower 副本。副本之间的数据通过复制保持一致。
每个分区的副本会分散在不同的 Broker 上面。
所有写操作都会落到 Leader 副本。Follwer 副本会被动地从 Leader 同步数据。
副本同步流程:
-
Producer 写消息到 Leader(Broker 1)
-
Follower 副本(Broker 2 和 3)从 Leader 拉取数据
-
副本写入成功后,才会向生产者返回 ACK:写入成功时,Leader 等待 ISR 中副本的 ACK(可配置),若满足 acks 条件,就返回成功响应给 Producer
可以使用 kafka-topics.sh
的 describe 命令查看副本详情:
kafka-topics.sh --describe --topic your-topic --bootstrap-server localhost:9092
Topic: order-events Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
怎么设置副本因子?
使用 replication.factor = 3
参数进行设置。副本因子数量不能大于 Broker 的数量。
kafka-topics.sh --bootstrap-server <broker:port> \
--create \
--topic my-topic \
--partitions 3 \
--replication-factor 3
什么是 ISR?
ISR(In-Sync Replicas,同步副本集合)。
ISR 是与 Leader 副本保持“同步状态”的所有副本的集合。(只有同步的那些副本才叫 ISR),Kafka 会动态维护 ISR 列表,失速或宕机的副本会被踢出 ISR,重新同步后再加入回来。
Kafka 只有在消息被写入 ISR 中的副本后,才认为写入是成功的(取决于 acks=all
和其他配置)。
可以设置最小 ISR 数量,在同步的副本数量少于这个规定的数量后,就不再正常接收请求。
也可以设置副本的最大滞后时间,在这个滞后事件之内的,也容忍为一个同步副本。
什么是 OSR 和 AR
OSR:Out-of-Sync Replicas
AR:Assigned Replicas 所有副本 = ISR + OSR
21. Leader 和 Follower 的职责?如果 Leader 崩溃了,怎么处理?
Kafka 中每个 分区(Partition) 都有一个:
-
Leader 副本(Primary Replica):负责 所有读写操作,Producer 写消息 → 发送给 Leader,Consumer 读取消息 → 从 Leader 拉取,维护当前的最新 offset 状态,将写入的数据 同步给 followers。
-
Follower 副本(Secondary Replicas):被动地 从 Leader 拉取数据(同步副本),保持和 Leader 数据一致(即进入 ISR - In-Sync Replica 集合),不对外提供读写服务(默认情况下)。
如果 Leader 崩溃了,会重新选举一个新的 Leader。
-
Zookeeper / KRaft Controller 检测到 Leader 不可用(通过心跳失联)。
-
从 ISR(In-Sync Replica)列表中挑选一个 follower 副本成为新的 Leader。
-
Controller 更新分区的元数据,将新的 Leader 广播给所有 Broker。
-
生产者和消费者重新感知 Leader 地址,自动连接新 Leader。
22. 如何增加 Kafka 的 partition 数量?会带来什么影响?
修改分区数量:
kafka-topics.sh --bootstrap-server localhost:9092 \
--alter --topic my-topic --partitions 10
这个命令会将 my-topic
的分区数从原有值增加到 10。
会带来更多的吞吐量。但是会触发 Rebalance,带来短暂的消费中断。然后同一个 Key 可能会因为 N 的改变,会落入不同样的分区。
23. Kafka 如何保证高可用性?Zookeeper 的作用是什么?
主要还是靠多副本机制来实现的,配合 KRaft 或者 Zookeeper 的选举。
Zookeeper 在 2.8 之前,负责元数据的管理、Leader 的选举,以及 Broker 的健康检查(心跳机制)。
六、使用与运维场景
24. 你在项目中 Kafka 的使用场景是什么?为什么选择 Kafka?
我们在项目中主要通过 Kafka 实现了系统的异步解耦和高并发处理能力,比如用户浏览事件会发送事件到 Kafka,缓解数据库的压力。
Feed 事件也使用 Kafka 进行异步处理,来将用户相关的消息推送到订阅者的收件箱中。
选择 Kafka,主要是对比了三个主流的消息中间件,发现 kafka 的 github 的 star 数量最多,所以就选了它。此外因为它具备高吞吐、可靠的消息持久化能力,支持横向扩展,适合处理大量用户行为数据。
25. 🌟 Kafka 消费延迟(Lag)是什么?怎么排查和优化?
Kafka 的 Lag 是指:
某个 Consumer Group 尚未处理的消息数量,即:
Lag = 当前 Partition 的最新 Offset(Log End Offset)– 消费者已提交的 Offset
Lag 表示的是“消费者落后了多少消息”,它反映了消费的实时性和处理能力是否跟得上生产速度。
怎么排查?
使用 kafka-consumer-groups.sh
的 describe 方法:
kafka-consumer-groups.sh --bootstrap-server <broker> \
--describe --group <group-id>
可以增加 topic 的分区数优化。
26. 🌟 Kafka 消息积压怎么排查和解决?
排查 Lag 即可,查看落后的消息多不多,落后的非常多就说明消息积压。
Kafka 消息积压(backlog)是生产环境中非常常见的问题,核心是消息生产速度 > 消费处理速度,导致未消费的消息越来越多,Topic 的分区中堆积了大量未被消费者消费的消息。,进而影响系统稳定性。
可以增加 Consumer 实例、增加分区数来解决,如果是单机,可以升级为集群。对于使用了 Key 的情况,要保证 Key 要均匀,不能集中在一个分区。
27. 🌟 Kafka 的重平衡会有哪些问题?怎么优化?
当 Consumer Group 中的成员(Consumer)发生变动(加入、离开、崩溃)时,Kafka 会重新分配分区给消费者的过程,称为 Rebalance。
会带来:
-
消费中断,因为要重新分配分区。
-
消息丢失,可能 Rebalance 之前没有正确提交 Offset。
怎么优化?可以使用稳定分区分配策略比如协作粘性分区,渐进式 Rebalance,几乎无停顿。
具体参考上面的分配策略那。
28. 🌟 Kafka 如何应对数据倾斜?
Kafka 的某些 分区接收到的数据远多于其他分区,导致消费或存储不均衡,从而影响整体吞吐、性能或延迟。
原因可能是:
-
使用了固定的 Key。
-
指定了某个分区。
解决:
-
不使用固定的 Key,轮询发送消息。
-
如果必须指定 key,要保证其分布均匀,例如使用一致性哈希来自定义分区器。
-
增加分区的数量。
29. Kafka 是如何做负载均衡的?
-
生产者负载均衡:使用基于轮询的或者基于 Hash Key 的分区投递策略。
-
Broker 的负载均衡:多分区
-
消费者负载均衡:使用合理的分区分配策略,例如粘性或者协作粘性。
30. Kafka 的典型使用模式有哪些?比如解耦、异步、日志采集等
-
消息队列模式:任务分发、发短信等
-
发布订阅:一个消息可以被多个不同消费者组独立消费,例如订单创建事件被库存服务、通知服务、积分服务等多个系统订阅处理。
-
日志采集:将多个服务节点上的日志收集到 Kafka,再统一落地、分析。
七、对比题
31. Kafka 、RocketMQ 和 RabbitMQ 有什么区别?各自的优劣?适用场景?
Kafka:擅长高吞吐、批量处理、日志流式场景,强在流处理、可持久化、扩展性。
RocketMQ:国产,注重事务性、可靠性、灵活性,适合对消息可靠投递有较高要求的场景。
RabbitMQ:强调灵活的路由、低延迟和协议支持,适合中小型系统和短消息通讯。
32. Kafka 和传统消息队列相比最大的特点是什么?
最大区别在于它不是只为了传递消息,而是把消息当作可持久化的日志来管理,并支持高吞吐、可重放、多消费者并行消费的场景。这使它非常适合做事件流平台或数据管道,而不仅仅是消息队列。
八、项目实战提问
33. Kafka 与数据库、缓存如何集成?保证一致性?
Kafka 与数据库、缓存集成时,要重点解决数据一致性问题。我们可以通过 Kafka 的事务机制保证消费 + offset + 写库的原子性,也可以采用 outbox 模式先写数据库 / 缓存再异步发消息,从而避免数据丢失。