Skip to content

Kafka 面试题(2026-06-18) ​


【面试题】1. Kafka 的 Socket Server 网络模型是怎样的?Acceptor 线程和 Processor 线程怎么分工? ​

【标准答案】Kafka Broker 的网络层用 Reactor 模式,一条连接的生命周期被拆成两部分处理。Acceptor 线程(1 个)只干一件事:监听端口,接收新 TCP 连接,然后把连接轮询分配给 Processor 线程。Processor 线程(默认 3 个,num.network.threads)负责从已建立的连接上读请求、写响应,但自己不处理业务逻辑——它把读到的请求扔到 RequestChannel 的共享队列里,由 RequestHandler 线程(num.io.threads,默认 8)从队列取出做实际处理(写日志、查索引等),处理好再把响应放回队列让 Processor 写回客户端。这样设计的好处是网络 IO 和业务处理解耦——Processor 只负责快速收发字节流,CPU 密集型操作交给 Handler 线程池,避免某条慢请求拖垮整个连接。面试说出来 Acceptor→Processor→Handler 三层流水线,面试官就知道你懂 Kafka 的 IO 模型不是一把梭。

【面试题】2. Kafka 事务消息写入 __transaction_state 内部 Topic 存了什么?Transaction Coordinator 怎么用这些信息做事务提交? ​

【标准答案】__transaction_state 是 Kafka 内部 Topic,专门存事务的元数据和状态日志。每一条消息代表一次事务状态变更。Transaction Coordinator 作为事务管家,负责把事务的全生命周期写进这个 Topic:①事务开始→写一条状态为 ONGOING 的记录,带上 Transactional ID 和 Producer Epoch;②涉及哪些分区的元数据陆续写入;③生产者发 prepare_commit→Coordinator 写一条 PREPARE_COMMIT,然后向各分区写 COMMIT 标记;④最后写 COMPLETE_COMMIT 收尾。如果中途 Coordinator 挂了,新的 Coordinator 启动后直接重放 __transaction_state 日志就能完整恢复所有挂起的事务状态,该 abort 的 abort,该继续 commit 的 commit。面试官追问"类似什么设计"?——这就是 Kafka 版的事务日志,类似数据库的 Redo Log,用以复现状态机的确定性恢复。Transaction Coordinator 本身无状态,状态全在这个内部 Topic 里。

【面试题】3. 消费者 pause() 和 resume() 方法是怎么实现流量控制的?什么场景下比调消费能力更好用? ​

【标准答案】pause() 不是让消费者停止心跳或退出消费组,而是告诉 KafkaConsumer:这个分区的消息暂时别 fetch 了,但心跳照发、连接不断。底层实现很简单:KafkaConsumer 内存里维护一个 pausedPartitions 集合;每次 poll() 循环发送 Fetch 请求前先检查,paused 的分区不包含在请求里,Broker 不受影响,完全客户端侧控制。resume() 只是从集合里移除,下一次 poll() 自动恢复拉取。适用场景:①下游数据库/缓存压力突然飙升,积压未处理完,暂停新增拉取让现有消息先消化掉,比调小 max.poll.records 更精细——可以针对特定分区暂停;②同一个消费组里有快慢消费者,快的暂停等慢的,避免分区重分配时 Consumer 拿太多分工不均。注意:pause 只在当前客户端生效,Rebalance 后新分配给你时是 unpaused 状态,要重新调。

【面试题】4. Kafka 分区重分配(Partition Reassignment)的完整执行流程是什么?replica-movement-throttle 是怎么限制数据迁移速度的? ​

【标准答案】分区重分配的本质是 Leader 选一个新 Broker 作为 Follower,让它追上数据后切换。流程分三步:第一步——管理员生成 JSON 重分配计划(kafka-reassign-partitions.sh),指定每个分区的新副本分布。第二步——Broker 收到计划后对新副本所在 Broker 的 Controller 下令:在目标 Broker 上创建空副本(设置成非 ISR)。第三步——目标 Broker 的 ReplicaFetcher 从 Leader 开始追数据,追上后加入 ISR,此时如果需要还可以触发 Preferred Leader 切换把流量切过来,最后删掉旧副本。replica-movement-throttle 就是在第三步起作用——它限制每个 ReplicaFetcher 的同步带宽(默认不限),防止数据迁移把生产流量挤掉。设成 50MB/s 表示每个 Follower 同步带宽上限 50MB/s。实战经验:大规模重分配一定要设限速,并且分批次执行(比如每次重分配 5% 分区),否则网络跑满全集群抖动;迁移完成记得去掉限速,否则后续正常副本恢复也受限。

【面试题】5. Preferred Leader Election 是什么?auto.leader.rebalance 默认关闭,打开有什么坑? ​

【标准答案】每个分区都有一个 preferred leader——创建分区时分配的第一个副本,标记在 ZK/KRaft 的元数据里。Preferred Leader Election 就是把这位置上的副本重新选回 Leader,目的只有一个:让 Leader 分布重新均衡。因为经过多次宕机恢复后,可能出现一台 Broker 上挂着远超平均的 Leader 数(旧 Leader 恢复后变 Follower 了),流量倾斜到少数 Broker。auto.leader.rebalance.enable=true 时,Controller 每 5 分钟扫一遍集群,如果某 Broker 的 Leader 不平衡率超过 10% 且持续 3 分钟,就自动触发 Preferred Leader Election 迁移。坑就出在"自动"上:①每次 Leader 切换都有短暂不可用(几毫秒到几十毫秒),如果集群本来就抖,再自动切 Leader 会放大问题;②切 Leader 期间生产客户端要更新元数据,大量分区同时切可能让客户端短时间内重试风暴;③无法控制切 Leader 的时间窗口——万一在业务高峰期自动触发就炸了。所以生产环境默认关掉,建议走运维脚本在低峰期手动执行。

【面试题】6. 几万台机器的 Kafka 集群上,操作系统层面该调哪些参数才能把吞吐打满? ​

【标准答案】OS 调优是 Kafka 性能的"最后一公里"。①Page Cache:Kafka 依赖 OS 的页缓存来加速读取,保证系统的 dirty_ratio(dirty_background_ratio 默认 10%)不要设太小——Kafka 写入量大,频繁刷脏页会导致 IO 毛刺。vm.swappiness 设为 1(尽量不用 Swap,Kafka 用堆外内存+PageCache 量大,一旦换到磁盘延迟直接崩)。②磁盘调度:SSD 用 noop/deadline(内核 5.x 后默认 mq-deadline),机械盘用 deadline,核心是降低 IO 等待——Kafka 顺序读写不需要调度器重排序。挂载参数加 noatime,省掉每次读文件的 atime 时间戳更新。③网络:net.core.rmem_max / wmem_max 各设到 16MB,TCP 缓冲区够大才不丢包;net.ipv4.tcp_tw_reuse=1 加快短连接回收(生产者会频繁建连)。④文件描述符:ulimit -n 至少设 10 万(每个分区占至少一个 fd)。⑤最后关掉 Transparent Huge Pages(THP),Kafka 内存分配是零碎的,THP 的延迟碎片整理会随机卡顿几百毫秒。面试说出来 noatime + swap=1,面试官就知道你真正上过生产。

【面试题】7. Kafka 集群需要扩容或升级,滚动重启的正确操作步骤是什么?有哪些必做的前置检查? ​

【标准答案】滚动重启的标准流程:第一步——前置检查,确认 min.insync.replicas ≥ 2(保证一台 Broker 下线不影响写入)、分区 ISR 全满(没有 under-replicated 的分区)、消费者消费无严重 Lag(避免重启后又积压)。第二步——生成一份分区 Leader 分布快照(kafka-metadata.sh --snapshot),重启后做对比。第三步——在低峰期单台 Broker 执行 controlled.shutdown:先调用 kafka-server-stop.sh,它会向 Controller 优雅下线——把所有 Leader 迁移走,副本同步完成后才退出。第四步——升级配置/版本,重启这台 Broker,等 ISR 全部恢复(kafka-topics.sh --describe 确认 under-replicated=0)。第五步——重复以上流程滚动走完所有 Broker。踩过的坑:①每次间隔建议 5-10 分钟给副本追数据的时间,太快会导致大量 under-replicated 分区;②如果有 Preferred Leader 没切走,重启后这台 Broker 的 Leader 还是它自己,流量不会自动均衡——等你全滚完再用 kafka-leader-election.sh 手动做一次全局 Leader 均衡;③中途如果消费者 Rebalance——正常,消费组 Coordinator 在重启的 Broker 上会临时踢掉消费者,组会自己恢复。升级前必须拍 Kafka 数据快照、备份配置文件、准备好回滚脚本,至少留 30 分钟回滚窗口。

【面试题】8. Kafka 的机架感知(Rack Awareness)是怎么让副本跨机架分布的?如果只有两个机架,能不能做到"任意一个机架断电集群还可用"? ​

【标准答案】机架感知原理:每个 Broker 配置 broker.rack 参数(比如 rack-1, rack-2),创建 Topic 时 Kafka 的副本分配算法会在满足"副本不在同一 Broker"的基础上多一层约束——同一个分区的不同副本尽量不在同一个 rack。具体实现:分配算法拿到 Broker 列表后按机架分组,优先让第二个副本落在与 Leader 不同的机架上,第三个副本再跨一层。效果是:如果一个机架断电或交换机坏掉,集群至少还有另一个机架上的副本在 ISR 里,通过 Leader 切换数据不丢、服务不停。但两机架下有个硬伤:如果只有 2 副本且每个机架一个,断电一个后 ISR 里只剩一个副本——如果这个副本还写了数据但没同步到对面的 Follower(对面已经宕机),副本数实际降为 1,可靠性打折扣。实际多机房部署建议:复制因子 ≥ 3、min.insync.replicas ≥ 2,三个副本分布在三个不同机架/可用区,才能真正容忍一整个机架离线。面试说出来"两个机架不够,必须三个副本跨三个可用区",面试官就知道你考虑过边际情况。

【面试题】9. 用 Kafka 自己做死信队列(DLQ),正确的实现方案是什么?消费失败的消息怎么路由、怎么存放、怎么补处理? ​

【标准答案】Kafka 没有内置死信概念,但可以自己建。核心思路:消费失败不抛异常,把消息写到一个独立的死信 Topic,主消费业务继续推进。具体方案:①在业务消费的 catch 块里,构造一条"死信消息",Header 里塞原始 Topic、原始 Partition、原始 Offset、失败时间、异常堆栈、重试次数,Value 保留原始消息体。②用生产者异步发到 dlq.{原Topic} 的 Topic 中(避免 DLQ 写入失败阻塞主消费流)。③如果重试次数 < N(比如 3),就发到 retry Topic 做延迟重试;≥ N 则进真正的 DLQ。④补处理:运维侧写一个"死信回放"工具——读 DLQ 消息里的 Header 拿到原始坐标,人工/自动判断是修 Bug 还是数据问题后,重新写入原 Topic 让消费者再处理一次。跟 RabbitMQ 的关键区别:Kafka 的 DLQ 是纯业务侧逻辑,Broker 完全不参与——这个自主性也是代价,需要你的消费代码有纪律。死信 Topics 必须设 retention 长一点(比如 7 天),防止未处理就过期了。

【面试题】10. Apache Pulsar 主打"计算存储分离",Kafka 主打"一体化架构"——这两种设计各有什么优势和代价?什么情况下应该选择 Pulsar? ​

【标准答案】Kafka 的"一体"是指 Broker 既是计算层也是存储层——分区数据直接存在 Broker 的本地磁盘上,扩缩容需要迁数据、重分配分区。好处是架构简单、延迟极低(毫秒级),因为数据不跨网络走存储节点。代价是:存储量和计算能力绑定,存不动了就得加 Broker(带新磁盘),负载不均时某台磁盘爆满只能手工迁移分区。Pulsar 是计算存储分离:BookKeeper 管存储(Bookie 节点),Broker 只管路由和分发(无状态)。好处是:①存储和计算独立扩展——流量大了加 Broker,数据多了加 Bookie,互不影响;②Broker 无状态,挂了秒恢复;③原生多租户加上分层存储(冷数据自动搬 S3/ODPS),运维灵活。代价:①每一层多一次网络跳转,延迟比 Kafka 高;②BookKeeper 集群本身也有运维成本;③生态成熟度不如 Kafka。选型判断:如果团队规模大、多业务线、需要存 PB 级长期数据和多租户隔离,Pulsar 架构优势明显;如果是中规模、低延迟、流式计算为核心的场景,Kafka 的简单和生态优势是硬通货。别盲目追新——架构再好,团队能运维、周边工具链能接上才算落地。