面灵AI→

纷享销客后端面经:SingleFlight、线程池隔离与三数据源一致性

时间
2026-10
来源
牛客网

《面试题目》

基础信息与项目背景

  1. 请做一个简单的自我介绍,并重点阐述你近期正在开展的工作内容。
  2. 你简历上提到的算法竞赛奖项具体是什么?你的英语水平如何?
  3. 你目前的实习诉求是什么?为什么想找实习?
  4. 你这个项目的来源是什么?是独立完成还是团队合作?你在团队中具体负责哪部分?
  5. 你的简历是否是 AI 生成的?你是如何撰写这份简历的?
  6. 你们团队规模多大?这个项目占团队工作量的百分之多少?

后端架构与并发控制

  1. 请解释 SingleFlight 机制,它在什么经典场景下使用?
  2. 在你的项目中,SingleFlight 是如何做到复用相同 AI 请求以节省 token 的?
  3. 为什么使用 Redis Lua 脚本?它解决了什么问题?
  4. 为什么分布式锁无法满足你的业务场景需求?
  5. 你如何处理请求超时或失败的情况?请说明你的错误分类策略。
  6. 当 owner 节点宕机后,你是如何解决「脑裂」导致的状态冲突问题的?
  7. 你引入了什么版本校验机制来保障数据一致性?

性能压测与极端场景

  1. 你展示的 P95 等数据是压测结果吗?如果不是,你的常规化恢复方案的时间成本和数据量是多少?
  2. MongoDB 在你的业务中存储的是什么数据?数据量大概多大?
  3. 如果数据量膨胀到几百兆的极端情况,你的方案如何应对?
  4. 如果缓存失效,同时触发大量并发恢复请求,你如何处理?
  5. 你提出的基于优先级的请求聚合方案具体是如何实现的?聚合条件是什么?
  6. 你的请求聚合方案如何避免重复恢复?最终实际会发出几次请求?
  7. 并发恢复状态时如果发生冲突,你的快照版本号和幂等机制是如何设计的?
  8. 在高并发场景下,你的方案还有哪些防御性设计?

线程池与资源管理

  1. 你的项目中线程池具体应用在哪些场景?
  2. 你是如何通过异步请求与 CompletableFuture 等待结果的?心跳机制是如何做续约的?
  3. 如果慢请求拖垮线程池,你有什么防范方案?
  4. 请说明你的线程池具体配置与优化策略。
  5. 当线程池发生堆积时,你的超时中断、拒绝策略和定时回收方案具体如何落地?
  6. AI 长耗时任务极易拖垮线程池导致服务宕机,你如何重点防范?
  7. 你提出将 AI 调用隔离至独立线程池并调整参数,这个方案有什么潜在风险?做技术选型时如何评估副作用?

数据一致性与多数据源

  1. MySQL、Redis 与 MongoDB 并存时,如何保证答题状态的一致性?
  2. 你的查询链路是如何设计的?降级策略是什么?
  3. 如果让你在三者的查询链路中去掉一个,你会去掉哪个?去掉后数据恢复怎么做?
  4. 如果把 MongoDB 快照迁移至 MySQL,会对恢复速率产生什么影响?你如何权衡?

AI Agent 与项目技术栈

  1. 你了解 AI Agent 和 RAG 技术吗?请谈谈你的理解。
  2. 你听说过「harness」这个概念吗?
  3. 在你参与的 AI 面试项目中,Agent 调用的完整流程是怎样的?
  4. 后台 Agent 设计中,请求是如何经过路由层判断类型并选择执行策略的?
  5. 工具是如何注入大模型进行循环调用与观察的?
  6. 你的项目中有哪些权限与容错机制?
  7. 你们团队使用了什么平台做低代码 Agent 编排?你在其中偏哪个方向?

《参考解析》

项目介绍与贡献边界:面试官连续追问项目来源、团队规模、你负责的部分,核心是确认真实性。回答用「项目一句话定位 + 团队分工 + 我负责的模块 + 一个可验证结果」。被问「简历是不是 AI 生成的」不用慌,说明你怎么组织素材、哪些经历可以展开、哪些数字有据可查即可。算法竞赛和英语水平如实回答,不要包装。

SingleFlight 与 Redis Lua:SingleFlight 解决「同一 key 并发请求只执行一次」,经典场景是缓存击穿、AI 接口按 token 计费、同一会话被重复提交。Lua 的价值是把「查结果、抢锁、写标记」合成一次原子执行,避免检查与写入之间的竞态;SET key owner NX PX ttl 本身原子,但结合「结果已存在直接返回」就需要脚本。分布式锁只能保证互斥,不能把「后来者」变成「结果复用者」,而且锁释放后下一个请求还会重新执行,所以不适合去重场景。

错误分类与超时:把错误分成四类:可重试(网络抖动、上游 5xx、限流 429)、不可重试(参数错误、鉴权失败、schema 不合法)、部分成功(流式输出中断、工具执行了一半)、未知(超时但可能已生效)。策略不同:可重试用指数退避加抖动、限制次数;不可重试直接失败并返回结构化错误;部分成功要能续传或回滚;未知必须先查状态再决定是否重试,写操作一律带幂等键。

脑裂与版本校验:锁过期或网络分区会出现两个 owner。解决用 fencing token:每次选举 token 单调递增,写结果时带 token,存储端只接受不小于当前 token 的写;旧 owner 醒来写结果会被拒绝。再配「租约」——owner 定期续约,续不上就主动放弃;以及「读己之写」校验,写完回读版本号确认。

极端数据量与请求聚合:MongoDB 存的是会话快照与消息,单会话几十 KB 到几百 KB,几百兆是异常膨胀(比如把整段音频或全量工具输出写进去了)。应对是分片存储(消息按序号分段)、大字段外置到对象存储、TTL 归档、压缩。缓存失效时的并发恢复用「按优先级聚合」:同一 sessionId 的恢复请求合并成一个批次,按「最近活跃、是否在面试中、付费用户」排优先级,低优先级排队;聚合窗口内只发一次恢复请求,结果广播给等待者。冲突用快照版本号 + 幂等键解决,恢复操作本身可重放。

线程池隔离与风险:AI 调用是长耗时、易阻塞的,不能和业务请求共用一个池,否则慢请求会把池占满,拖垮整个服务。做法是独立线程池 + 有界队列 + 明确的拒绝策略(快速失败或降级),核心线程数与最大线程数按「下游并发能力」而不是「CPU 核数」设,配合超时中断和心跳续约。风险是隔离后资源利用率下降、队列积压导致延迟上升、以及隔离池本身成为瓶颈;评估时要看下游配额、P95/P99 延迟、队列长度和拒绝率,做容量规划与压测,而不是拍脑袋调参。

三数据源一致性:MySQL 存强一致的业务数据(订单、用户、答题结果),Redis 存热状态与锁,MongoDB 存快照与消息。一致性策略是「以 MySQL 为准,Redis 做缓存与短期状态,MongoDB 做可重放的快照」:写路径先写 MySQL(事务),再更新 Redis(删除缓存或写新值),最后异步写快照;读路径先 Redis,未命中查 MySQL,再回填。跨源不做分布式事务,用「本地消息表 + 重试 + 幂等」保证最终一致;对一致性要求极高的答题状态,读的时候要带版本号,发现快照落后就以 MySQL 为准修复。

去掉一个数据源怎么选:如果只能留两个,去掉 MongoDB——它的快照能力可以用 MySQL 的大字段表或对象存储替代;Redis 也不能轻易去掉,因为锁、热状态、限流都依赖它。去掉 MongoDB 后恢复速率会受 MySQL 行写入与事务开销影响,但可以通过批量写、分区表、只存增量来缓解。反过来把 MongoDB 迁到 MySQL,恢复会变慢(文档转行、索引更多、写入更重),但一致性和运维复杂度更好,这是「性能 vs 一致性/成本」的权衡。

Agent 调用链路与路由:一次调用是「请求进入 → 路由层判断类型(问答、面试追问、评分、工具任务)→ 组装上下文(系统提示词、会话记忆、检索结果、工具清单)→ 调模型(流式)→ 解析输出(文本或工具调用)→ 执行工具并回填 → 循环直到收敛 → 落库与埋点」。路由层可以用规则 + 小模型分类,输出任务类型、置信度和执行策略(直接回答、检索增强、调工具、转人工);置信度低时走保守策略。

工具注入与 harness:工具以 schema 形式注入提示词,模型输出工具名和参数,运行时校验、执行、把结果作为观察追加到上下文,形成 ReAct 循环。harness 可以理解为一层执行框架:统一管理模型调用、工具注册、上下文装配、重试降级、权限与审计、trace 与评测,让业务只描述任务,不重复实现这些横切逻辑。权限上做工具白名单、参数校验、危险操作确认;容错上做超时、重试、熔断、预算和人工兜底。

低代码编排与个人方向:如果团队用 Dify/Coze/百炼这类平台,说明你负责的是节点编排、提示词与工具配置,还是把平台能力接进业务后端;个人方向可以答「偏后端工程化」——把 Agent 做成可观测、可回滚、可评测的服务,而不是只调提示词。