大厂Java面试实录:Spring Boot/Cloud、Redis+Kafka、JVM调优与RAG/Agent(Spring AI)三轮递进问答

大厂Java面试实录:Spring Boot/Cloud、Redis+Kafka、JVM调优、RAG/Agent(Spring AI)三轮递进拷打

角色

  • 面试官(M):语气严肃,追问很细。
  • 候选人小Y(Y):会一点,但不多;简单题能对答,难题开始"玄学输出"。

面试场景设定

你应聘的是某互联网大厂 电商+内容社区(UGC) 业务线,近期在做 AIGC 智能客服 (知识库问答 + 工单流转)。技术栈以 Java/Spring 生态 为主:

  • 服务:Spring Boot + Spring Cloud + OpenFeign + Resilience4j
  • 数据:MySQL(JPA/MyBatis)+ Redis + Elasticsearch
  • 消息:Kafka
  • 可观测:Micrometer + Prometheus/Grafana + ELK + Jaeger
  • AI:Spring AI + RAG + 向量库(Milvus/Redis)+ Agent 工具调用(MCP/函数调用风格)

第一轮(基础到上手):做一个"订单查询+客服入口"服务

Q1(Spring Boot 基础)

M: 你用 Spring Boot 起一个"订单查询"接口,怎么做版本管理、依赖管理?Maven 里常见的依赖冲突怎么排?

Y: 就...spring-boot-starter-web 然后写个 @RestController。依赖冲突的话我一般 mvn clean install,不行就删 .m2 目录重来。

M: (沉默两秒)...你至少说出了一个"终极大法"。那你知道 dependency:tree 吗?

Y: 知道知道,树嘛,能看到树。

M: 行,先过。


Q2(JVM/JDK 基础)

M: 线上用 Java 8/11/17,你能说说它们在 GC 或运行时方面的差异吗?

Y: Java 17...更快?Java 11...也更快?反正越新越好。

M:(表情依旧严肃)"越新越好"不算答案。那你至少说说你熟悉的 GC:CMS、G1?

Y: G1 就是把内存切成很多块,然后...按块回收。

M: 这句还算对。继续。


Q3(数据库连接池与事务)

M: 订单查询走 MySQL,你用 HikariCP。为什么生产上推荐 HikariCP?怎么设置连接池参数?

Y: Hikari...快。参数就默认吧,一般不会出问题。

M: (终于给了点引导)默认不一定安全。比如你订单库有慢查询,会把连接占住,你的池子会被打满。那你知道 maximumPoolSizeconnectionTimeout 这些怎么结合 QPS 算吗?

Y: 我一般先配个 200,顶不住再加。

M:(点头但不夸)至少你知道会"顶不住"。


Q4(缓存:Redis + Spring Cache)

M: 订单查询要加缓存,你会怎么用 Redis?怎么避免缓存穿透、击穿、雪崩?

Y: Redis 就 get/set。穿透就...加缓存;击穿就...加锁;雪崩就...多加几台 Redis。

M:(第一次夸)思路方向对了:穿透/击穿/雪崩是三件事。等下我会细问锁怎么加。


第二轮(进阶到分布式):UGC + 订单事件流

Q1(Kafka:异步解耦与一致性)

M: 下单后要发"订单已创建"事件,UGC 社区要展示"我买过这件商品"徽章。你为什么用 Kafka?如何保证"订单落库"和"发消息"不丢?

Y: Kafka 吞吐高。保证不丢就...多发几次?或者失败重试。

M: 你说的是"可能重复",但不等于不丢。那你知道 Outbox 模式或事务消息吗?

Y: 听过...Outbox 就是...有个 box。

M:(语气仍严肃)行,至少听过。


Q2(Spring Cloud:OpenFeign + Resilience4j)

M: 订单服务要调库存服务,用 OpenFeign。库存服务偶发超时,你怎么做超时、重试、熔断、限流?

Y: Feign 可以配超时。重试我觉得可以一直重试直到成功。熔断的话...把服务重启。

M:(轻轻叹气)一直重试会把下游打死。熔断不是重启。你应该提到 Resilience4j 的 CircuitBreaker/Retry/RateLimiter/Bulkhead。

Y: 对对对,就是那个...四件套。


Q3(分布式追踪与可观测)

M: 线上说"下单很慢",你怎么用 Micrometer + Prometheus/Grafana + ELK + Jaeger 排查?

Y: 看日志,看 Grafana。如果不行就加日志。

M:(终于给了鼓励)"加日志"是很多人最后的救命稻草。但大厂更希望你先用指标定位,再用 trace 找具体链路,最后用日志看细节。


Q4(数据库:索引与分页)

M: 订单列表分页查询,表很大。你用 MyBatis 或 JPA 都行。怎么做高性能分页?

Y:limit offset。offset 大了就...加机器。

M:(严肃)offset 大了会扫很多行。你应该考虑"基于游标/主键的翻页"(seek method)和合适的联合索引。

Y: 嗯...游标我在 IDE 里用过。

M: 不是那个游标。


第三轮(高阶到AI落地):AIGC 智能客服(RAG + Agent 工具调用)

Q1(RAG:检索增强生成)

M: 智能客服要回答"我这单什么时候发货",大模型不能瞎编。你怎么用 RAG 降低 Hallucination?

Y: 我让模型"不要瞎编"。提示词写严一点。

M:(冷静)提示词只能缓解。你得把"事实"检索出来喂给模型,并且让它引用来源。


Q2(向量化与向量库)

M: 企业知识库(退换货政策、物流规则、活动规则)怎么入库?Embedding 模型怎么选?向量库选 Milvus/Chroma/Redis 有什么考虑?

Y: Embedding 就...把文字变成数字。向量库我觉得 Redis 最好,因为我熟。

M: 熟是优势,但要讲清楚:规模、召回、延迟、运维成本、过滤能力。


Q3(Agent:工具执行框架与MCP/函数调用)

M: 客服不仅要回答,还要能"查订单状态、发起退款、创建工单"。你怎么做 Agent 工具调用?怎么保证安全与审计?

Y: 我给模型一个接口文档,它就会调。

M:(严肃追问)模型不会"天然守规矩"。你得做工具白名单、参数校验、权限控制(OAuth2/JWT/Spring Security)、以及完整审计日志。

Y: 对对对,我们可以在网关里拦一下。


Q4(会话内存与多轮对话)

M: 用户说"我上次那单怎么还没到",又说"不是这单,是上上单"。你怎么做聊天会话内存?怎么避免把别人的信息串了?

Y: 用一个全局 Map 存起来?key 用用户昵称。

M:(终于露出一丝无奈)昵称会重复。你需要 userId+sessionId+过期策略,必要时落 Redis,并对敏感信息做脱敏与访问控制。


面试收尾

M: 今天先到这里。你基础还行,但复杂系统设计和 AI 落地细节需要补强。回去等通知吧,我们 HR 这两天联系你。

Y: 好的好的,我回去就把...那个 box...补上。


题目答案与小白可落地笔记(按三轮整理)

目标:把面试问题变成"能在真实业务里写出来/配出来/排查出来"的知识点。


第一轮答案:订单查询服务(Spring Boot + MySQL + Redis)

A1 Maven 依赖管理与冲突排查

业务点: 大厂项目模块多、依赖多,冲突是常态(如 Jackson/Netty/Guava 版本打架)。

做法:

  • 使用 Spring Boot Parent/BOM 统一依赖版本:
    • spring-boot-starter-parentdependencyManagement 引入 spring-boot-dependencies
  • 排查冲突:
    • mvn -q dependency:tree -Dincludes=groupId:artifactId
    • 看是否出现多个版本(nearest-wins)
  • 解决:
    • dependencyManagement 锁版本
    • 对不需要的传递依赖用 exclusions

A2 Java 8/11/17 与GC要点(面试常考)

业务点: 版本升级通常为了更好吞吐、更低停顿、更强观测能力。

要点速记:

  • Java 8:默认 Parallel GC(Server 模式下常用 CMS/G1 需手动);CMS 已逐步淘汰。
  • Java 11:LTS;G1 更成熟;JFR、容器支持更完善。
  • Java 17:LTS;G1 进一步增强;ZGC/ Shenandoah 在部分发行版可用;整体运行时优化更多。
  • G1 核心思想:Region 化内存 + 预测停顿时间,适合大堆、低停顿诉求。

排查工具链:

  • jcmd / jstack / jmap / jstat
  • GC 日志:-Xlog:gc*(11+)

A3 HikariCP 为什么常用 & 参数如何估算

业务点: 高并发下连接池"过大"会压垮 DB,"过小"会把应用线程卡死。

为什么 HikariCP:

  • 性能好、实现简洁、延迟低
  • 对连接泄漏检测(leakDetectionThreshold)等支持较好

关键参数:

  • maximumPoolSize:连接池最大连接数
  • minimumIdle:最小空闲连接
  • connectionTimeout:取连接超时(避免线程无限等)
  • idleTimeout / maxLifetime:连接存活与回收(需小于 MySQL wait_timeout)

粗略估算思路:

  • 需要连接数 ≈ QPS × 平均SQL耗时(秒) × 安全系数
  • 例:2000 QPS,平均 SQL 10ms:2000×0.01=20;乘 2~3 的安全系数 → 40~60

A4 Redis 缓存三大问题与解法

业务点: 订单查询是典型读多写少,适合缓存,但要扛恶意流量与热点。

  • 缓存穿透 (查不存在的订单号):
    • 参数校验 + 布隆过滤器(Bloom Filter)
    • 对"确实不存在"写入空值缓存(短 TTL)
  • 缓存击穿 (热点 key 过期瞬间):
    • 互斥锁/单飞(singleflight):只有一个线程回源
    • 逻辑过期(value 带过期时间,后台异步刷新)
  • 缓存雪崩 (大量 key 同时过期/Redis 故障):
    • TTL 加随机抖动
    • 多级缓存(Caffeine 本地 + Redis)
    • Redis 高可用(哨兵/集群)+ 降级策略

第二轮答案:事件驱动 + 微服务治理 + 可观测

B1 Kafka 为什么用 & "落库+发消息不丢"的实现

业务点: 下单完成后要驱动多个下游(积分、库存、UGC 徽章、风控),同步调用会把链路拉长。

Kafka 价值:

  • 异步解耦、削峰填谷、吞吐高、可回放(按 offset)

不丢消息的经典方案:Outbox(事务外盒)

  1. 订单服务本地事务:
    • 写订单表
    • 写 outbox 表(事件内容、状态)
  2. 后台任务/CDC(Debezium)读取 outbox,投递 Kafka
  3. Kafka 投递成功后标记 outbox 已发送

消费端幂等:

  • 消费消息带 eventId,落库去重(唯一索引)
  • 处理失败重试 + 死信队列(DLQ)

B2 OpenFeign + Resilience4j 的正确姿势

业务点: 下游偶发超时是常态,要"可控失败"。

建议组合:

  • 超时:连接超时 + 读超时(别太大)
  • 重试:只对幂等请求重试(如 GET 查询库存),并设置退避(backoff)
  • 熔断:CircuitBreaker(失败率阈值、半开探测)
  • 隔离:Bulkhead(线程池/信号量隔离,防止被拖死)
  • 限流:RateLimiter(保护下游与自身)
  • 降级:Fallback(返回兜底、提示稍后再试)

B3 可观测三件套:指标、链路、日志

业务点: "慢"可能来自 DB、下游、GC、网络、锁竞争。

排查路径:

  1. Metrics(Prometheus/Grafana + Micrometer)
    • QPS、P95/P99 延迟、错误率
    • JVM:堆、GC 暂停时间、线程数
    • 连接池:活跃连接、等待时间
  2. Tracing(Jaeger/Zipkin)
    • 还原一次请求的调用链,找哪个 span 最慢
  3. Logging(ELK)
    • 结合 traceId 检索日志
    • 对关键路径打结构化日志(JSON)

B4 大表分页:别用深 offset

业务点: 订单列表越翻越慢是必现问题。

问题: limit offset offset 很大时,MySQL 仍需扫描大量行再丢弃。

解法:Seek/游标分页

  • 以上一次最后一条记录的 (create_time, id) 作为游标:
    • where (create_time,id) < (?,?) order by create_time desc, id desc limit 20
  • 配合联合索引:
    • (user_id, create_time, id)

第三轮答案:AIGC 智能客服(RAG + Agent + 安全)

C1 RAG 如何降低幻觉(Hallucination)

业务点: "物流状态、退款规则"必须以真实系统/文档为准。

RAG 基本流程:

  1. 用户问题 → 重写/归一化(可选)
  2. 向量检索召回相关文档片段(TopK)
  3. 将片段作为上下文(context)喂给 LLM
  4. 生成回答并引用证据(source),必要时输出"不确定/需人工"

关键策略:

  • 让模型只基于 context 作答(指令约束)
  • 设定置信度阈值:召回不足则转人工
  • 输出带引用(文档名/段落ID/链接)

C2 知识库入库:Embedding 与向量库选型

入库流程:

  • 文档加载(PDF/HTML/Markdown/数据库)
  • 清洗与切分(chunking,按段落/标题;控制 token)
  • Embedding 向量化(OpenAI/Ollama 等)
  • 写入向量库(向量 + metadata,如业务线、版本、生效日期)

选型考虑:

  • Redis 向量检索:运维简单、适合中小规模与低延迟场景;但在超大规模与复杂检索能力上可能不如专用向量库。
  • Milvus:更偏大规模向量检索、索引类型丰富、适合企业级。
  • Chroma:更轻量,适合原型/中小项目。

必须做的工程化:

  • metadata 过滤(如只查"当前生效版本")
  • 增量更新(Flyway/Liquibase 管理规则版本,或给文档加版本号)

C3 Agent 工具调用:别让模型"想调就调"

业务点: 客服要能执行动作(查订单、发起退款、建工单),属于高风险操作。

推荐架构:

  • LLM 只负责"计划/意图识别"
  • 工具调用由 工具执行框架 托管(Spring AI function calling / 自研 tool registry)
  • 工具以白名单注册:
    • getOrderStatus(orderId)
    • createTicket(userId, category, content)
    • applyRefund(orderId, reason)

安全与审计:

  • 身份认证:JWT/OAuth2(Spring Security)
  • 授权:用户只能查自己的订单(ABAC/RBAC)
  • 参数校验:Bean Validation + 业务校验(订单是否属于该用户)
  • 审计日志:记录 toolName、参数、调用人、结果、traceId
  • 敏感信息脱敏:手机号/地址

MCP(模型上下文协议)类思想落地:

  • 将"工具描述、参数 schema、返回结构"标准化
  • 让工具可扩展、可插拔、可测试

C4 会话内存:隔离、过期、可追溯

业务点: 多轮对话要"记得住",但不能串用户、不能泄露隐私。

实现建议:

  • 会话 key:userId + sessionId(不要用昵称)
  • 存储:短期内存 + Redis(带 TTL),必要时落库
  • 记忆分层:
    • 短期:本轮关键信息(订单号、意图)
    • 长期:用户偏好(需用户授权)
  • 合规:敏感信息加密/脱敏;权限校验贯穿工具调用

你可以怎么"补强小Y"

  • Outbox + 幂等消费 写成一套 demo
  • 用 Resilience4j 给 Feign 做一套"超时+熔断+降级"
  • 给订单接口接上 Micrometer 指标 + traceId 日志
  • 用 Spring AI 做一个最小 RAG:文档切分 → 向量库 → 检索 → 引用回答

(全文完)

相关推荐
罗不俷12 小时前
从零搭建 Mac Java 开发环境:Homebrew + JDK + Maven + Git 全流程配置
java
折哥的程序人生 · 物流技术专研12 小时前
Java 23 种设计模式:从踩坑到精通 —— 开篇及系列介绍
java·开发语言·后端·设计模式·面试·架构
_日拱一卒12 小时前
LeetCode:124二叉树中的最大路径和
java·数据结构·算法
ch.ju12 小时前
Java程序设计(第3版)第四章——构造方法
java·开发语言
程序员三明治12 小时前
【AI】Tika:一次文档解析引擎的工程实践
java·人工智能·大模型·llm·后端开发·rag·tika文件解析
阿维的博客日记12 小时前
Spring Boot 里怎么统计接口参数和耗时并打印日志
java·spring boot·后端
fengxin_rou12 小时前
【Spring Boot 认证登录注册模块全解析】:JWT+BCrypt+Redis 企业级实践
spring boot·redis·后端
存在的五月雨12 小时前
JVM线程泄漏 问题记录
jvm
基哥的奋斗历程12 小时前
Maven install Java.lang.StackOverflowError
java·开发语言·maven