【面试题】1. Kafka 如何保证消息在分区内的顺序性?什么情况下会打破顺序?
【标准答案】Kafka 只在单个分区内保证消息严格有序——同一个分区里,先发的一定先被消费。前提是:生产端必须把需要有序的消息发到同一个分区(Key 相同或指定分区号),而且 max.in.flight.requests.per.connection 设为 1(关闭幂等时)或开启幂等(此时可设 >1)。会打破顺序的常见场景:①没指定 Key,消息随机分发到多个分区;②配置了重试,max.in.flight >1 且没开幂等,前一条失败重试被后一条追上了;③消费端多线程并行消费同一分区的消息。记住一句话:要顺序就老老实实单分区、串行消费;如果想并行,按 Key 做分片再串行,别动同一分区里的顺序。
【面试题】2. Kafka 的分区分配策略 Range、RoundRobin、Sticky 分别怎么分?各自的坑是什么?
【标准答案】三种策略都是 Coordinator 在消费组 Rebalance 时决定"哪个消费者拿哪些分区"。①Range:按主题逐个处理分区,每个消费者分连续的一段。坑:多主题订阅时,分区数不均会导致"头大脚小"——前面的消费者可能多分好几个分区。②RoundRobin:把所有主题的分区打平排成列表,轮询分配给消费者。比 Range 更均匀,但坑是变化频繁时容易触发再均衡。③Sticky(默认,2.4+):尽量保持现有分配不变,只移动最小量的分区完成重新分配。优势是稳定性好,减少不必要的分区迁移。实战建议:如果消费者数量稳定,用 Sticky 最省心;如果频繁扩缩容,关注均衡即可,别过度优化分配策略。
【面试题】3. Kafka 的日志分段(LogSegment)是怎么设计的?.log、.index、.timeindex 分别存什么?
【标准答案】每个分区的数据在磁盘上是"分段"存储的,一个 Segment 对应一组文件。①.log:存实际消息内容,每一条消息有 offset + 物理位置;②.index:稀疏索引,存 offset → 物理位置的映射,查找消息时先定位到最近的索引,再在 log 文件里顺序扫几 KB 就能找到,速度快;③.timeindex:时间戳 → offset 的映射,按时间范围消费时就靠它定位。三个文件一一对应,基础名是 Segment 起始 offset。默认一个 Segment 满 1GB 或者 7 天就滚动(roll)生成新 Segment。面试说出来.keyIndex 和 .timeIndex 的差别,面试官就知道你真拆过 Kafka 的磁盘文件。
【面试题】4. Kafka 的 GroupCoordinator 是怎么管理消费组的?它和 Controller 的分工是什么?
【标准答案】GroupCoordinator 是负责消费组管理的节点,每个 Broker 上都有。当消费组创建或 Rebalance 时,组内第一个消费者所在的 Broker 的 GroupCoordinator 会成为这个组的"管家"。它的核心工作:①选出一个消费者当 Group Leader;②让 Leader 执行分区分配方案;③把分配结果下发给所有成员;④维护成员心跳,超时没心跳就踢掉触发 Rebalance。Controller 则是管集群级别的元数据——分区 Leader 选举、Broker 上下线感知、ISR 维护。一句话:Controller 管的是 Broker 和分区的"组织架构",GroupCoordinator 管的是消费组的"团队分工"。面试官追问 Offset 存哪?——老版本存 ZK,新版本存内部 Topic __consumer_offsets,由 GroupCoordinator 负责写。
【面试题】5. 手动提交位移的 commitSync 和 commitAsync 有什么区别?实战中怎么选?
【标准答案】commitSync 是同步提交,发请求后阻塞等待 Broker 确认成功才返回。好处是可靠,失败了一定知道,可以重试;坏处是阻塞,影响消费吞吐。commitAsync 是异步提交,发请求立即返回,不等待确认。好处是不阻塞、吞吐高;坏处是失败了可能不知道,位移没提交成功会导致重启后重复消费。实战策略:正常消费循环里用 commitAsync 异步批量提交,不耽误拉消息;在消费组关闭(shutdown hook)或 Rebalance 前用 commitSync 同步兜底提交一次,确保分区放弃前把位移写进去。如果业务要求精准,再配合业务幂等处理,位移提交+幂等=最终一致性。
【面试题】6. Kafka vs RocketMQ:架构、消费模型、事务消息三方面核心差异?什么场景选谁?
【标准答案】①架构:Kafka 对标海量流式数据,Broker 无状态、元数据存 ZK/KRaft,依赖分区多副本做到高吞吐;RocketMQ 用 NameServer 做路由发现,Broker 可组主从模式,天然支持事务消息和延迟消息。②消费模型:Kafka 是拉模式(Pull),消费者按自己的节奏拉消息,实时性靠拉取间隔和长轮询保证;RocketMQ 支持推拉结合,Broker 主动推,实时性略好但需要消费者反压控制。③事务消息:RocketMQ 原生支持"半消息+查回",更适合分布式事务场景(下单扣库存);Kafka 事务是 Exactly-Once 语义,面向流处理内部一致性。选型:日志、流处理、大数据管道选 Kafka;电商交易、分布式事务、延迟消息选 RocketMQ;别再拿"谁性能好"硬比,场景不对全白搭。
【面试题】7. 线上消费者突然发现丢了一批消息,Leader 没切换、offset 也没跳,从哪开始查?排查链路是什么?
【标准答案】丢消息的排查要从"消息走了哪条路"反推。第一步:确认消息到底有没有刷进 Kafka——查生产者回调里的发送结果,看有没有 send failure;同时看生产者 metrics 的 record-error-rate,如果 >0,生产端就丢了。第二步:检查 min.insync.replicas 和 acks 配置——如果 acks=1 且 Leader 宕机前 Follower 没同步完,Leader 一切换消息就丢了。第三步:查日志清理策略,如果 retention.ms 设太短,消费者还没消费消息就过期被删了。第四步:确认有没有人手动 seek 了偏移量,或者有消费组删 Offset。最后看 Broker 日志有没有 ReplicaFetcherThread 的异常。实战口诀:生产先看回调,Broker 看副本,消费看 Offset 和过期。
【面试题】8. 双十一流量翻5倍,某消费组积压千万条消息,你如何在不重启业务的前提下快速消积压?
【标准答案】三步走:第一步调参数不重启——调大消费者每次拉取条数 max.poll.records(默认 500→2000),减少拉取频率浪费,但注意别超过 max.poll.interval.ms 导致 Rebalance。第二步不重启扩容——增加消费组内的消费者数量,但前提是 Topic 分区数要够;如果分区数不够,紧急增加分区(只增不减,慎用,可能影响有序性)。第三步临时降级+错峰——把非核心逻辑(发通知、写日志、写统计)暂时关掉或者扔到队列异步处理,消费链路变短后吞吐自然上去。如果这些都不够,写一个临时消费者指定新消费组,把积压消息批量读到 Redis 或本地,让主消费组跳过旧消息从最新开始消费,腾出手后再回头补数据。
【面试题】9. 新业务要接入 Kafka,你作为技术负责人,Topic、分区、消费组的整体设计 checklist 是什么?
【标准答案】第一刀:定 Topic 粒度。一个业务线一个 Topic 还是按子域拆分?原则是一个 Topic 的消息类型要一致,别把订单、日志、通知全塞一起。第二刀:定分区数。根据预估峰值 QPS / 单分区吞吐(保守算 20MB/s 写)反推分区数,留 1.5 倍余量;分区只能增不能减,宁可多不少。第三刀:定副本数。生产最少 3 副本,min.insync.replicas ≥ 2。第四刀:定清理策略。根据业务要求设 retention(按时间还是按大小),不重要的数据 7 天,重要的存档数据走 S3/ODPS。第五刀:定消费组。一个业务逻辑一个消费组,别多个逻辑共用一个组;位移重置策略设为 earliest 还是 latest 要写准入文档写明。第六刀:定序列化。吞吐优先用 Protobuf/Avro,可读性优先用 JSON,必须前后一致。第七刀:定监控和告警。消费 Lag > 阈值、生产失败率 > 0.1%、分区不均衡必须告。产出物:Topic 准入文档 + 消费者接入规范 + 监控大盘,缺一不可。
【面试题】10. 幂等消费者到底怎么写?Redis 去重、MySQL 唯一键、布隆过滤器各有什么适用场景和坑?
【标准答案】幂等消费者核心思路:消费前先查"这条消息处理过没",处理完标记"已处理"。①Redis 去重:用消息唯一 ID(业务 ID 或 offset+partition)作 Key,SET NX 做到"只有第一个消费者能处理"。优点快,缺点内存有限、Redis 挂了漏防。②MySQL 唯一键:在业务表上建唯一索引(order_id + event_type),处理时 INSERT IGNORE 或 ON DUPLICATE KEY 忽略,数据库天然去重。优点不丢、持久化,缺点写压力大时成为瓶颈。③布隆过滤器:内存极小,判断"可能没处理过"再做去重。优点省内存,缺点有误判率(判存在实际不存在→漏处理),需要配合兜底方案。实战组合:先布隆过滤,命中后走 Redis 精确去重,最后 MySQL 唯一键兜底,三层防御;同时设去重窗口(如 7 天后自动清理),别把去重变成新的瓶颈。