大厂Java面试实录:Spring Boot微服务 + Redis缓存 + Kafka消息队列 + Prometheus链路追踪 + RAG向量检索
故事背景
某互联网大厂(电商+内容社区一体化)Java后端岗面试。
- 面试官(M):严肃、节奏快、喜欢循序渐进。
- 小Y(Y):自称"全栈",实则水货但基础题能答,复杂题开始飘。
业务背景:
- 电商首页推荐/活动页有高并发读;
- 下单后需要异步处理(扣库存、发券、消息推送);
- 平台要做可观测性(监控、链路追踪、日志);
- 新增"智能客服/导购",希望基于企业文档做RAG问答。
第一轮:从Java与Spring Boot基础切入(电商推荐接口)
Q1(M)
电商首页 /feed 推荐接口用 Spring Boot + Spring MVC 做,你会怎么设计 Controller 到 Service 的分层?DTO/VO 怎么放?
A1(Y) 就...Controller 调 Service,Service 调 Mapper,然后 VO 就直接返回嘛,DTO也差不多。分层就那几层。
M点评 基础方向对了,但"DTO/VO差不多"这句很危险。继续。
Q2(M)
你们推荐接口要保证响应快。JVM 层面你怎么排查"偶发 1s+ 延迟"?用到哪些工具/指标?
A2(Y) 我一般看看 GC 日志,有问题就加内存。或者重启一下也会好。
M点评 看 GC 日志是对的,但"重启解决一切"属于玄学。我们继续往可观测性走。
Q3(M)
你用 HikariCP 连接 MySQL。连接池关键参数(最大连接数、最小空闲、连接超时)怎么根据业务估算?
A3(Y) 我一般就默认,最多把 max 调大一点,比如 200。调大总没错。
M点评 "调大总没错"是经典事故开端。下一题。
Q4(M)
你会用 JUnit 5 + Mockito 给 /feed 相关的 Service 写单测吗?Mock 哪些依赖?怎么验证边界?
A4(Y) 会的会的,@Test 写一下,Mockito when...thenReturn。边界就...多测几组数据。
M点评 方向可以。第一轮到这。
第二轮:缓存、消息队列与微服务治理(下单链路)
场景升级:用户点击"立即购买",下单成功后需要:
- 同步:创建订单、冻结库存
- 异步:发券、发站内信、推荐系统更新特征
Q1(M)
下单接口你会如何用 Redis + Spring Cache 做缓存?哪些数据适合缓存,哪些坚决不缓存?
A1(Y) 订单应该也能缓存吧?反正 Redis 很快。实在不行加个过期时间。
M点评 订单状态强一致,乱缓存会出事故。我们接着问一致性。
Q2(M)
库存扣减你会怎么做?
- 数据库扣?
- Redis 预扣?
- 怎么防止超卖?
A2(Y) 我觉得用 Redis incr/decr 就可以了,原子嘛。然后定时同步到数据库。
M点评 思路之一,但细节很多:回滚、对账、幂等。你说得还不够落地。
Q3(M)
下单后异步发券,你用 Kafka/RabbitMQ 都行。说说你如何设计消息:
- topic/queue 怎么拆
- 消息体放什么
- 如何保证"至少一次"下游不重复发券(幂等)
A3(Y) topic 就叫 coupon-topic。消息体放 userId 和订单号。重复就...业务上判断一下?
M点评 能想到"判断一下"说明你知道幂等,但要讲清楚怎么判断。
Q4(M)
你们是微服务:订单、库存、优惠券、推荐。 用 Spring Cloud / OpenFeign / Resilience4j,你怎么做超时、重试、熔断、隔离?
A4(Y) Feign 调用失败就重试几次,不行就返回失败。熔断就是...断掉?
M点评 好的,第二轮结束,你的"概念还行,细节薄"。最后一轮上难度。
第三轮:可观测性 + AI工程化RAG(智能客服)
场景再次升级:平台新增"智能客服/导购"。
- 数据来源:售后政策、商品说明、物流规则、活动规则(企业文档)
- 目标:问答要可追踪、可解释、低幻觉
Q1(M)
线上出现"支付成功但订单未更新"投诉。 你如何用 Micrometer + Prometheus/Grafana 定位?你会打哪些关键指标?
A1(Y) 我会看 CPU、内存、QPS,可能就是机器扛不住。
M点评 这些是基础盘,但要结合业务指标和依赖指标。下一题链路追踪。
Q2(M)
跨服务调用链(订单→库存→券→消息)你用 Jaeger/Zipkin 怎么串起来?traceId 从哪来?日志怎么关联(SLF4J + Logback)?
A2(Y) traceId 应该框架会生成吧。日志里打印一下就能关联。
M点评 "框架会生成"也对,但你得知道在哪一层生成、怎么透传、怎么进 MDC。
Q3(M)
智能客服要做 RAG :文档加载→切分→向量化→向量库检索→提示填充→LLM回答。 说说你会怎么用 Spring AI 做一条最小可用链路?向量库你选 Milvus/Chroma/Redis 的考量?
A3(Y) RAG 就是把文档丢给大模型问就行了吧?向量库随便选一个,Milvus 听起来更大厂。
M点评 这就开始飘了:直接丢文档会超上下文且成本爆炸,还会更幻觉。
Q4(M)
如果客服回答"编造政策"(Hallucination)导致客诉,你如何工程化降低幻觉?
- 检索
- 引用
- 置信度
- 兜底
A4(Y) 让它"不要编"就行,在 prompt 里写清楚。实在不行人工审核。
M点评 Prompt 是手段之一,但不足以作为唯一方案。
结束语(M)
今天先到这。你基础还可以,但系统性和工程细节需要再打磨。 回去等通知吧,有结果我们 HR 会联系你。
文末:12道题详细答案与学习要点(按业务场景讲清楚)
说明:下面按"电商推荐→下单链路→智能客服RAG"的业务推进,把每题拆成:怎么做 + 为什么 + 常见坑,尽量让小白能照着复现。
第一轮答案(推荐接口)
1)Spring Boot 分层 + DTO/VO
怎么做
- Controller(接口层) :只做协议适配
- 接收
RequestDTO(参数校验:@Valid) - 返回
ResponseVO(面向前端展示)
- 接收
- Service(领域/业务层) :编排业务
- 聚合多数据源:商品、活动、库存、推荐
- 做降级、缓存、限流等策略
- Repository/DAO(数据访问层) :
- 用 MyBatis/JPA/Hibernate 访问数据库
- 尽量不把 ORM 实体直接暴露给上层
DTO/VO/Entity 的区别
- DTO:接口输入/输出的传输对象(可含校验、兼容字段)
- VO:返回给前端的展示对象(字段更"产品化")
- Entity/PO:持久化对象(表结构映射)
常见坑
- 直接把 Entity 当 VO 返回:字段泄露、耦合数据库、改表影响接口。
2)JVM 视角排查偶发延迟
怎么做(推荐排查路径)
- 看应用指标:P95/P99 延迟、线程池队列长度、拒绝次数
- 看 GC :
- Java 8:CMS/G1
- Java 11/17:G1/ZGC(按需求)
- 指标:GC pause time、allocation rate、old gen 占用
- 线程栈 :
jstack看是否线程阻塞(锁、IO、死锁) - 火焰图:async-profiler 定位 CPU 热点
- 下游依赖:DB 慢查询、Redis 超时、Feign 调用超时
为什么 延迟通常来自:GC pause、锁竞争、线程池耗尽、下游抖动。
常见坑
- 只盯 CPU/内存,不看 P99 和线程池。
- 看到 Full GC 就盲目加内存,结果 OldGen 更大,GC 更慢。
3)HikariCP 参数估算
核心公式(粗略指导)
maxPoolSize不是越大越好,应接近:- 数据库可承受并发连接数(全站总和)
- 单实例并发请求中"需要DB连接"的比例
- 建议从压测出发:
- 统计接口的 DB 时间占比
- 以目标 QPS 推算连接需求
建议值(经验起点)
maximumPoolSize:10~50 起步,结合实例数与数据库限制minimumIdle:接近常态并发即可connectionTimeout:一般 250ms~1s(不要无限等)
为什么 连接过多会让数据库上下文切换、锁竞争加剧,整体吞吐下降。
常见坑
- 盲目把池子调到 200/500,数据库先崩。
4)JUnit 5 + Mockito 单测 Service
怎么做
- 测 Service:Mock DAO/外部RPC(Feign)、Redis、消息发送器
- 关注:
- 正常路径
- 参数非法
- 下游异常(超时/返回空)
- 边界:空列表、极大分页、重复请求
例子(伪代码)
when(productRepo.find...).thenReturn(...)verify(recoClient).query(...)验证调用次数
常见坑
- 单测里启动全量 Spring 容器导致慢:
- 业务逻辑用纯 Mockito
- 集成测试再用
@SpringBootTest
第二轮答案(下单链路)
5)Redis + Spring Cache:哪些能缓存,哪些别碰
适合缓存
- 商品详情、类目树、活动规则(读多写少)
- 推荐结果(短 TTL)
- 库存"展示用"数值(允许稍旧)
不建议缓存/谨慎缓存
- 订单状态、支付状态(强一致、频繁变化)
- 账户余额、优惠券核销结果(资金/权益类)
正确姿势
@Cacheable缓存读多写少数据@CacheEvict在更新后主动失效- 引入"缓存穿透/击穿/雪崩"治理:
- 穿透:布隆过滤器/空值缓存
- 击穿:热点 key 互斥锁/逻辑过期
- 雪崩:TTL 随机抖动、分批预热
6)库存扣减与防超卖
常见方案
- 数据库扣减(强一致)
- SQL:
update sku set stock=stock-1 where id=? and stock>0 - 优点:强一致
- 缺点:高并发下 DB 压力大
- SQL:
- Redis 预扣 + 异步落库(高性能)
- Redis
DECR预扣(原子) - 下单成功后异步写库
- 需要:失败回滚、对账、最终一致
- Redis
关键工程点
- 幂等:同一订单重复扣减必须无副作用
- 失败回滚:支付失败/超时取消要把库存加回
- 对账补偿:Redis 与 DB 不一致要定期校准
常见坑
- 只做 Redis decr 不做回滚,库存会"越卖越少"。
7)Kafka/RabbitMQ 异步发券:消息设计与幂等
topic/queue 拆分
- 按业务域:
order-events、coupon-events、notify-events - 按事件类型:
order.created、order.paid(可用不同 topic 或同 topic 不同 key)
消息体建议
eventId(全局唯一,强烈建议)orderId、userIdeventType、occurredAt- 必要时加
traceId
至少一次投递下的幂等
- 消费端落库前先做幂等判断:
- 建唯一键:
eventId或orderId+eventType - 插入成功才执行发券;重复插入冲突则直接 ack
- 建唯一键:
- 或 Redis setnx 做去重(但要考虑过期与可靠性)
常见坑
- 只靠"业务上判断一下"但没有唯一约束,重复消费必然出错。
8)OpenFeign + Resilience4j:超时/重试/熔断/隔离
超时
- Feign/HTTP 客户端设连接超时、读超时(如 200ms/500ms 级别)
重试
- 仅对"幂等请求"重试(GET、可重试的查询)
- 对"创建订单/扣库存"这类非幂等操作要谨慎:
- 必须有幂等 token/业务唯一键
熔断(Circuit Breaker)
- 下游错误率或超时率超过阈值→短路一段时间
- 配合降级:返回兜底数据/静态配置
隔离(Bulkhead)
- 线程池隔离/信号量隔离,避免一个下游拖垮全局线程
常见坑
- 无脑重试把下游打死(雪上加霜)。
第三轮答案(可观测性 + RAG)
9)Micrometer + Prometheus/Grafana:定位"支付成功但订单未更新"
你要看的不仅是资源,而是"业务链路指标"
- 订单服务:
order_create_total、order_update_fail_total- 更新订单状态耗时直方图(P95/P99)
- 支付回调:
- 回调 QPS、失败率、签名验证失败数
- 消息队列:
- 消费 lag、重试次数、死信队列数量
- 数据库:
- 慢查询数、连接池等待时间(Hikari 指标)
定位思路
- 若支付已成功但订单没更新:
- 查"支付回调是否到达"
- 查"更新订单的消费者是否堆积/失败"
- 查"订单表更新是否被锁/慢SQL"
10)Jaeger/Zipkin 链路追踪 + 日志关联
traceId 从哪来
- 通常在网关/入口(Spring Cloud Gateway / Nginx sidecar / 应用入口 filter)生成
- 通过 HTTP Header 透传(如 W3C Trace Context:
traceparent)
怎么串起来
- 服务间调用(Feign/RestTemplate/WebClient)自动注入/传递 trace context
- MQ 场景:发送消息时把 traceId 写入 header,消费时继续创建 span
日志关联(SLF4J + Logback)
- 把 traceId 放入 MDC:
MDC.put("traceId", ...) - log pattern 增加
%X{traceId} - ELK 中按 traceId 聚合检索
常见坑
- 只在入口生成 traceId,但异步线程/MQ 消费没透传,链路断。
11)Spring AI 实现最小可用 RAG 链路
最小链路(MVP)
- 文档加载:从 Markdown/PDF/网页/数据库加载(Document Loader)
- 切分:按段落/标题切 chunk(避免超上下文)
- 向量化(Embedding) :
- OpenAI embedding 或 Ollama 本地模型
- 写入向量库:Milvus/Chroma/Redis Vector
- 检索:根据 query 做相似度搜索 topK
- 提示填充(Prompt Template) :
- 把"检索到的片段 + 用户问题 + 约束规则"组合
- 生成回答:调用 LLM
向量库选型考量
- Milvus:大规模、性能强、运维成本较高
- Chroma:轻量、适合原型和中小规模
- Redis Vector:如果你已经重度 Redis,运维简单;但要关注容量/成本
常见坑
- 不切分直接塞全文→token 爆炸、回答飘、成本高。
12)降低 Hallucination(幻觉)的工程化手段
检索增强
- 强制"只基于检索内容回答":prompt 加约束 + 系统消息
- 提升检索质量:
- 更合理切分(chunk size、overlap)
- 元数据过滤(类目/地区/时间)
- rerank(重排序)
引用与可解释
- 回答中输出引用片段/文档链接/条款编号
- UI 展示"依据来源"
置信度与兜底
- 相似度阈值:低于阈值→返回"未检索到依据,建议转人工"
- 关键问题(退款/赔付)强制人工确认
观测与回放
- 记录:query、检索结果、prompt、模型输出、用户反馈
- 用于持续评估与修正
常见坑
- 只靠一句"不要编造"→模型仍会编,只是编得更委婉。
建议学习路线(按本文出现的技术点串起来)
- Spring Boot + MVC 分层、DTO/VO/Entity
- HikariCP + MySQL 慢查询与连接池指标
- Redis 缓存与一致性、Spring Cache
- Kafka/RabbitMQ:事件设计、幂等、消费重试
- Resilience4j:超时/重试/熔断/隔离
- Micrometer + Prometheus/Grafana + Jaeger/Zipkin + ELK
- Spring AI:RAG、Embedding、向量库、反幻觉工程