美团 AI应用开发秋招一面
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 高并发下 Java 服务出现延迟毛刺,如何区分 CPU、锁竞争和外部依赖问题?
- JDK 8 的 ConcurrentHashMap 在高冲突场景下如何保证并发安全?
- synchronized 与 ReentrantLock 在复杂并发场景下如何选择?
- volatile 为什么不能保证 i++ 的原子性?
- 线程池核心参数如何根据任务类型设置?
- 为什么不建议在线程池中使用无界队列?
- CPU 缓存、伪共享和 LongAdder 的关系是什么?
- Redis 为什么通常比 MySQL 快,什么情况下不一定快?
- Redis 高并发计数为什么不能使用普通读改写?
- Redis 集群为什么不支持跨槽位事务,如何设计原子操作?
- 缓存与数据库如何保证最终一致性?
- Redis 集群发生网络分区时如何保证可用性与数据安全?
- RabbitMQ 如何避免消息重复消费?
- 消息可能在哪些环节丢失,如何建立可靠传输链路?
- RabbitMQ 的死信队列除了保存失败消息还有什么价值?
- MySQL InnoDB 的聚簇索引为什么会影响二级索引大小?
《参考解析》
- 用 P99 延迟、CPU、GC、线程池队列、锁等待和外部 RPC 分位耗时分段定位,结合 trace、JFR 或线程 dump 验证结论。
- ConcurrentHashMap 读操作大多无锁,写入通过 CAS 初始化桶并锁定单个桶;链表过长会树化,但复合判断与写入仍需原子方法或额外同步。
- synchronized 适合简单、边界清晰的互斥区并能自动释放锁;ReentrantLock 提供可中断、超时、公平锁和多个 Condition,适用于需要精细控制的状态机或资源池。
- volatile 保证可见性和有序性,i++ 仍包含读、改、写三个步骤。需要原子递增时使用 AtomicInteger 或 LongAdder,涉及多字段则使用锁或 CAS 状态对象。
- CPU密集任务的线程数接近CPU核数,IO密集任务可适当增加,但要受外部依赖并发上限、队列容量和内存约束,并为不同资源隔离线程池。
- 无界队列会掩盖过载,使延迟和堆内存持续增长。应使用有界队列、限流、超时和明确的拒绝或降级策略。
- 伪共享源于不同线程修改同一缓存行中的变量;LongAdder 将热点计数分散到多个 Cell,适合吞吐统计,不适合要求每次读取都严格精确的余额或库存。
- Redis 基于内存且执行简单命令,但大Key、阻塞命令、网络往返、淘汰、复制和长 Lua 脚本都可能使它变慢。
- 普通读改写存在竞态,应使用 INCR 等原子命令;多个字段需要一起更新时使用短小 Lua 脚本。
- 不同 Key 可能落在不同槽位,单节点无法保证跨节点事务。相关 Key 可使用 Hash Tag 放入同一槽位,跨槽位业务改用事件驱动和补偿事务。
- 常见做法是先更新数据库再删除缓存,并用延迟双删、事务消息或 CDC 补偿;事件带版本号且处理幂等,强一致场景以数据库为最终裁决。
- 主从切换提高可用性但可能丢失异步复制数据。应配置写入保护、复制偏移检查,并通过幂等键、数据库回源和恢复对账降低风险。
- RabbitMQ 通常是至少一次投递,消费者在业务成功但 ACK 前宕机会重复消费。应以消息ID做幂等记录,并让业务更新与记录处于同一事务或受唯一索引保护。
- 生产、Broker落盘、投递、消费处理和 ACK 前都可能丢失。Outbox、本地事务、持久化消息、手动 ACK、重试、死信和对账共同组成可靠链路。
- 死信队列可隔离异常消息,支持人工审核、失败分析、版本迁移和补偿。重试应退避并记录原消息、异常、次数和 traceId。
- InnoDB 二级索引叶子节点保存索引列和主键值,主键越长,所有二级索引越大,从而增加磁盘、缓存和回表成本。