Skip to content

Kafka 面试题(2026-06-18 23:54) ​

考点:消费者拦截器 | 生产者内存管理 | 压缩算法选型 | Follower拉取机制 | 脏选举 | 日志压缩Cleaner | KRaft架构 | 延迟消息对比 | Exactly-Once | 磁盘故障排查


【面试题】1. Kafka 的消费者拦截器(ConsumerInterceptor)能干哪些事?和生产者拦截器有什么不同? ​

【标准答案】消费者拦截器是消费链路的切面,主要用于:①在 poll() 返回消息前做统一处理——消息脱敏、字段过滤、自动补充上下文;②onCommit() 里拿到位移提交结果做监控埋点;③按比例采样日志记录消费轨迹做审计。和生产者拦截器最大区别:onConsume() 拿到的已经是反序列化后的 ConsumerRecord,可以直接操作业务对象,而生产者 onSend() 拿到的是原始 ProducerRecord。另外消费者多了 onCommit() 回调。实战场景:多团队共用消费框架时,写一个"自动注入 traceId + 脱敏手机号"的 ConsumerInterceptor,业务方代码完全无感。注意:拦截器抛异常会吞掉整批 poll 结果——生产环境里 try-catch 必须全覆盖。


【面试题】2. 生产者的 buffer.memory 满了之后会发生什么?max.block.ms 在这个过程里起什么作用? ​

【标准答案】buffer.memory(默认 32MB)是生产者攒批的总缓冲区,当所有分区发送速度赶不上消息进入速度时就会满。满了之后:第一阶段——send() 线程阻塞等待,最长等 max.block.ms 毫秒(默认 60s),期间如果腾出空间就继续写入。第二阶段——超时还没空间,send() 直接抛 TimeoutException,消息丢失且不走回调。关键点:阻塞的是你的业务线程,设太久接口会被卡死。max.block.ms 同时覆盖三个超时——等缓冲区空间、等元数据就绪、等序列化完成。配置建议:buffer.memory = 单条大小 × 峰值QPS × 最长Broker延迟 × 安全系数(5~10);max.block.ms 对齐业务接口超时上限(500ms~1s),宁可快失败也不要卡死调用方。


【面试题】3. Kafka 支持 gzip、snappy、lz4、zstd 四种压缩算法——压缩率、速度、CPU 开销上怎么选? ​

【标准答案】四个算法沿"速度↔体积"光谱排列。gzip:压缩率最高(70%~80%)、CPU 消耗也最高——适合带宽瓶颈场景(如跨机房同步)。snappy:速度极快、压缩率中等(50%~60%)、CPU 友好——低延迟场景的常见默认值。lz4:速度与压缩率介于两者之间,但解压特别快——适合消费侧对解压延迟敏感的场景。zstd(Kafka 2.1+):压缩率接近 gzip、速度接近 snappy,是目前综合最优选。选型口诀:低延迟 → snappy 或 lz4,省带宽 → gzip 或 zstd,版本支持 → 无脑 zstd。额外红利:压缩在生产者端做、Broker 原样存、消费者解压。配批量发送时压缩整批效率远高于逐条压缩——这是攒批的另一个隐藏收益。面试官如果听到你说"zstd 是最佳平衡点",就知道你跟进过 Kafka 版本特性。


【面试题】4. Kafka 的 Follower 副本是怎么从 Leader 拉数据的?ReplicaFetcherThread 的工作机制是什么? ​

【标准答案】每个 Follower 所在 Broker 上有一个 ReplicaFetcherThread 线程,工作循环极简:①构造 Fetch 请求(带待拉分区的 fetchOffset),发到 Leader Broker;②Leader 从本地日志读出数据返回;③Follower 追加到本地日志,更新 LEO。核心细节:一个 ReplicaFetcherThread 管同一源 Broker 的所有 Follower 分区,不是每分区一个线程。也就是说 Broker-A 到 Broker-B 的 100 个 Follower 分区,共享一条复制线程串行拉取。这就是为什么副本不宜跨太多 Broker 各放一个——每对 Broker 间只有一条复制通道。限流方面:follower.replication.throttled.rate 控制复制带宽上限,通过 Quota 机制在 Fetch 响应里通知客户端降速。面试说出"一对一 Broker 共用一个 ReplicaFetcherThread"这一句,面试官就知道你看过 Kafka 的复制源码骨架。


【面试题】5. unclean.leader.election.enable 允许"脏选举"是什么意思?打开有什么致命风险?什么场景必须开? ​

【标准答案】"脏选举"指:分区所有 ISR 副本都宕机后,允许从 OSR(已踢出 ISR 的落后副本)里选 Leader。正常情况下必须设 false——因为 OSR 副本数据有缺失,选它当 Leader 意味着把落后副本的"截断点"当合法状态,之前已提交的消息永久丢失。举个例子:acks=all 成功返回后 ISR 全炸,从 OSR 选一个落后 1000 条的副本当 Leader——那 1000 条消息一去不回。唯一要开 true 的场景:只有 1 个副本且对"服务不可用"零容忍(宁可丢数据也不能停机),比如用户体验埋点这种丢了也无所谓的场景。跟钱和订单相关的业务——unclean.leader.election.enable 永远 false。总结一句话:脏选举是"用数据丢失去换服务连续性"的极端开关,你开之前必须清楚丢了什么。


【面试题】6. Kafka 日志压缩(Log Compaction)的 Cleaner 线程是怎么工作的?"墓碑消息"在压缩中起什么作用? ​

【标准答案】Log Compaction 不做"按时间删",而是"同一 Key 只留最新值"。Cleaner 工作机制两步走:第一步——扫描所有 Segment,构建 Key→最新 Offset 映射表;第二步——重遍历旧 Segment,某条消息的 Key 在映射表里对应 Offset 不是它自己 → 旧值,直接删掉。分区 Segment 因此分两段:dirtyHead(待清理)和 cleanedTail(已清理)。墓碑消息是一条 Value=null 的消息,含义是"这个 Key 该删了"。Cleaner 遇到墓碑保留它(当最新值),超过 delete.retention.ms(默认 24h)后墓碑自身也被清掉,Key 彻底消失。生产踩坑:消费者收到 Value=null 的墓碑消息必须特殊处理;compact 是异步的,消费者可能反复读到同一 Key 的多版本——消费端幂等是硬要求。场景判断:用户快照、CDC、配置下发 → compact;日志埋点 → delete;两者都要 → compact+delete。


【面试题】7. Kafka 从 ZK 模式迁移到 KRaft 模式,架构上发生了什么变化?解决了哪些 ZK 时代的核心痛点? ​

【标准答案】ZK 模式下 Kafka 元数据全部存 ZooKeeper,带来三大痛点:①外部依赖——运维 Kafka 必须同时运维 ZK 集群,多一套故障面;②分区瓶颈——分区上万后 ZK Watch 通知雪崩,Controller 加载元数据要几十秒;③脑裂风险——Controller 与 ZK Session 超时可能误判死亡引发双主。KRaft 模式把元数据内化:部分 Broker 兼任 Controller 组成 Raft 共识组,选举一个 Active Controller 写元数据日志,其余 Broker 从 Controller 同步元数据。核心变化:①去掉 ZK,架构只剩 Kafka;②元数据通过 Raft 日志复制保证一致性,Controller 切换降到秒级;③支持百万级分区(增量同步替代全量加载)。迁移路径:Kafka 3.3+ KRaft 已生产就绪,但从 ZK→KRaft 必须建新集群 + MirrorMaker 2 同步数据,不能就地转换。面试说出"元数据日志化 + Raft 共识",面试官就知道你看过 KIP-500。


【面试题】8. Kafka 为什么不支持原生延迟消息?RocketMQ 在这块是怎么做的?业务需要延迟消息时在 Kafka 上怎么实现? ​

【标准答案】Kafka 的设计哲学是"消息落盘后立即可消费"——任何在 Broker 端挂起、延时重投的逻辑都被视为"流外部逻辑",Broker 不揽这个活。RocketMQ 原生支持 18 个延迟级别(1s~2h),原理是将延迟消息先存入内部 SCHEDULE_TOPIC_XXXX,定时任务扫描到期后投递到真实 Topic。要在 Kafka 上做延迟消息:方案一——自建 delay-topic,起一个消费者消费它,达到延迟时间后写入目标 Topic;方案二——Redis ZSet + 定时任务做中间层,到期投递到 Kafka;方案三——Kafka + RocketMQ 混用,谁擅长交给谁。这不是强弱问题,是取舍:Kafka 把简单留给 Broker(只负责读写),复杂留给消费者自己实现;RocketMQ 反之,Broker 做更多事换开箱即用的特性。面试官追问"是不是 Kafka 不如 RocketMQ"——答:场景决定,流处理走 Kafka,交易核心走 RocketMQ,别用单一维度比高下。


【面试题】9. Kafka 的端到端 Exactly-Once(精确一次)语义怎么实现?从生产者到 Broker 到消费者的全线保证需要哪些配置? ​

【标准答案】Exactly-Once 是三段组合拳。第一段——生产者幂等(enable.idempotence=true):Broker 用 PID+Seq 去重,消灭重试导致的重复写入。第二段——事务(transactional.id + initTransactions):把"跨分区原子写入 + 位移提交"包成一个事务,要么全成功要么全不生效;消费者设 isolation.level=read_committed,只读已提交事务的消息,避免读到半途未提交的脏数据。第三段——消费端业务幂等:Kafka 事务只保证"位移提交与消息写入"的原子性,不保证你的业务逻辑只执行一次——消费者处理消息后必须用 Redis SET NX 或 MySQL 唯一键做业务去重。完整配置清单:生产者 → enable.idempotence + transactional.id;Broker → unclean.leader.election.enable=false + min.insync.replicas≥2;消费者 → isolation.level=read_committed + 业务幂等。缺任意一段都不是真正的 Exactly-Once。


【面试题】10. 凌晨 5 点被报警叫醒——Kafka Broker 磁盘写满了,生产写入失败、消费者 Lag 暴涨。你半睡半醒怎么做紧急处理和根因排查? ​

【标准答案】紧急处理三步——先止血再找根。第一步:立刻调大该 Broker 上所有 Topic 的 retention.bytes(kafka-configs.sh 在线改),触发最老 Segment 强制删除。如果还不行,进磁盘手动删最老 Segment 的 .log 和 .index(注意:绝对别碰 active Segment——文件名最大的那个)。第二步:多台 Broker 同时紧张就加磁盘/扩容 Broker,把分区迁移到新机器。第三步:写入恢复后开始排查根因——①用 kafka-log-dirs.sh 查哪个 Topic 数据暴涨;②查业务侧有没有死循环写日志的 Bug;③看 retention 设置——是不是 compact 策略下没有新值的 Key 永远不删?④确认定时清理脚本是不是挂了。预防措施:磁盘使用率告警设 70%(黄)+ 85%(红),留足应急窗口;所有 Topic 强制设 retention.bytes 和 retention.ms 双重上限,防止新建 Topic 忘了设保留策略撑爆磁盘。


生成时间:2026-06-18 23:54:00 | 考点覆盖:10/22