Kafka消息队列面试考题
05 - Kafka消息队列 · 高频面试考题背诵指南
目录
- Q1:Kafka 的整体架构?核心组件关系?
- Q2:Kafka 为什么这么快?
- Q3:Kafka 消息存储结构
- Q4:ISR、HW、LEO 分别是什么?
- Q5:Kafka 生产者发送消息的完整流程
- Q6:如何保障 Kafka 消息零丢失?
- Q7:Kafka 生产者幂等性原理?如何开启?
- Q8:Kafka 消费者组的工作机制
- Q9:分区分配策略(Range / RoundRobin / Sticky)
- Q10:Rebalance 再平衡的触发条件和如何避免
- Q11:如何保证 Kafka 消息的顺序消费?
- Q12:消费者如何实现幂等性消费?
- Q13:消息积压如何紧急处理?(结合 SRM 项目)
- Q14:Kafka 事务消息原理?Exactly Once 如何保证?
- Q15:死信队列(DLQ)在项目中的应用
一、Kafka 核心概念
Q1:Kafka 的整体架构?核心组件关系?
核心考点:Broker/Topic/Partition/Replica/Producer/Consumer/ZooKeeper/Controller 各自职责。
背诵话术:
Kafka 的核心组件:
- Broker:Kafka 的服务节点,一个 Kafka 集群由多个 Broker 组成。每个 Broker 负责存储和提供一部分分区的数据。
- Topic(主题):消息的逻辑分类,生产者向特定 Topic 发送消息,消费者从 Topic 订阅消息。
- Partition(分区):Topic 的物理分片,每个 Topic 可以有多个 Partition,分布在不同的 Broker 上。Partition 内部消息有序,是 Kafka 并行消费的基础。Kafka 的最大并发度 = Partition 数量。
- Replica(副本):每个 Partition 有多个副本(一个 Leader + 多个 Follower)。Leader 负责所有读写,Follower 异步从 Leader 同步数据。Leader 所在的 Broker 宕机后,从 ISR 中选举新 Leader。
- ISR(In-Sync Replicas):与 Leader 保持同步的副本列表。如果 Follower 长时间未同步(超过
replica.lag.time.max.ms,默认 10s),会被踢出 ISR。- Producer(生产者):向 Kafka 发送消息,可指定 Topic 和 Partition。
- Consumer Group(消费者组):一组消费者的逻辑分组。同一个 Topic 的同一个 Partition,同一时刻只能被同一消费者组内的一个消费者消费,实现负载均衡;不同消费者组之间互相独立,实现广播效果。
- ZooKeeper(Kafka 3.0 前):负责存储 Broker 元数据、Controller 选举、Topic 配置、Consumer Offset 等信息(新版本 Kafka 已用 KRaft 自管理替代 ZooKeeper)。
Q2:Kafka 为什么这么快?
核心考点:五大性能利器:顺序写磁盘、PageCache、零拷贝、批量发送、消息压缩。
背诵话术:
Kafka 在大数据量下依然能保持极高吞吐的核心原因有以下几点:
顺序写磁盘(Sequential Write):Kafka 的消息追加写到日志文件末尾,是顺序 IO 操作。机械硬盘的顺序写速度可达 600MB/s,远超随机写的 100MB/s,SSD 顺序写更快。Kafka 日志文件的设计决定了它的磁盘写性能极高。
PageCache(内存映射):Kafka 不直接将数据写到磁盘文件,而是先写到操作系统的 PageCache(内核缓冲区),由 OS 异步刷盘。消费者读取最新消息时,数据大概率还在 PageCache 中,直接内存读取,无需磁盘 IO。
零拷贝(Zero-Copy):传统的文件传输需要 4 次数据拷贝(磁盘→内核缓冲区→用户空间→Socket缓冲区→网卡)。Kafka 使用 Linux 的
sendfile()系统调用,直接将 PageCache 中的数据发送到网卡,只需 2 次拷贝,避免了数据经过用户空间的来回拷贝,大幅降低 CPU 消耗和延迟。批量发送(Batch):Producer 不是每条消息都立即发送,而是通过
batch.size和linger.ms配置,将多条消息攒成一批再一起发送,减少网络请求次数。消息压缩(Compression):支持 GZIP、Snappy、LZ4、ZSTD 等压缩算法,减小消息体积,降低网络传输带宽消耗。压缩和解压都在 Producer/Consumer 端完成,Broker 只负责存储压缩后的数据。
Q3:Kafka 消息存储结构
核心考点:.log / .index / .timeindex 文件的关系,稀疏索引的查找过程。
背诵话术:
每个 Partition 对应一个目录,目录下按照”日志段(LogSegment)”分割成多组文件:
.log文件:存储实际的消息数据,文件名是该 Segment 中第一条消息的 offset(如00000000000000000000.log)。消息以追加方式写入。.index文件(偏移量索引):记录offset → 文件物理位置的映射关系,是稀疏索引(不是每条消息都建索引,而是每隔一定字节建一个索引项)。文件名与.log相同。.timeindex文件(时间戳索引):记录timestamp → offset的映射,支持按时间戳查找消息。消息查找过程(如查找 offset = 368772 的消息):
- 对所有 LogSegment 的起始 offset 进行二分查找,定位到消息所在的 LogSegment(如起始 offset 为 368769 的 Segment)。
- 在该 Segment 的
.index文件中二分查找,找到不超过目标 offset 的最大索引项,获取对应的物理位置(如 position = 256)。- 从
.log文件的 position 256 开始顺序读取,直到找到 offset = 368772 的消息。
Q4:ISR、HW、LEO 分别是什么?
核心考点:ISR 的动态维护机制,HW 保证消费者只能读到已提交的消息。
背诵话术:
LEO(Log End Offset):每个副本(Leader 或 Follower)当前最新消息的下一个偏移量,代表该副本日志的末端位置。Leader 的 LEO 最大,Follower 会不断追赶 Leader 的 LEO。
HW(High Watermark,高水位):HW = ISR 中所有副本 LEO 的最小值。消费者只能消费到 HW 之前的消息,HW 之后的消息即使已写入 Leader 也不可见(未提交)。这样保证了即使 Leader 宕机,从 ISR 中选出的新 Leader 上也一定有 HW 之前的所有消息,不会丢失已提交的数据。
ISR(In-Sync Replicas):与 Leader 保持同步的副本集合,由 Leader 动态维护。如果某个 Follower 超过
replica.lag.time.max.ms(默认 10s)未向 Leader 发送心跳或同步请求,Leader 会将其从 ISR 中踢出(降为 OSR)。Follower 追上后重新加入 ISR。min.insync.replicas配置了保证消息不丢失所需的最小 ISR 数量。
二、生产者
Q5:Kafka 生产者发送消息的完整流程
核心考点:客户端 Sender 线程、RecordAccumulator 批次累积、网络发送。
背诵话术:
生产者调用
send()方法后,消息不会立即发送,而是经过以下流程:
- 拦截器链(Interceptors):对消息进行预处理(如添加头信息、过滤、监控统计)。
- 序列化器(Serializer):将 Key 和 Value 序列化为字节数组。
- 分区器(Partitioner):决定消息发送到哪个 Partition(默认按 Key 哈希,无 Key 时轮询或粘性分配)。
- RecordAccumulator(消息累加器):消息被放入对应 Broker 的
RecordBatch中,当 Batch 大小达到batch.size(默认 16KB)或等待时间超过linger.ms(默认 0ms,即不等待),Batch 被标记为就绪,等待发送。- Sender 线程:从 RecordAccumulator 中取出就绪的 Batch,通过
NetworkClient建立 TCP 连接,将 Batch 发送给对应的 Leader Broker。- ACK 确认:根据
acks配置等待 Broker 的确认响应,失败时按retries配置进行重试。
Q6:如何保障 Kafka 消息零丢失?
核心考点:三端闭环:Producer(acks=all + 幂等)+ Broker(min.insync.replicas≥2)+ Consumer(手动提交)。
背诵话术:
在招行调度中台和金融结算场景中,消息零丢失是底线,需要从生产者、Broker、消费者三端同时配置:
Producer 端:
acks = all(或-1):消息必须被 Leader + ISR 中所有 Follower 都确认写入,才算发送成功,避免 Leader 宕机时 Follower 没有数据的情况。retries = Integer.MAX_VALUE:网络抖动等临时故障自动重试。enable.idempotence = true:开启幂等性,防止重试导致消息重复写入(详见 Q8)。Broker 端:
min.insync.replicas >= 2:要求 ISR 中至少有 2 个副本(含 Leader)才能确认消息,结合acks=all使用,确保至少有 1 个 Follower 也写入了数据,即使 Leader 宕机也不丢失。- 设置
unclean.leader.election.enable = false:禁止落后的 Follower(不在 ISR 中)参与 Leader 选举,防止选举出的新 Leader 数据不全。Consumer 端:
enable.auto.commit = false:关闭自动提交 offset。- 必须在业务逻辑成功处理完消息并完成数据库持久化后,才手动提交 offset(调用
consumer.commitSync()或commitAsync())。如果消费过程中抛出异常,不提交 offset,消息会被重新消费,配合幂等消费防止业务重复处理。
Q7:Kafka 生产者幂等性原理?如何开启?
核心考点:ProducerID + Sequence Number 在 Broker 端去重,防止 retry 导致重复消息。
背诵话术:
问题背景:
acks=all+retries配置下,如果 Broker 已成功写入消息但 ACK 响应在网络中丢失,Producer 会认为发送失败而重试,导致 Broker 收到重复的消息。幂等性原理:开启幂等性后(
enable.idempotence = true,会同时强制设置acks=all、retries=MAX),Kafka 给每个 Producer 分配一个全局唯一的 PID(Producer ID),并为每条发往同一 Partition 的消息分配一个单调递增的 Sequence Number(序列号)。Broker 端维护了每个
(PID, Partition)组合的最大已接收 Sequence Number。收到消息时:
- 如果收到的 Sequence Number = 已记录的最大值 + 1:正常写入。
- 如果已接收过(Sequence Number ≤ 最大值):识别为重复消息,丢弃,返回成功 ACK。这样 Producer 重试时,Broker 会自动去重。
局限性:幂等性只保证单个 Producer 会话内、单个 Partition 内的消息不重复,跨 Partition 或 Producer 重启后(PID 会改变),幂等性不再有效。跨 Partition 的幂等需要 Kafka 事务来保证。
三、消费者
Q8:Kafka 消费者组的工作机制
核心考点:分区与消费者的对应关系,并行度上限等于分区数。
背诵话术:
Kafka 的消费者组(Consumer Group)遵循以下核心规则:
- 同一 Partition,同一时刻只能被同一 Consumer Group 中的一个 Consumer 消费。这保证了组内消费者对该分区的有序消费,避免了重复处理。
- 不同 Consumer Group 之间完全独立,同一 Topic 的消息会分别广播给每个订阅的消费者组,实现”广播消费”效果(如日志数据同时被监控系统和数据分析系统各自独立消费)。
并行度:消费者组内的并行消费能力取决于 Partition 数量,增加消费者数量不能超过 Partition 数(超出的消费者会空闲)。因此,分区数是 Kafka 消费并行度的天花板,如果发现消费积压,在增加消费者之前,必须先确认 Partition 数量是否足够。
Q9:分区分配策略(Range / RoundRobin / Sticky)
核心考点:三种策略的分配逻辑,Sticky 减少 Rebalance 时的数据移动。
背诵话术:
Range(默认):按 Topic 维度,将该 Topic 的 Partition 连续分段分配给消费者组中的消费者。如果 Partition 数不整除消费者数,靠前的消费者会多分配一个 Partition。多个 Topic 时,每个 Topic 独立分配,可能导致靠前的消费者总是多分一个,产生负载不均。
RoundRobin:将所有 Topic 的所有 Partition 按字典序排好,然后轮询分配给每个消费者。负载更均匀,但 Rebalance 时所有分配关系都要重算,迁移量大。
Sticky(推荐):初始分配类似 RoundRobin,但在 Rebalance 时尽量保持现有的分配关系不变,只将离开的消费者所持有的 Partition 重新分配给其他消费者。减少了不必要的 Partition 迁移,降低了 Rebalance 对消费的影响。生产中推荐使用 Sticky,特别是消费者频繁上下线时。
Q10:Rebalance 再平衡的触发条件和如何避免
核心考点:Rebalance 期间消费者暂停消费,触发条件,避免频繁触发的配置手段。
背诵话术:
Rebalance 的触发条件:
- 消费者组内有消费者加入或离开(扩容/缩容/消费者崩溃)。
- 消费者组订阅的 Topic 分区数变更(手动增加 Partition)。
- 消费者心跳超时(消费者未在
session.timeout.ms内发送心跳,被 GroupCoordinator 认为已死亡)。Rebalance 的代价:Rebalance 期间,所有消费者必须停止消费,等待新的分配方案执行完毕,可能造成几秒到几十秒的消息消费中断,影响消费延迟。
避免频繁 Rebalance 的手段:
- 调大
session.timeout.ms(默认 45s)和heartbeat.interval.ms(默认 3s),防止因网络抖动导致的误判心跳超时。- 调大
max.poll.interval.ms(默认 5 分钟),避免消费者处理单批消息耗时过长导致被踢出组(核心!)。如果业务逻辑处理时间可能超过 5 分钟,必须调大这个参数。- 每次
poll()拉取的消息数量(max.poll.records)不要设太大,避免处理时间超时。- 使用
Sticky分区策略减少 Rebalance 的迁移成本。
Q11:如何保证 Kafka 消息的顺序消费?
核心考点:同一 Key → 同一 Partition → 单线程消费,三层保证。
背诵话术:
Kafka 保证同一 Partition 内消息的有序性,但不保证跨 Partition 的全局有序。因此要保证消息顺序消费,需要三层配合:
Producer 侧(相同业务数据发同一 Partition):为相关联的消息设置相同的
Key(如订单 ID、用户 ID),Kafka 的分区器会通过hash(key) % partitionNum确保相同 Key 的消息始终路由到同一个 Partition,从而保证这些消息的发送顺序。Partition 侧:Partition 内消息严格有序,只有一个消费者负责消费(Consumer Group 机制保证)。
Consumer 侧(单线程消费该 Partition):消费者对拿到的消息必须单线程按序处理,不能将消息分发到多线程并行处理(否则处理完成顺序无法保证)。如果确实需要提高消费速度,可以按 Key 进一步分组后再交给不同的线程处理(相同 Key 的消息始终在同一线程内串行),保证单个业务实体的消息有序。
注意:开启幂等性后(
enable.idempotence = true),生产者重试消息的顺序也有保障(Kafka 通过max.in.flight.requests.per.connection控制)。如需严格有序且不重复,需结合幂等性 + 顺序 Key + 单线程消费。
Q12:消费者如何实现幂等性消费?
核心考点:消息至少消费一次(At Least Once),业务侧的幂等性防重设计。
背诵话术:
Kafka 默认语义是 At Least Once(至少一次):Consumer 消费消息后,在提交 offset 之前服务重启,则该批消息会被重新消费。因此,消费者端的幂等性设计至关重要。
常见幂等性方案:
数据库唯一索引:消费消息后,在数据库插入记录时,以消息中的唯一业务 ID(如订单号、流水号)作为唯一索引,重复消费时数据库会抛出唯一键冲突异常,捕获该异常并忽略,保证数据不重复插入。这是最简单可靠的方案,在招行结算和 SRM 采购中均使用。
Redis SETNX 防重:消费消息前,以消息的唯一 ID 为 Key,执行
SET msg_id 1 NX EX 7d(7 天过期),若返回成功则继续处理;若返回失败说明已处理过,直接跳过。适合对数据库写入压力敏感的场景。状态机检查:根据业务状态判断,如订单只能从”待支付”流转到”已支付”,重复消费”已支付”消息时,发现订单已是”已支付”状态,直接幂等返回成功,不做重复处理。
Token 令牌机制:消息中携带一个唯一 token,处理前先从 Redis 中删除该 token(
DEL token_key),删除成功则处理,失败则说明已被处理(token 已不存在),幂等返回。
Q13:消息积压如何紧急处理?(结合 SRM 项目)
核心考点:先定位瓶颈,再扩容消费者或扩分区,临时中转是最后手段。
背诵话术:
在 SRM 采购平台,供应商活动期间可能出现询价消息大量积压的情况。我的紧急处理流程分三步:
第一步:定位积压根因:通过 Kafka 监控(Lag 指标)确认积压的 Topic 和 Consumer Group,排查是消费异常(抛错导致重试卡死)、下游处理太慢(比价引擎计算耗时),还是数据库 IO 瓶颈导致消费速度跟不上生产速度。
第二步:扩容消费者(最快的手段):
- Partition 数量充足(如有 10 个分区):立即对消费者服务进行水平扩容,将实例数增加到与分区数持平(最多 10 个消费者),让每个分区都有消费者并行处理。
- Partition 数量不足(如只有 2 个分区):增加消费者实例无用(多出来的实例会空闲)。需要先扩 Partition,但在线扩 Partition 可能影响已有的 Key 路由(hash 值会变化)。此时最稳妥的方案是:启动一个临时中转服务,专门负责快速消费旧 Topic 的消息并立即 ACK,将消息转投到一个新建的、有足够 Partition 数(如 20 个)的临时 Topic,再启动 20 个消费者实例消费临时 Topic。
第三步:临时降级处理:关闭消费端一些非关键的业务逻辑(如积分回调、日志埋点),只保留核心的询价处理,释放 CPU 和 IO 资源,加快消费速度,待积压消费完后再恢复。
四、Kafka 高级特性
Q14:Kafka 事务消息原理?Exactly Once 如何保证?
核心考点:TransactionCoordinator + 两阶段提交,幂等性 + 事务 = Exactly Once。
背诵话术:
Kafka 的 Exactly Once(精确一次)语义 = 幂等性(单 Partition 不重复)+ 事务(跨 Partition/跨 Topic 的原子性)。
Kafka 事务的两阶段提交:
- Prepare 阶段:Producer 向特定 Broker(
TransactionCoordinator)申请一个事务 ID(transactional.id),并开始一个事务(beginTransaction())。随后向一个或多个 Partition 发送消息,这些消息写入 Broker 日志,但对消费者不可见(被标记为未提交状态)。- Commit / Abort 阶段:Producer 调用
commitTransaction()后,TransactionCoordinator 向所有参与的 Partition 写入一个事务提交标记(Transaction Marker),只有当消费者读到这个标记时,才能消费该事务中的消息。如果调用abortTransaction(),写入回滚标记,消费者忽略这批消息。读已提交消费:消费者需设置
isolation.level = read_committed,只读取已提交的事务消息,未提交的消息对该消费者不可见,从而保证消费端不会读到”中间态”数据。
Q15:死信队列(DLQ)在项目中的应用
核心考点:消费失败消息的最终归宿,避免无限重试阻塞正常消费。
背诵话术:
死信队列(Dead Letter Queue, DLQ) 用于处理无法被正常消费的消息,防止它们因无限重试而阻塞整个消费进程。
标准处理流程:
- Consumer 消费某条消息失败(如下游服务不可用、数据格式错误)。
- 进行有限次数的本地重试(如 3 次)。
- 如果重试依然失败,将该消息连同错误原因、重试次数等元信息,转投到一个专门的死信 Topic(如
original_topic_name.DLQ)。- 消费者正常提交 offset,继续消费后续消息,不会被单条失败消息阻塞。
- 死信 Topic 由专门的死信处理服务监听,进行告警通知(钉钉/邮件)和人工排查。
- 问题修复后,可以从死信 Topic 重放消息进行补偿处理。
在招行调度中台的应用:核心金融流程调度消息如果消费失败,会经过 3 次重试后进入死信 Topic,触发高优先级告警,运维人员在 10 分钟内介入处理,确保金融流程不会因消息处理失败而长时间卡顿。








