面灵AI→

经纬恒润 Java 秋招一面:Kafka、线程池与线上问题排查

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. ConcurrentHashMap 的 get 方法加锁吗?
  2. 联合索引怎么建立的?
  3. Kafka 怎么保证可靠性、不重复消费?
  4. rebalance 什么原因导致的?最常见的?
  5. 线程池参数怎么设置的?拒绝策略使用的什么?Kafka 适合什么拒绝策略?
  6. 项目里 MCP 怎么用的,用户输入到输出整个链路流程
  7. Kafka 消息堆积怎么排查?
  8. 增加消费者组,消费 TPS 无明显提升,什么原因?
  9. 接口偶发高 RT 怎么排查?
  10. 水平越权出现在生产环境,但测试环境无法复现,怎么办?
  11. 多线程状态变更怎么保证不错乱?
  12. 为什么红黑树转换节点是 8?

反问

《参考解析》

  1. ConcurrentHashMap 的 get 为什么可以不加锁:它的读路径靠 volatile 语义保证可见性——节点数组元素与 Node 的 val/next 都是 volatile 的,读线程拿到的要么是旧值要么是新值,不会读到”写了一半”的对象。所以 get 不加锁,锁只出现在写路径(CAS 或 synchronized 锁桶头)。追问通常接扩容:读遇到 ForwardingNode 时会去新表里找,这是为了扩容期间读也不阻塞。答题时把”读不加锁、写才加锁、扩容可并行”三层说全。

  2. 联合索引与最左前缀:建索引要先看查询模式——把等值条件里区分度高的列放左边,范围条件列放最后,因为范围之后的列用不上索引的有序性。要能顺手画出一个 (a,b,c) 索引支持的查询清单、指出 where b=? 这类为什么用不上。另外注意索引不是越多越好:每个索引都要在写路径上付出维护代价,并且要考虑覆盖索引能否免掉回表。

  3. Kafka 的可靠与去重是两件事,分开答:可靠性一端看生产侧(acks 配置、重试、副本数与最小同步副本数)和存储侧(副本机制、刷盘策略),消费侧看是否手动提交位点、先处理完再提交。不重复消费本质上是”端到端幂等”问题,Kafka 自己做不到,要靠生产端幂等生产者加消费端业务幂等(唯一键、去重表、状态机加版本号)。面试官最想听的是你承认”至少一次投递下必然可能重复”,然后给出幂等方案。

  4. rebalance 的常见触发原因:消费者加入或离开(扩缩容、进程重启)、心跳超时被判掉线、单批处理时间超过最大轮询间隔被踢出组、订阅的 topic 分区数变化。生产上最常见的其实是”业务处理太慢导致超过最大轮询间隔”和”消费者实例上下线”。答完可以补一句治理手段:调大合理的超时、控制单批条数、用静态成员或者协作式再平衡减少抖动。

  5. 线程池参数与拒绝策略,关键是给出推导过程:核心线程数、最大线程数、队列、空闲回收、拒绝策略五项要说清彼此关系——队列一长,最大线程数其实很难起作用。业务上分两类:CPU 密集按核数靠上设,IO 密集按”等待时间与计算时间的比值”放大。拒绝策略里,对”消息必须处理”的链路,让调用线程自己执行(CallerRunsPolicy)能形成反压,比直接丢弃或抛异常的 AbortPolicy 更合适;丢弃类策略在核心链路上一般不能接受,除非业务明确允许丢。

  6. 消息堆积怎么排查:先量化,看消费延迟(lag)是在涨、在平还是在跌,以及生产速率和消费速率的对比,判断是生产暴涨还是消费变慢。消费变慢再往下拆:单条处理耗时是否变长(下游依赖、慢 SQL、外部接口超时)、是否发生了 rebalance 抖动、分区是否有热点导致部分消费者空转、消费者实例是否掉过线。扩容分区和消费者是解法之一,但得先确认瓶颈不在下游。

  7. 加了消费者 TPS 不涨,通常是这几处卡住:消费者实例数超过了订阅的分区数,多出来的实例分不到分区、纯属空转;分区键设计导致数据倾斜,热点分区把个别消费者压满;瓶颈根本不在消费侧,而在下游数据库或第三方接口;单批拉取条数太小、或者每条都同步提交位点,把时间花在了往返上。答题时把”并行度上限=分区数”这条先说清,是最容易拿分的一句。

  8. 接口偶发高 RT 与”只在生产复现”的越权问题,都是排查方法论题:偶发高 RT 先看分布而不是均值——P99 出现毛刺时依次怀疑 GC 停顿或内存压力、连接池排队、锁竞争、慢 SQL、线程池排队、下游超时与网络抖动,靠链路追踪定位到具体环节再谈优化。水平越权只在生产复现,往往说明差异在数据或配置:生产有跨租户数据、网关或鉴权中间件的配置不同、测试环境缺真实角色。正确做法是从代码和鉴权链路上审出越权点,补上统一的服务端校验与审计日志,而不是靠”复现不出来”结案。

  9. 红黑树阈值 8 与多线程状态:阈值 8 是统计学结论——哈希值均匀分布时,单个桶里的节点数近似服从泊松分布,长度到 8 的概率已经低到千万分之几,所以正常情况下根本用不到红黑树,设成 8 是为了让极端情况有兜底、又不必频繁树化;退化回链表的阈值 6 留了缓冲区,避免在 7 附近反复转换。多线程状态变更不错乱,本质是原子性、可见性、有序性三件事:把状态变更收敛到一个入口、用锁或 CAS 保证复合操作的原子性、用 volatile 或锁保证可见性,极端场景可以直接用状态机加版本号做乐观校验。