Kafka
阅读提示
这页题量很大,建议先吃透 4 条主线:Kafka 是什么 -> 为什么快 -> 可靠性怎么保 -> 顺序与幂等如何落地。
面试回答时先给结论,再给关键参数(如 acks、ISR、offset),最后补一个业务场景。
本文以 Apache Kafka 4.0.x 与官方 Java 客户端为基准;消费者分配讨论普通 consumer group(subscribe),不涵盖 Share Group。历史版本差异另行注明。
推荐训练法
- 第一遍只看问题标题,先建立知识地图。
- 第二遍优先掌握“可靠性、顺序性、重复消费”三块高频追问。
- 第三遍用 2 分钟口述 1 题,训练可表达性。
什么是消息中间件?(了解)
- 消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。
- 消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
Kafka是什么?有什么作用?(了解)
- Kafka 是一个分布式的流式处理平台,它以高吞吐、可持久化、可水平扩展、支持流数据处理等多种特性而被广泛使用

主要功能体现于三点:
消息系统:kafka与传统的消息中间件都具备系统解耦、冗余存储、流量削峰、缓冲、异步通信、扩展性、可恢复性等功能。与此同时,kafka还提供了大多数消息系统难以实现的消息顺序性保障及回溯性消费的功能。
存储系统:kafka把消息持久化到磁盘,相比于其他基于内存存储的系统而言,有效的降低了消息丢失的风险。这得益于其消息持久化和多副本机制。也可以将kafka作为长期的存储系统来使用,需要按容量配置保留策略;日志压缩只保留每个 key 的最新状态(删除标记也有保留期限),不等于永久保留所有历史事件。
流式处理平台:kafka为流行的流式处理框架提供了可靠的数据来源,还提供了一个完整的流式处理框架,比如窗口、连接、变换和聚合等各类操作。
Kafka中有哪几个组件?
- 主题:Kafka主题是一堆或一组消息。
- 生产者:在Kafka,生产者发布通信以及向Kafka主题发布消息。
- 消费者:Kafka消费者订阅了一个主题,并且还从主题中读取和处理消息。
- 经纪人:在管理主题中的消息存储时,我们使用Kafka Brokers。
Kafka Replicas是怎么管理的?(了解)

- AR:分区中的所有 Replica 统称为 AR
- ISR:所有与 Leader 副本保持一定程度同步的Replica(包括 Leader 副本在内)组成 ISR
- OSR:与 Leader 副本同步滞后过多的 Replica 组成了 OSR
- Leader 负责维护和跟踪 ISR 集合中所有 Follower 副本的滞后状态,当 Follower 副本落后过多时,就会将其放入 OSR 集合,当 Follower 副本追上了 Leader 的进度时,就会将其放入 ISR 集合。
- 默认情况下,只有 ISR 中的副本才有资格晋升为 Leader。
kafka的消费者是pull(拉)还是push(推)模式,这种模式有什么好处?
- Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从broker 拉取消息。
- 优点:pull模式消费者自主决定是否批量从broker拉取数据,而push模式在无法知道消费者消费能力情况下,不易控制推送速度,太快可能造成消费者奔溃,太慢又可能造成浪费。
- 缺点:如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到到达。为了避免这点,broker 长轮询由
fetch.min.bytes指定期望最小字节数,fetch.max.wait.ms限制等待时间;二者与应用poll(Duration)的等待上限不同。
kafka维护消息状态的跟踪方法(了解)
- 在同一个消费者组内,同一分区在稳定分配时只交给一个消费者;不同组可以独立消费同一分区。已提交 offset 代表下一条待消费位置,区别于客户端已拉取的位置。
- offset 不等于业务完成凭证。先提交再处理,崩溃后可能跳过未处理消息;先处理再提交,二者之间崩溃会重放。位移跟踪不会自动消除丢失或重复。
kafka判断一个节点还活着的有那两个条件?
- 存活与同步资格是两件事。旧 ZooKeeper 模式依赖 broker 的 ZooKeeper 会话;Kafka 4.0 已移除该模式。KRaft 中 broker 向活动 controller 发送心跳,超时会被围栏(fence)。
- follower 是否留在 ISR 还取决于复制进度,未在
replica.lag.time.max.ms允许时间内追上 leader 会被移出 ISR。进程存活不代表能承担干净的 leader 切换。
参见官方 KRaft 与 ZooKeeper 差异。
讲一讲 kafka 的 ack 的三种机制
Java 客户端使用 acks(request.required.acks 是旧客户端名称),4.0 默认 all:
0:不等待 broker 响应,无法确认是否收到记录,客户端通常也无法据此重试。1:leader 写本地日志后响应,不等 follower 复制;此时 leader 故障可能丢失已确认记录。all(-1):等待当前 ISR 确认,不是等待全部配置副本。配合min.insync.replicas约束最少 ISR 数量,不足则拒绝写入,不能宣称绝不丢失。
参见官方 acks 与幂等配置。
kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?
- Kafka 中发送 1 条消息的时候,可以指定(topic, partition, key) 3 个参数,partiton 和 key 是可选的。
- Kafka 分布式的单位是 partition,同一个 partition 用一个 write ahead log 组织,所以可以保证FIFO 的顺序。不同 partition 之间不能保证顺序。因此你可以指定 partition,将相应的消息发往同 1个 partition,并且在消费端,同组稳定分配时,1 个 partition 只交给 1 个 consumer,就可以实现这些消息的顺序消费。
- 另外,你也可以指定 key(比如 order id),具有同 1 个 key 的所有消息,会发往同 1 个partition,分区数与策略不变时,同 key 可保持分区内日志顺序;消费端还须按分区串行处理。关闭幂等且有多个在途请求时,生产重试可能乱序。
kafka 如何不消费重复数据?比如扣款,我们不能重复的扣。
- 幂等生产者:producer ID、epoch 和分区序列号避免同一生产会话重试导致重复写入;应用再次
send同一业务事件仍可产生两条记录。4.0 无冲突配置时默认启用,要求acks=all、retries>0、max.in.flight.requests.per.connection<=5。 - 事务:Kafka 可原子提交输出记录与消费位移,消费端使用
isolation.level=read_committed隔离未提交和已中止的事务记录。这适用于 Kafka 到 Kafka 的处理,不能直接将外部数据库扣款纳入 Kafka 事务。 - 业务幂等:用事件 ID 建唯一约束,将“写去重记录”和“更新余额”放在同一数据库事务中,再提交 Kafka 位移;重放时约束阻止再次扣款。调用外部支付接口还需对方支持幂等键。
- 手动提交 offset:处理完成再提交仍有“业务成功、提交前崩溃”的重放窗口;提交失败也需处理,手动提交本身不去重。
poll(offset=42) -> 数据库事务:去重记录 + 扣款 -> commit offset=43
|
提交前崩溃:重放 42
唯一约束阻止重复扣款partition的数据文件(offffset,MessageSize,data)(了解)
- 标题中的
(offset, MessageSize, data)只能作为旧格式的简化记忆,不是 Kafka 4.0 的完整布局。自 0.11 起的 magic=2 格式以 RecordBatch 为单位,含 baseOffset、batchLength、CRC、属性、时间戳、producer ID/epoch、序列号等;批内 Record 使用 offset delta,还可包含 key、value、headers。 - offset 是分区内逻辑位置,不是文件字节地址;日志压缩清理后可能有空洞,不能保证指定 offset 仍有记录。batchLength 也不是单条消息体大小。
- 分区日志分成多个 segment:
.log存数据,.index是 offset 到文件位置的稀疏索引,.timeindex辅助按时间查找。先按 base offset 定位 segment,再查索引并顺序扫描日志。
kafka如何实现数据的高效读取?(顺序读写、分段命令、二分查找)
Kafka实现数据的高效读取主要依赖于其独特的设计,包括顺序读写、分段存储和二分查找等机制。具体来说:
顺序读写:Kafka采用顺序写入的方式,新消息会被追加到已有消息的末尾。这种写入模式减少了磁盘I/O操作的随机访问,大大提高了写入效率。同样,读取时也是顺序读取,这与磁盘的物理特性相契合,进一步提升了读取性能。
分段存储:Kafka将每个partition划分为多个segment,每个 segment 包含日志与索引文件,按大小或时间等条件滚动,实际大小不固定。这样设计避免了管理大文件的复杂性,同时方便了对旧数据的清理。每个segment文件独立存储,使得数据管理和维护更加高效。
二分查找:Kafka为每个segment维护了一个索引文件,其中存储了消息的offset和物理位置的映射关系。当需要查找特定offset的消息时,Kafka会利用二分查找算法先定位 segment,再在稀疏索引中找不大于目标 offset 的最近位置,随后顺序扫描日志,从而实现了高效的数据访问。
此外,Kafka还通过内存缓存、消息压缩等技术手段进一步提升了数据的读取效率。内存缓存使得Kafka可以将索引文件的部分内容保留在内存中,加速查找操作;而消息压缩则减少了磁盘和网络带宽的使用,使得数据在传输和存储时更加高效。
Kafka 消费者端的 Rebalance 操作什么时候发生?(了解)
在Kafka中,消费者端的Rebalance操作主要发生在以下几种情况,以确保消费者组能够公平且高效地分配订阅主题的分区:
消费者组成员变化:当有新消费者加入消费者组或现有消费者离开(无论是正常关闭、崩溃还是长时间未发送心跳被判定为“死亡”)时,会触发Rebalance。这是因为消费者组的成员变化可能导致分区分配不再均衡。
订阅主题分区数变化:如果消费者组订阅的任一主题的分区数发生变化(如分区增加;Kafka 不支持直接减少已有主题的分区数),也会触发Rebalance。这是为了根据新的分区情况重新分配消费者与分区之间的关系。
消费者组订阅变化:当消费者组订阅的主题列表发生变化,比如消费者取消了对某些主题的订阅,同样可能引发Rebalance。
协调者故障或变化:负责管理消费者组的协调者(Coordinator)如果发生故障,或者消费者组被重新分配给了另一个协调者,不必然触发分区重分配,状态恢复时可继续原分配。
Rebalance过程是一个协调消费者组内成员重新分配分区的过程,它确保了消费者组能够动态地适应变化,保持高效的消息处理能力。然而,频繁的Rebalance可能会对性能产生负面影响,因此在设计和使用Kafka时,需要合理设置相关参数和配置,以减少不必要的Rebalance操作。
4.0 的 group.protocol 默认是 classic;选择 consumer 协议后使用新的增量协调机制,不能将所有再均衡都理解成全组停顿。经典协议使用 session.timeout.ms、heartbeat.interval.ms,新协议的心跳与会话超时由服务端相关参数控制。处理过慢超过 max.poll.interval.ms 也会影响组成员资格。手动 assign 不参与这种自动分区分配。参见官方 消费者配置。
Kafka的那些设计让它有如此高的性能?
- 顺序写入与零拷贝技术:与传统的数据库系统不同,Kafka采用顺序写入的方式,这大大减少了磁盘寻道时间,提高了写入性能。同时,在适用的明文传输路径上可利用零拷贝减少数据搬运;TLS 等路径不能一概套用 sendfile 的零拷贝结论。
- 分区与副本机制:Kafka将数据分散到多个分区中,每个分区都可以独立处理数据写入和读取请求,充分利用了集群的计算和存储能力。同时,每个分区都有多个副本,既保证了数据的可靠性,复制由 follower 拉取推进,但
acks=all仍等待 ISR;副本增加网络与存储开销。 - 批量操作与压缩:Kafka支持批量写入和压缩功能,批量写入减少了网络传输和磁盘IO的次数,而压缩功能则降低了磁盘存储空间的使用,并减少了数据传输的开销。
- 高效的消费者组机制:消费者组允许多个消费者并行消费不同分区;同组同一分区在稳定分配时只交给一个消费者,提高了并发处理能力。同时,Kafka支持多种消费模式,如发布订阅模式和队列模式,以适应不同的业务需求。
- 可扩展的架构:Kafka的分布式架构可以轻松通过添加更多的节点来扩展处理能力。这种水平扩展的能力使得Kafka能够应对大规模数据处理和实时流处理场景。
Kafka为什么不支持读写分离?
问题的前提已过时:Kafka 自 2.4 起支持 follower fetching。默认仍读 leader;配置 broker 的 replica.selector.class=org.apache.kafka.common.replica.RackAwareReplicaSelector、各 broker 的 broker.rack 和消费者 client.rack 后,可优先选择合适的就近副本。
生产者仍向 leader 写入;follower 读取受已提交数据边界限制,不是任意读取滞后副本尾部。就近读取主要减少跨机架/可用区流量,副本滞后仍可能增加读取等待。
参见官方 KIP-392。
分区Leader选举策略有几种?
这里指数据分区 leader,区别于 KRaft controller quorum 选主;不是按 offset 最大竞选,也没有供业务任意实现选举算法的普通客户端插件。
- 干净选举:从可用 ISR 中选择替代者;没有合格副本时通常等待恢复,不会自动退到 OSR。
- 首选副本选举:分配列表第一个副本是 preferred replica;它在 ISR 中且可用时,可通过管理操作或自动 leader 均衡迁回。
- 非干净选举:显式允许
unclean.leader.election.enable后,无可用 ISR 时可使用落后副本恢复服务,可能丢失已提交数据。4.0 默认关闭。
参见官方 Broker 配置。
请简述下你在哪些场景下会选择 Kafka?
- 实时数据流处理:Kafka擅长处理大规模、高吞吐量的实时数据流。当需要实时收集、处理和分析大量数据时,比如日志聚合、监控数据、用户行为追踪等,Kafka是一个理想的选择。
- 微服务间通信:在微服务架构中,服务间需要高效、可靠的通信机制。Kafka作为消息中间件,可以提供解耦、异步和可靠的通信方式,帮助微服务实现松耦合和高内聚。
- 事件驱动架构:Kafka支持事件驱动架构,允许系统根据事件进行响应和处理。这对于需要实时处理事件、触发后续操作或进行复杂工作流协调的应用场景非常有用。
- 日志和数据湖:Kafka可以作为日志和数据湖的存储引擎,为大数据分析、机器学习等提供数据源。它的顺序存储和高效读取特性使得处理大规模数据变得更加容易和高效。
- 分布式系统的消息总线:在需要构建分布式系统的消息总线时,Kafka可以提供高可用、可扩展和容错的消息传递服务,确保系统组件之间的通信畅通无阻。
请谈一谈 Kafka 数据一致性原理
- 副本复制:leader 接收写入,follower 拉取日志;普通消费者默认读 leader,也可按前述配置读取 follower。
- 提交边界:高水位(HW)反映复制提交进度,普通消费者只读高水位之前的记录。ISR 用于复制确认和干净选举,OSR 仍会复制日志追赶。
read_committed还受最后稳定偏移量(LSO)限制,并跳过已中止事务记录。 - 幂等与事务:生产重试去重、Kafka 内原子提交与外部业务幂等解决不同问题,不能笼统称为所有系统之间精确一次传递。可靠性还取决于副本数、确认策略和故障范围。
生产者发送消息有哪些模式?
- 发后即忘(fire-and-forget):它只管往 Kafka 里面发送消息,但是不关心消息是否正确到达,这种方式的效率最高,但是可靠性也最差,比如当发生某些不可重试异常的时候会造成消息的丢失
- 同步(sync):producer.send()返回一个Future对象,调用get()方法变回进行同步等待,就知道消息是否发送成功,逐条
send(...).get()才会串行等待,也可先发送多条再等待各 Future - 异步(async):Kafka支持 producer.send() 传入一个回调函数,消息不管成功或者失败都会调用这个回调函数,这样就算是异步发送,我们也知道消息的发送情况,然后再回调函数中选择记录日志还是重试都取决于调用方
发送消息的分区策略有哪些?

- 1.轮询:依次将消息发送该topic下的所有分区,可显式选择 RoundRobinPartitioner;4.0 默认无 key 时采用粘性分区聚合批次,并可自适应偏向较快 broker,不等同于逐条轮询。
- 2.key 指定分区:在创建消息是 key 不为空,并且使用默认分区器,Kafka 会将 key 进行 hash,然后根据hash值映射到指定的分区上。这样的好处是 key 相同的消息会在一个分区下,Kafka 并不能保证全局有序,但是在每个分区下的消息是有序的,按照顺序存储,按照顺序消费。在保证同一个 key 的消息是有序的,这样基本能满足消息的顺序性的需求。但是如果 partation 数量发生变化,那就很难保证 key 与分区之间的映射关系了。
- 3.自定义策略:实现 Partitioner 接口就能自定义分区策略。
- 4.指定 Partiton 发送
参见官方 分区器配置。
Kafka 支持读写分离吗?为什么?(了解)
支持,具体条件见上文“Kafka为什么不支持读写分离?”:2.4+ 的 follower fetching 需要副本选择器和机架配置配合,默认仍读 leader。生产者继续写 leader,follower 仍需复制数据。跨机架流量与副本滞后是主要权衡因素。
那 Kafka 是怎么去实现负载均衡的?(了解)
- Kafka 的负责均衡主要是通过分区来实现的,我们知道 Kafka 是主写主读的架构,如下图:

- 共三个 broker ,里面各有三个副本,总共有三个 partation, 深色的是 leader,浅色的是 follower,上下灰色分别代表生产者和消费者,虚线代表 follower 从 leader 拉取消息。
- 我们从这张图就可以很明显的看出来,每个 broker 都有消费者拉取消息,每个 broker 也都有生产者发送消息,图中 leader 数量相同,但分区流量和消息大小不同,实际负载未必一样,这也说明了 kafka 独特的架构方式可以通过主写主读来实现负载均衡。
Kafka 的可靠性是怎么保证的?

1.acks:这个参数用来指定分区中有多少个副本收到这条消息,生产者才认为这条消息是写入成功的,这个参数有三个值:
1.acks = 1。Java 客户端自 3.0 起默认 all,4.0 也如此。生产者发送消息,只要 leader 副本成功写入消息,就代表成功。这种方案的问题在于,当返回成功后,如果 leader 副本和 follower 副本还没有来得及同步,leader 就崩溃了,那么在选举后新的 leader 就没有这条消息,也就丢失了。
2.acks = 0。生产者发送消息后直接算写入成功,不需要等待响应。这个方案的问题很明显,只要服务端写消息时出现任何问题,都会导致消息丢失。
3.acks = -1 或 acks = all。生产者发送消息后,需要等待 ISR 中的所有副本都成功写入消息后才能收到服务端的响应。毫无疑问这种方案的可靠性是最高的,但是如果 ISR 中只有leader 副本,那么就和 acks = 1 毫无差别了。
2.消息发送的方式:我们可以通过同步或者异步获取响应结果,失败做重试来保证消息的可靠性。
3.手动提交位移:
enable.auto.commit=true时,客户端按auto.commit.interval.ms周期提交 poll 返回记录对应的位置,不感知业务是否完成。但是如果消费者消费出错,没有进入真正的业务处理,那么就可能会导致这条消息消费失败,从而丢失。我们可以开启手动提交位移,等待业务正常处理完成后,再提交offset。
确认与可用性的关系:副本数为 3,设置 acks=all、min.insync.replicas=2,关闭非干净选举时:
ISR={A,B,C} -> C 落后被移出 -> ISR={A,B}:A、B 复制后可确认
ISR={A} -> 不足 min ISR=2:拒绝写入,等待副本恢复min ISR 是写入门槛,不是只等待其中两个副本:ISR 为三个时 all 仍等待当前 ISR。确认也不等于每个副本已同步刷盘。超时不能证明记录未写入,应利用幂等重试并检查最终结果。
参见官方 min.insync.replicas。上述为机制示意,未运行集群故障实验。
分区数越多越好吗?吞吐量就会越高吗?(了解)
- 增加分区可提高并行度,但吞吐量不必然与分区数成正比,还受磁盘、网络、批次和业务处理能力限制
客户端/服务器端需要使用的内存就越多(超过了一定限度,就会对性能造成影响)
- 服务端在很多组件中都维护了分区级别的缓存,分区数越大,缓存成本也就越大。
- 同组有效消费并行度受分区数限制,但不会自动创建线程,一个消费者也可处理多个分区,线程的开销成本也就越大
- 生产者发送消息有缓存的概念,会为每个分区缓存消息,当积累到一定程度或者时间时会将消息发送到分区,分区越多,这部分的缓存也就越大
什么情况下 kafka 会丢失消息?
Kafka 有三次消息传递的过程:生产者发消息给 Broker,Broker 同步消息和持久化消息,Broker 将消息传递给消费者。
1.生产者发送数据:
当 acks 为 0,只要服务端写消息时出现任何问题,都会导致消息丢失。
当 acks 配置为 1 时,生产者发送消息,只要 leader 副本成功写入消息,就代表成功。这种方案的问题在于,当返回成功后,如果 leader 副本和 follower 副本还没有来得及同步,leader 就崩溃了,那么在选举后新的 leader 就没有这条消息,也就丢失了。
2.Broker 存储数据:kafka 通过 Page Cache 将数据写入磁盘。
Page Cache 就是当往磁盘文件写入的时候,系统会先将数据流写入缓存中,但是什么时候将缓存的数据写入文件中是由操作系统自行决定。单副本整机断电可能丢失尚未刷盘的数据;其他存活同步副本可用于恢复,不能将单节点断电等同于集群必然丢失消息。多副本同时失效仍可能丢失已确认数据。
3.消费者消费数据:在开启自动提交 offset 时,客户端周期提交已拉取记录对应的位置,并不等待业务处理完成,如果业务还没有来得及处理,那么消息就会丢失。
