面灵AI→

美团后端开发一面:消息队列幂等与多 Agent 设计

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

《面试题目》

  1. 自我介绍。我看你第一志愿填了 AI 全栈,为什么选择后端方向?这些项目有上线的吗?
  2. 项目三:简单介绍一下这个项目的业务?为什么选择用 RabbitMQ?项目里的幂等是怎么实现的?如果队列消费出重复的任务是怎么处理的?为什么选择分层存储而不直接用一个大的 MySQL?那怎么保证写业务的一致性呢?
  3. 项目二:这个项目为什么选择用多 Agent 而不用一个大 Agent?这个分工领域 team 是不是可以设计很多个?为什么选择设计这三个?大模型如果出现超时或者超额是怎么处理的?有考虑用重试吗?有设计什么降级的策略吗?项目有没有设计相关的评判指标?
  4. 八股:线程池的核心参数?线程的创建流程是什么?核心线程知道自己是核心线程吗?
  5. 八股:慢 MySQL 怎么排查?索引失效有哪些场景?
  6. 手撕:把链表 0,1,2,3,…,n 重排为 0,n,1,n-1,2,n-2,…。
  7. 反问。

《参考解析》

幂等要先说清重复从哪来,再说怎么挡。 消息队列的重复投递来自至少三个方面:生产者发送成功但 ack 丢失导致重发、Broker 在消费者处理完成但确认丢失后重投、以及消费者自身重试。所以幂等不能只做在一处:生产端给每条消息带全局唯一的业务 ID(messageId 或业务单号),消费端建一张幂等表(业务 ID 加唯一索引)或用 Redis 的 SETNX 占位,处理前先查/占位,处理成功后再落完成状态,重复消息直接丢弃或返回成功;状态机类业务则用「只允许从合法前态迁移」来天然幂等。必须同时讲清失败与回滚:占位后处理失败要让占位可过期(否则消息被永久吞掉),事务内要保证业务写入与幂等记录同生共死(本地事务或消息表方案),并说明消费端是至少一次语义、下游要能承受重复。被追问「为什么会重复」而只会答「加个 Redis 去重」的,通常会被继续追问到答不出来。

分层存储的取舍要落到数据特征上。 分层(热数据放 Redis 或宽表、温数据放 MySQL、冷数据放对象存储或数仓)的理由是访问频率与数据量极度不均:全放一个大 MySQL 会让单表膨胀到影响索引深度、DDL 与备份窗口,而绝大多数查询只碰最近一小部分数据。回答时给出量化依据:单表行数、日增行数、P95 查询模式、冷热比例与迁移成本。写一致性才是这题的难点:跨存储没有单一事务,常见做法是让 MySQL 做唯一事实源、其他层通过 binlog 订阅或事务消息异步同步,接受秒级延迟但要能补偿;或者把写路径收敛到一处并用版本号防旧数据覆盖新数据。还要准备「同步失败怎么办」:对账任务扫描差异并重放、告警与人工介入开关、读路径兜底(缓存版本落后时回源)。

多 Agent 的拆分标准是「职责边界与上下文是否真的不同」。 拆多个 Agent 的价值在于每个 Agent 有独立的系统提示、工具集与上下文窗口,能减少相互干扰、便于独立评测与迭代;代价是编排复杂度、延迟与 token 成本、以及跨 Agent 的信息丢失和错误累积。所以判断标准是:任务是否有清晰的阶段或领域划分、每个阶段需要的工具与知识是否不同、是否有独立的验收标准。如果只是为了「看起来更智能」而拆,一个大 Agent 加更清晰的提示与工具往往更好。被追问「能不能设计更多个」时,要答出边际收益递减——每多一个环节就多一次交接失败的可能,通常先用 2–4 个把链路跑通,再靠评测数据决定要不要继续拆。

超时、重试与降级要成体系地回答。 超时按调用类型设:单次模型调用设请求级超时,整个任务设时间与 token 双预算,超预算就中止并返回已得结果。重试只对可重试错误(限流、网络抖动、5xx)做,带指数退避加抖动并限定次数,非幂等的工具调用要有幂等键才允许重试。降级按层次给:换更小更快的模型、缩小上下文、跳过非关键步骤、返回缓存近似结果,最后才是明确告知失败。评判指标要说得出名字:任务成功率、P95 耗时、单任务 token 成本、工具调用失败率、人工介入率,以及离线评测集上的准确率与格式合规率——没有指标就没有迭代依据。

线程池的核心参数与创建流程要能连起来讲。 七个参数是核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略。提交流程是:核心线程没满就新建核心线程;核心线程满了先进队列排队;队列满了才创建非核心线程直到最大线程数;再满就触发拒绝策略。由此可以解释两个常被追问的推论:用无界队列时最大线程数形同虚设、永远不会触发拒绝策略;用 SynchronousQueue 时不排队、直接扩到最大线程数。至于「核心线程知道自己是核心线程吗」,答案是否定的——线程本身没有标识,是线程池在 Worker 层面用「当前线程数是否超过核心线程数」来决定它是否允许超时回收(allowCoreThreadTimeOut),所以核心与非核心只是池内计数逻辑的区分。

慢查询排查与索引失效场景要按清单答。 排查顺序:先用慢查询日志或 performance_schema 定位到具体 SQL,再 EXPLAIN 看 type、key、rows 与 Extra,重点看是否全表扫描、是否出现 Using filesort 与 Using temporary;然后用 EXPLAIN ANALYZE 看实际行数与耗时,确认预估是否失真(统计信息过期会导致选错索引)。优化手段按收益排序:补正确的索引、消除导致失效的写法、减少返回列走覆盖索引、改写 SQL 与深分页、最后才是结构层面的分区与归档。索引失效的常见场景:对索引列做函数或运算、隐式类型转换(字符串列传数字)、like '%x' 前置模糊、or 连接非索引列、违反联合索引最左前缀、!= 与 not in 在选择性差时被优化器放弃、以及范围查询之后的列在联合索引里失去有序性。

链表重排是「找中点 + 反转 + 合并」的套路题。 以 0,1,2,3,4,5 重排为 0,5,1,4,2,3 为例:快慢指针找中点把链表切成两半(奇数长度时前半段多一个),后半段原地反转,再交替合并。全程常数额外空间、时间 O(n),边界是空链表与单节点。先确认「重排」的读法(本题是首尾交替而非按值排序),再口头说明思路与边界——手撕跑不通时把思路讲清,往往比纠结调试更能拿分。