面灵AI

美团 AI应用开发秋招一面

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

《面试题目》

  1. 高并发下 Java 服务出现延迟毛刺,如何区分 CPU、锁竞争和外部依赖问题?
  2. JDK 8 的 ConcurrentHashMap 在高冲突场景下如何保证并发安全?
  3. synchronized 与 ReentrantLock 在复杂并发场景下如何选择?
  4. volatile 为什么不能保证 i++ 的原子性?
  5. 线程池核心参数如何根据任务类型设置?
  6. 为什么不建议在线程池中使用无界队列?
  7. CPU 缓存、伪共享和 LongAdder 的关系是什么?
  8. Redis 为什么通常比 MySQL 快,什么情况下不一定快?
  9. Redis 高并发计数为什么不能使用普通读改写?
  10. Redis 集群为什么不支持跨槽位事务,如何设计原子操作?
  11. 缓存与数据库如何保证最终一致性?
  12. Redis 集群发生网络分区时如何保证可用性与数据安全?
  13. RabbitMQ 如何避免消息重复消费?
  14. 消息可能在哪些环节丢失,如何建立可靠传输链路?
  15. RabbitMQ 的死信队列除了保存失败消息还有什么价值?
  16. MySQL InnoDB 的聚簇索引为什么会影响二级索引大小?

《参考解析》

  1. 用 P99 延迟、CPU、GC、线程池队列、锁等待和外部 RPC 分位耗时分段定位,结合 trace、JFR 或线程 dump 验证结论。
  2. ConcurrentHashMap 读操作大多无锁,写入通过 CAS 初始化桶并锁定单个桶;链表过长会树化,但复合判断与写入仍需原子方法或额外同步。
  3. synchronized 适合简单、边界清晰的互斥区并能自动释放锁;ReentrantLock 提供可中断、超时、公平锁和多个 Condition,适用于需要精细控制的状态机或资源池。
  4. volatile 保证可见性和有序性,i++ 仍包含读、改、写三个步骤。需要原子递增时使用 AtomicInteger 或 LongAdder,涉及多字段则使用锁或 CAS 状态对象。
  5. CPU密集任务的线程数接近CPU核数,IO密集任务可适当增加,但要受外部依赖并发上限、队列容量和内存约束,并为不同资源隔离线程池。
  6. 无界队列会掩盖过载,使延迟和堆内存持续增长。应使用有界队列、限流、超时和明确的拒绝或降级策略。
  7. 伪共享源于不同线程修改同一缓存行中的变量;LongAdder 将热点计数分散到多个 Cell,适合吞吐统计,不适合要求每次读取都严格精确的余额或库存。
  8. Redis 基于内存且执行简单命令,但大Key、阻塞命令、网络往返、淘汰、复制和长 Lua 脚本都可能使它变慢。
  9. 普通读改写存在竞态,应使用 INCR 等原子命令;多个字段需要一起更新时使用短小 Lua 脚本。
  10. 不同 Key 可能落在不同槽位,单节点无法保证跨节点事务。相关 Key 可使用 Hash Tag 放入同一槽位,跨槽位业务改用事件驱动和补偿事务。
  11. 常见做法是先更新数据库再删除缓存,并用延迟双删、事务消息或 CDC 补偿;事件带版本号且处理幂等,强一致场景以数据库为最终裁决。
  12. 主从切换提高可用性但可能丢失异步复制数据。应配置写入保护、复制偏移检查,并通过幂等键、数据库回源和恢复对账降低风险。
  13. RabbitMQ 通常是至少一次投递,消费者在业务成功但 ACK 前宕机会重复消费。应以消息ID做幂等记录,并让业务更新与记录处于同一事务或受唯一索引保护。
  14. 生产、Broker落盘、投递、消费处理和 ACK 前都可能丢失。Outbox、本地事务、持久化消息、手动 ACK、重试、死信和对账共同组成可靠链路。
  15. 死信队列可隔离异常消息,支持人工审核、失败分析、版本迁移和补偿。重试应退避并记录原消息、异常、次数和 traceId。
  16. InnoDB 二级索引叶子节点保存索引列和主键值,主键越长,所有二级索引越大,从而增加磁盘、缓存和回表成本。