大厂Java面试:Spring Boot + Redis/Kafka + Spring Cloud + JVM + RAG/向量检索(小Y翻车实录)

大厂Java面试:Spring Boot + Redis/Kafka + Spring Cloud + JVM + RAG/向量检索(小Y翻车实录)

故事背景

地点:某互联网大厂视频内容社区(UGC + AIGC 辅助创作)事业部。

岗位:Java后端/微服务工程师。

面试官(严肃):

"我们做的是内容社区:视频上传、转码、推荐、评论、弹幕、举报审核,还有 AIGC 智能客服与创作助手。你简历里写了 Spring Boot、Redis、Kafka、K8s、还有 Spring AI + RAG。我们按业务链路聊,三轮。"

候选人小Y(搞笑且有点水):

"好嘞,我最擅长......把复杂问题讲简单(讲不明白)。"


第一轮(基础链路):上传-发布-阅读的核心服务怎么搭?(4题)

Q1:如果我们用 Spring Boot + Spring MVC 做"视频发布服务",你会怎么分层?Controller/Service/DAO 各干啥?

小Y:

"Controller 接请求,Service 写业务,DAO 查数据库。基本就......三板斧。"

面试官:

"嗯,至少没把 DAO 写进 Controller 里。那我们继续。"

Q2:发布视频后会产生一堆事件(转码、审核、推送给粉丝、搜索索引),你会选 Kafka 还是 RabbitMQ?为什么?

小Y:

"我一般都用 Kafka,因为......大厂都用。RabbitMQ 也行吧,主要看领导喜欢哪个。"

面试官(眉头一皱):

"理由太'领导驱动'了。给你个机会,补充下吞吐、顺序、回放这些差异。"

小Y:

"Kafka 吞吐大,能回放;RabbitMQ 更像传统消息队列,功能......更全面?"

面试官:

"勉强及格。下一题。"

Q3:视频详情页是高并发热点,你会怎么用 Redis + Spring Cache 做缓存?如何解决缓存穿透、击穿、雪崩?

小Y:

"我就加个 @Cacheable。穿透就......加布隆过滤器?击穿就加锁,雪崩就加随机过期。差不多。"

面试官:

"回答方向对。你知道布隆过滤器误判怎么处理吗?"

小Y:

"误判......那就当它没判错(开玩笑)。其实可以回源数据库再确认。"

面试官:

"还行,知道兜底回源。"

Q4:你说熟 JVM。线上视频发布服务突然 Full GC 飙升,你会怎么定位?

小Y:

"先重启试试......(小声)再看 GC 日志?jstack?jmap?反正工具都能用上。"

面试官:

"重启不算定位。说一条你最常用的链路:从现象到结论。"

小Y:

"嗯......看 GC 日志确认是 Old GC 还是元空间,配合 heap dump 看大对象;再看是不是缓存没上限导致内存涨。"

面试官:

"这句像工程师了。第一轮到这。"


第二轮(微服务进阶):多服务协作、容灾与可观测(5题)

Q1:我们有"发布服务、用户服务、关注服务、通知服务"。你会用 Spring Cloud + OpenFeign 怎么做服务调用?如何做超时与重试?

小Y:

"OpenFeign 写个接口就能调。超时重试......默认就行?不行就加大超时时间。"

面试官:

"默认值最危险。那如果下游抖动,你无限重试会怎样?"

小Y:

"会......更抖?"

面试官:

"对,会雪崩。继续。"

Q2:你会怎么用 Resilience4j 做熔断、限流、隔离?在"发布后通知粉丝"这个链路里,哪些地方应该降级?

小Y:

"熔断就是断开,限流就是不让进。隔离就是......分开线程池?降级的话通知失败就算了,反正粉丝自己刷也能看到。"

面试官(点头):

"通知是可降级的,核心发布不可降级。思路对。"

Q3:数据库层面:发布服务写 MySQL,评论服务也写 MySQL。你选 MyBatis 还是 JPA/Hibernate?怎么做数据库迁移?

小Y:

"我喜欢 MyBatis,SQL 看得见。迁移用 Flyway 或 Liquibase,都行。"

面试官:

"为什么评论更适合 MyBatis?发布更适合 JPA?给个例子。"

小Y:

"评论查询复杂、排序多;发布可能就是简单 CRUD......所以 JPA 省事?"

面试官:

"可以。继续。"

Q4:在 K8s 上跑服务,如何做灰度发布与回滚?CI/CD 你用过 Jenkins/GitLab CI + Docker + Kubernetes 吗?

小Y:

"我会写 Dockerfile,K8s 就 kubectl apply。灰度就是发一半 pod?回滚就是再 apply 回去。"

面试官:

"......你这叫'手工灰度'。那 readiness/liveness、HPA、滚动更新参数你了解多少?"

小Y:

"readiness 是准备好接流量,liveness 是活着;HPA 自动扩缩。参数我得查一下。"

面试官:

"至少概念是对的。"

Q5:可观测性:你会怎么用 Micrometer + Prometheus + Grafana + ELK + Jaeger/Zipkin 定位一次接口慢?

小Y:

"先看 Grafana 的 QPS、RT;然后看日志;再看链路追踪。慢的话可能是 SQL 慢或者下游慢。"

面试官:

"不错,路径清晰。第二轮结束。"


第三轮(综合实战):AIGC 智能客服 + RAG 企业知识库(4题)

Q1:现在要做"内容社区智能客服",回答用户:账号封禁原因、申诉流程、充值问题。你会怎么做 RAG(检索增强生成)

小Y:

"把文档喂给大模型,让它自己学会。"

面试官(严肃加倍):

"'喂给模型'不是工程方案。说清楚:向量化、检索、提示拼接、回答生成。"

小Y:

"就是先把文档切块,然后 Embedding,存向量库,比如 Milvus。用户问问题就向量检索拿 topK,再拼到 prompt,让模型回答。"

面试官:

"这才像话。"

Q2:如何降低 AI 幻觉(Hallucination)?如果客服答错"封禁原因",后果很严重。

小Y:

"那就让它别胡说......加一句提示:请勿编造。"

面试官:

"提示是手段之一,但不够。你还有什么?"

小Y:

"可以让它必须引用检索到的文档片段;没有证据就回答不知道;再加人工审核?"

面试官:

"可以,这叫 grounded generation + 拒答策略。"

Q3:如果我们用 Spring AI 接入模型(OpenAI/Ollama)并做工具调用(MCP/Tool calling),你会怎么设计"查封禁记录"的工具?

小Y:

"我就写个接口给它调:/ban/info。模型想查就查。"

面试官:

"权限呢?审计呢?以及模型怎么知道参数格式?"

小Y:

"权限用 Spring Security + JWT;审计打日志进 ELK;参数格式用 JSON schema 描述给模型?"

面试官:

"对,工具描述要标准化,MCP/函数调用都要 schema。"

Q4:聊天会话要有"记忆"(用户前面说过订单号、手机号)。你会怎么做会话内存?如何避免把敏感信息发给模型?

小Y:

"把聊天记录都存 Redis,然后每次全发给模型。"

面试官:

"......那你等着数据合规找你。你怎么改?"

小Y:

"做摘要记忆,只保留必要字段;敏感信息脱敏/加密;权限校验后再决定能不能放进 prompt。必要时本地检索,不把原文发给模型。"

面试官:

"终于像大厂做法了。今天面试到这,你回去等通知。"


参考答案与学习笔记(按业务链路串起来)

下面把面试中的问题,按"内容社区(UGC)+ AIGC 智能客服"的业务主线,给出可落地的参考答案。适合小白按图索骥。


第一轮答案:单体到高并发基础

1)Spring Boot + Spring MVC 分层怎么做?

典型三层:

  • Controller(接口层)
    • 参数校验(JSR-303)、鉴权(交给 Spring Security)、DTO 转换
    • 不写业务规则、不直接操作数据库
  • Service(业务层)
    • 事务边界(@Transactional
    • 组合多个 DAO/Client
    • 业务规则:发布状态机、风控校验、幂等
  • DAO/Repository(数据访问层)
    • MyBatis Mapper 或 JPA Repository
    • 只做数据读写,不掺业务

加分点:

  • 复杂对象转换用 MapStruct
  • 通用校验/异常用 @ControllerAdvice

2)发布事件:Kafka vs RabbitMQ 怎么选?

在"发布视频 → 转码/审核/推送/索引"这种事件驱动链路里:

  • Kafka 更适合:
    • 高吞吐、水平扩展强
    • 可回放(consumer offset 可控),适合重放"发布事件"做补偿
    • 分区内有序(按 videoId 做 key 保证同一视频事件有序)
  • RabbitMQ 更适合:
    • 低延迟、路由模型灵活(topic/direct/fanout)
    • 对复杂投递语义、死信队列(DLQ)等支持更"开箱即用"

工程建议:

  • 事件总线(发布/审核/索引)走 Kafka
  • 需要强路由或小规模可靠投递(比如后台运营通知)可用 RabbitMQ

3)Redis 缓存 + 三大问题

以"视频详情页"缓存为例:

  • Spring Cache
    • @Cacheable(cacheNames="video", key="#videoId")
    • 热点更新用 @CachePut 或手动 cache.evict + 异步预热

缓存穿透(请求不存在的数据)

  • 布隆过滤器(Bloom Filter)拦截不存在的 videoId
  • 或缓存空值(短 TTL)
  • 误判处理:Bloom 说"可能存在"就回源 DB 再确认

缓存击穿(热点 key 过期瞬间被打爆)

  • 互斥锁:Redis SETNX/Redisson Lock 控制单线程回源
  • 热点永不过期 + 异步刷新(逻辑过期)

缓存雪崩(大量 key 同时过期)

  • TTL 随机化
  • 分批预热
  • 多级缓存(Caffeine 本地 + Redis 分布式)

4)Full GC 飙升定位思路(JVM)

从现象到结论的常用链路:

  1. 先确认指标:Old Gen 使用率、Full GC 次数/耗时(Prometheus/JMX)
  2. 开/查 GC 日志(Java 11/17 用 -Xlog:gc*)看:
    • 是分配速率过高?晋升失败?还是元空间膨胀?
  3. jmap -dump 导出 heap dump,用 MAT/YourKit 看:
    • 最大对象是谁?是否集合无限增长(例如缓存 Map 未设上限)
  4. jstack 看线程是否阻塞导致堆积

典型原因:

  • 大对象(视频元数据/文本列表)频繁创建
  • 缓存无上限
  • 日志/trace 拼接过多字符串

第二轮答案:微服务、稳定性、可观测

1)Spring Cloud + OpenFeign:超时/重试怎么配?

原则:超时要短,重试要慎重。

  • Feign 设置连接/读取超时
  • 重试仅用于幂等请求(GET/查询),写请求避免重试或配合幂等 token

为什么不能无限重试?

  • 下游抖动时会放大流量,导致级联故障(雪崩)

2)Resilience4j:熔断/限流/隔离/降级

在"发布后通知粉丝"链路:

  • 发布主链路(写库):强一致关键路径,优先保证成功
  • 通知粉丝/站内信/Push:可降级

可用策略:

  • 熔断(CircuitBreaker):通知服务连续失败直接快速失败
  • 限流(RateLimiter):限制瞬时推送风暴
  • 隔离(Bulkhead/线程池隔离):通知慢不拖垮发布线程
  • 降级(fallback):落库一条"待通知任务"后异步补偿

3)MyBatis vs JPA/Hibernate + 迁移(Flyway/Liquibase)

  • 评论/搜索类查询:SQL 复杂、动态条件多、性能敏感 → MyBatis 更直观
  • 发布/账号等 CRUD:领域模型清晰、关联少 → JPA 能提高开发效率

数据库迁移

  • Flyway:版本化 SQL(V1__init.sql)简单直接
  • Liquibase:支持 XML/YAML/SQL,适合更复杂变更管理

连接池:优先 HikariCP(现代、性能好)


4)K8s 灰度与回滚(CI/CD)

  • 滚动更新 :Deployment 配 maxSurge/maxUnavailable
  • 灰度
    • 金丝雀:新版本少量 pod
    • 进阶:服务网格/Ingress 按比例分流
  • 回滚kubectl rollout undo deployment/...
  • 健康检查
    • readiness:决定是否接流量
    • liveness:决定是否重启
  • 弹性伸缩:HPA 基于 CPU/自定义指标(QPS/延迟)

5)可观测:Micrometer/Prometheus/Grafana + ELK + Jaeger

定位慢接口的"黄金三板斧":

  1. 指标(Metrics)
    • Grafana 看 P95/P99 延迟、错误率、饱和度
  2. 日志(Logs)
    • ELK 定位异常堆栈、慢 SQL 日志
  3. 链路(Tracing)
    • Jaeger/Zipkin 找到耗时最大的 span(DB/下游/序列化)

第三轮答案:AIGC 智能客服(RAG、工具调用、合规)

1)RAG 端到端怎么做?

以"封禁/申诉/充值规则"知识库为例:

  1. 文档加载:FAQ、工单规范、规则文档(PDF/HTML/数据库)
  2. 切分(chunking):按段落/标题切块,保留来源元数据
  3. 向量化(Embedding):OpenAI/Ollama embedding 模型
  4. 向量库:Milvus/Chroma/Redis Vector
  5. 检索:相似度 topK + 过滤(按业务线/国家/时间版本)
  6. 提示填充(prompt stuffing):把检索片段作为"证据"注入 prompt
  7. 生成:LLM 输出结构化回答(含引用)

2)如何降低幻觉(Hallucination)?

组合拳:

  • Grounding:必须基于检索证据回答,输出引用片段/链接
  • 拒答策略:检索不到就回答"不确定 + 建议转人工"
  • 结构化输出:JSON schema/固定模板,减少自由发挥
  • 人审/抽检:高风险问题(封禁/支付)走人工复核
  • 监控与回放:记录问答与证据,用于追责与优化

3)Spring AI + MCP/工具调用:如何设计"查封禁记录"工具?

工具接口示例(受控):

  • getBanInfo(userId):只返回必要字段
  • schema 描述参数与返回结构(便于模型正确调用)

安全与审计:

  • Spring Security + JWT/OAuth2:鉴权
  • RBAC:只有客服/用户本人可查
  • 审计日志:谁在什么时间查了谁(落 ES/日志系统)
  • 数据最小化:返回原因码 + 可展示文案,不返回内部风控细节

4)会话内存与隐私合规怎么做?

  • 会话存储:Redis 保存 session state(带 TTL)
  • 摘要记忆:把多轮对话压缩成摘要,只保留关键槽位(订单号、问题类型)
  • 敏感信息处理
    • 脱敏(手机号、身份证)
    • 加密存储(必要时)
    • prompt 注入前做敏感字段过滤
  • 分层策略
    • 不把原始 PII 发给模型
    • 能本地查询就本地查,给模型"结果摘要"

结尾

这场面试,小Y 的特点很明显:

  • 基础题能答个方向;
  • 一到"工程细节 + 风险控制 + 量化指标",就开始"领导喜欢哪个"。

如果你把文末答案真正做成自己的项目实践,下次面试官夸的就不只是"方向对",而是"你来带一个子系统"。

相关推荐
2301_809204701 小时前
Redis怎样强行终止陷入死循环的Lua脚本
jvm·数据库·python
Hello.Reader1 小时前
算法基础(九)——循环不变式如何证明一个算法是正确的
java·开发语言·算法
2401_846339561 小时前
mysql如何确保主从数据完全同步_开启半同步复制机制
jvm·数据库·python
m0_741481781 小时前
mysql如何设置定时自动备份脚本_编写shell脚本与cron任务
jvm·数据库·python
m0_631529821 小时前
如何用 cache 参数控制 Fetch 是否读取浏览器自带的缓存
jvm·数据库·python
朝新_1 小时前
【LangChain】少样本提示(few-shorting) 大模型 Few-Shot 提示工程:四大 Example Selector应用
java·人工智能·自然语言处理·langchain
m0_470857641 小时前
CSS如何实现表单元素的统一样式_使用CSS变量控制输入框状态
jvm·数据库·python
yhy66666662 小时前
java内存
java·开发语言
wang3zc2 小时前
如何正确管理浮层提示(Tooltip)显示时的页面焦点顺序
jvm·数据库·python