美团后端开发岗面经合集:从 Skill 路由到支付幂等与分布式锁
- 轮次
- 多轮面试合集
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
面经 01(9 月 23 日)
- 手撕:合并有序数组。
- SQL:查到某门课的平均值,按平均值和课程号排序。
- 设计模式有哪些,用过哪些?
- HashMap 为什么线程不安全,怎么安全?
- 头插法尾插法有什么区别?
- Java 里有哪些容器?
- 聊项目,难点,怎么解决?
- 聊实习,难点,怎么解决?
面经 02(9 月 14 日)
- 上一段实习项目是独立开发还是团队合作?你具体负责哪些部分?
- 原有固定 Workflow 是怎样工作的?
- Skill 相比 Workflow 有什么优势,具体如何降低维护成本?
- 系统有多个 Skill 时,怎样把用户问题路由到正确的 Skill?
- 同一产品存在多个 Skill 时,如何处理路由冲突?
- 随着 Skill 增多,如何持续提升路由准确率?
- Skill 增多导致模型上下文膨胀时怎么办?
- Skill 开发过程中遇到的主要挑战是什么?
- 与确定性代码相比,自然语言 Skill 应该怎样测试和调试?
- 是否会回放历史问题或注入已知故障,验证端到端效果?
- 这套 Skill 使用什么大模型、Harness 和执行框架?
- 是否了解 PI Agent 或 DeepSeek Harness?
- Agent Loop 在哪些条件下终止?
- 你现在还亲自写代码吗?最近一次手写代码是什么时候?
- 怎么看待程序员只使用 AI、不手写代码?
- AI 生成速度超过人工 Review 速度时怎么办?
- 长期来看,程序员更应培养代码审美,还是加强对 Agent 的操控?
- 在实习工作中,你的传统编程能力或代码审美有哪些具体提升?请举例。
- 请介绍一下云服务器产品及其核心能力。
- 裸金属服务器是否提供虚拟化,还是直接以物理机形式交付?
- 云服务器如何实现 Region 级或 AZ 级容灾?
- 给定一个数组和整数 K,统计和为 K 的连续子数组数量。
- AI 快速发展会怎样影响后端程序员的岗位和职业生涯?
- 当安全、架构等经验逐渐被 AI 内化后,资深工程师的经验是否仍有价值?
面经 03(9 月 14 日)
- 实习中做了什么?如何推进开发、保证开发正确,并借助 AI 辅助开发?
- 数据库事务的默认隔离级别是什么?
- 如何解决幻读?
- volatile 和 synchronized 分别解决什么问题?
- synchronized 可以修饰什么?如何保证原子性?
- 商城项目从订单支付到库存扣减的完整流程是什么?
- 支付的具体流程是怎样的?
- 如何保证支付回调不会重复扣减库存?
- Redis 在项目中用于哪些场景?
- Spring 事务的原理是什么?
- @Transactional 注解在哪些情况下会失效?
- 如何解决 @Transactional 自调用导致的失效?
- 平时使用什么 AI 工具?如何使用?
- 对比 Qoder 和 Claude Code 的使用体验。
- 手撕:最小覆盖子串。
面经 04(9 月 10 日)
- 讲一下项目。
- Lua 脚本作用?
- Caffeine + Redis 两级缓存怎么做的?怎么减少回源?
- 怎么保证数据一致性?如果没缓存只有数据库怎么办?
- Kafka 用用户 ID 做 key 会遇到什么问题?怎么扩容?
- Zset 怎么用过吗?原理?为什么用跳表?
- 慢 SQL 怎么优化?
- binlog 有什么模式,分别写的什么?主从复制会出现哪些问题?
- Redisson 分布式锁怎么续期?怎么避免死锁?
- 设计周报发送。
- 讲下怎么设计的。
面经 05(9 月 2 日)
- 询问实习有留用吗?
- 实习做了些什么,PHP 接口迁 Java 怎么做的,怎么确保迁移成功?
- Agent 项目是什么,你做了些什么?
- 数据库用的是什么,怎么用的?
- RAG 怎么做的?
- 工具注册的单例怎么做的?
- 为什么用单例,单例有什么好处,不用单例有什么问题?
- MySQL 用过吗,讲一下慢查询怎么办。
- 什么情况 MySQL 优化会不走索引变成全表扫描?
- 索引什么情况会失效?
- 商城项目的支付是怎么做的?
- 如果一个订单支付两次你怎么处理?
- 你具体是怎么做上一个问题的幂等性处理的?
- 手撕第 k 大的数,限制时间复杂度。
面经 06(原帖在此处被截断)
- 我看你第一志愿填了 AI 全栈,为什么选择后端方向?这些项目有上线的吗?
- 简单介绍一下这个项目的业务?为什么选择用 RabbitMQ?项目里的幂等怎么实现的?如果队列消费出重复的任务是怎么处理的?(原帖截断)
《参考解析》
1. HashMap 与容器。 HashMap 线程不安全有两个层面:并发 put 可能覆盖彼此的写入(读-改-写不是原子操作),JDK 1.7 的头插法在扩容时还可能形成环形链表导致 get 死循环;1.8 改成尾插避免了环,但仍会丢更新、size 计数不准。线程安全用 ConcurrentHashMap(分段/CAS + synchronized 桶头,读不加锁),或者外部加锁,不要用 Collections.synchronizedMap 做高并发。头插/尾插的区别在扩容迁移时:头插把旧链表逆序搬到新桶,实现简单但并发下容易出现环;尾插保持原顺序,1.8 还引入红黑树(链表长度 ≥8 且容量 ≥64 时树化)把最坏查找从 O(n) 降到 O(log n)。Java 容器分 List/Set/Map/Queue 四条线,常用实现和选型见「ArrayList vs LinkedList」「HashSet/LinkedHashSet/TreeSet」「HashMap/LinkedHashMap/TreeMap」「ArrayDeque/PriorityQueue」。
2. Skill 与 Workflow 的取舍、路由与测试。 Workflow 把步骤写死在代码里,稳定但每次需求变化都要改代码、发版;Skill 把「这类任务怎么做」写成可加载的自然语言流程加脚本,模型按需读取,新增场景只要加一个 Skill 文件,维护成本低、上下文占用也可控(只加载命中的那个)。代价是行为不完全确定,需要更强的评测。多 Skill 路由的做法:先给每个 Skill 写清「适用场景 / 不适用场景 / 关键词」的元数据,路由用「规则 + 语义相似度 + 模型判断」三层,冲突时用优先级或让模型在候选里选并给出理由;Skill 变多后路由准确率靠混淆矩阵找易混对、补充反例描述、拆分过粗的 Skill 来提升。上下文膨胀的解法是「索引化」:主提示词只放 Skill 目录(名称 + 一句话),命中后再加载正文,正文里再按步骤懒加载参考资料。测试上,确定性代码用单测,Skill 用「断言行为而非字句」的评测:准备历史问题集、注入已知故障、检查是否走到正确工具、是否满足约束、结果是否可用;再配人工抽检和线上埋点。
3. Agent Loop 的终止条件。 至少四类:任务完成(模型输出最终答案且校验通过)、预算耗尽(最大步数、最大 token、wall-clock 超时)、不可恢复错误(工具连续失败、权限拒绝、参数非法且无法补全)、需要人工介入(副作用操作待确认、信息缺失需用户补充)。工程上还要加「无进展检测」:连续几步调用相同工具、参数相同、结果相同,就判定空转并终止;以及「重复输出检测」防止模型原地打转。终止时必须把已完成的部分、失败原因、建议下一步写进结果,不能静默返回空。
4. 云服务器、裸金属与容灾。 云服务器是把物理资源虚拟化后按需交付的实例,核心能力是弹性伸缩、镜像与快照、按量/包年计费、VPC 网络与安全组、以及挂载云盘。裸金属不提供虚拟化,直接交付物理机(部分产品支持带外管理),适合高性能计算、数据库、以及需要独占硬件或特殊授权的场景。容灾分层:单机用多副本云盘 + 快照;AZ 级把实例分布在同一地域的多个可用区,靠负载均衡和跨 AZ 复制;Region 级做异地多活或冷备,靠数据异步复制 + DNS/全局流量调度切换,RPO/RTO 取决于复制方式。要讲清「备份不等于容灾」——备份解决误删,容灾解决机房级故障。
5. 支付到库存的幂等链路。 下单时生成全局唯一的订单号并落库(状态待支付),支付回调携带订单号 + 支付流水号;服务端先按流水号做唯一索引/Redis SETNX 去重,再用「条件更新」把订单从待支付改为已支付(update ... where order_id=? and status='PENDING',影响行数为 1 才继续),成功后发扣库存消息。扣库存同样幂等:库存表按 (sku_id, order_id) 建唯一约束,或用「扣减流水表 + 唯一键」,重复消息直接被数据库拒绝。整条链路是「本地事务 + 可靠消息 + 消费端幂等」,任何一步失败都能重试而不会重复扣减。@Transactional 失效的常见原因:自调用(不走代理)、方法非 public、异常被 catch 没抛出、抛的是检查异常而默认只回滚 RuntimeException、类没被 Spring 管理、以及多数据源/多线程里事务不传播。自调用失效的解法是注入自身代理、AopContext.currentProxy() 或拆到另一个 Bean。
6. 两级缓存与一致性。 Caffeine 本地缓存挡热点、Redis 分布式缓存挡回源,读顺序是「本地 → Redis → DB」,回源后依次回填。减少回源靠:热点探测与预热、空值缓存防穿透、随机过期时间防雪崩、逻辑过期 + 异步刷新避免击穿。一致性用「先更新 DB,再删缓存」(Cache Aside),删除失败靠重试/消息补偿;强一致要求高时用延迟双删或订阅 binlog(Canal)异步失效。只有数据库时,一致性靠事务和唯一约束保证,缓存只是加速层,任何缓存缺失都必须能回源。Kafka 用用户 ID 做 key 能保证同一用户有序,但会造成分区倾斜(大 V 用户打爆一个分区);扩容时新增分区会改变 key 到分区的映射,顺序保证被破坏,所以要么提前规划分区数、要么用「业务键 + 版本号」在消费端做幂等与重排。
7. ZSet、慢 SQL 与 binlog。 ZSet 用哈希表(member → score)+ 跳表(按 score 有序),跳表通过多级索引把查找/插入做到期望 O(log n),比红黑树实现简单、范围查询更自然,代价是空间常数更大。慢 SQL 优化顺序:先定位(慢查询日志、EXPLAIN 看 type/key/rows/Extra),再看索引(是否缺索引、是否失效、能否覆盖),然后改 SQL(避免 select *、拆大 join、分页优化),最后才是架构(读写分离、分库分表、归档)。binlog 三种模式:STATEMENT 记录 SQL 语句(日志小,但函数/时间等不确定语句会导致主从不一致)、ROW 记录每行变更(最安全,日志大)、MIXED 由系统选择。主从复制的常见问题:延迟(大事务、单线程回放)、中断(网络/权限)、数据不一致(非 ROW 模式)、以及主库宕机时未同步的 binlog 丢失。
8. Redisson 续期与死锁。 Redisson 加锁成功后启动看门狗,默认每 10 秒把锁的过期时间续到 30 秒,只要客户端活着锁就不会因业务超时释放;只有不显式指定 leaseTime 时才启用看门狗。避免死锁:锁必须带过期时间、加锁用 tryLock 带等待超时、解锁放在 finally、解锁前校验持有者(Lua 原子删除);业务侧还要防「锁住后调用外部服务卡死」,给外部调用设超时。Redisson 的看门狗依赖 JVM 存活,进程被 kill -9 或长时间 GC 仍可能丢锁,所以最终一致性要靠数据库条件更新兜底。
9. 周报发送的设计。 先拆需求:谁收、什么时间发、内容从哪来、失败怎么办。方案:定时任务(XXL-Job/ cron)在周五 18:00 触发,按部门分片拉取数据(本周工时、需求完成、线上问题),用模板渲染成邮件/IM 卡片,投递到消息队列后由消费者发送,发送结果落表(成功/失败/重试次数),失败的进重试队列并告警。关键点:幂等(同一周同一人只发一次,用唯一键)、限流(避免一次打爆邮件服务)、可重试、以及内容为空时的降级(发「本周无更新」或跳过)。规模大时按用户分片并行,任务状态可查询、可手动补发。
10. 算法题。 和为 K 的连续子数组:前缀和 + 哈希表,遍历时把 prefix - K 的出现次数累加,O(n)。最小覆盖子串:滑动窗口 + 计数表,右扩直到覆盖所有字符,再左缩更新最优,O(n)。第 K 大:快速选择(partition 后只递归一侧)平均 O(n),或小顶堆 O(n log k);面试要能说出最坏 O(n²) 以及随机化 pivot 的改进。