大厂Java面试实录:Spring Boot微服务 + Redis缓存 + Kafka消息队列 + Prometheus链路追踪 + RAG向量检索

大厂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 视角排查偶发延迟

怎么做(推荐排查路径)

  1. 看应用指标:P95/P99 延迟、线程池队列长度、拒绝次数
  2. 看 GC
    • Java 8:CMS/G1
    • Java 11/17:G1/ZGC(按需求)
    • 指标:GC pause time、allocation rate、old gen 占用
  3. 线程栈jstack 看是否线程阻塞(锁、IO、死锁)
  4. 火焰图:async-profiler 定位 CPU 热点
  5. 下游依赖:DB 慢查询、Redis 超时、Feign 调用超时

为什么 延迟通常来自:GC pause、锁竞争、线程池耗尽、下游抖动。

常见坑

  • 只盯 CPU/内存,不看 P99 和线程池。
  • 看到 Full GC 就盲目加内存,结果 OldGen 更大,GC 更慢。

3)HikariCP 参数估算

核心公式(粗略指导)

  • maxPoolSize 不是越大越好,应接近:
    • 数据库可承受并发连接数(全站总和)
    • 单实例并发请求中"需要DB连接"的比例
  • 建议从压测出发:
    1. 统计接口的 DB 时间占比
    2. 以目标 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)库存扣减与防超卖

常见方案

  1. 数据库扣减(强一致)
    • SQL:update sku set stock=stock-1 where id=? and stock>0
    • 优点:强一致
    • 缺点:高并发下 DB 压力大
  2. Redis 预扣 + 异步落库(高性能)
    • Redis DECR 预扣(原子)
    • 下单成功后异步写库
    • 需要:失败回滚、对账、最终一致

关键工程点

  • 幂等:同一订单重复扣减必须无副作用
  • 失败回滚:支付失败/超时取消要把库存加回
  • 对账补偿:Redis 与 DB 不一致要定期校准

常见坑

  • 只做 Redis decr 不做回滚,库存会"越卖越少"。

7)Kafka/RabbitMQ 异步发券:消息设计与幂等

topic/queue 拆分

  • 按业务域:order-eventscoupon-eventsnotify-events
  • 按事件类型:order.createdorder.paid(可用不同 topic 或同 topic 不同 key)

消息体建议

  • eventId(全局唯一,强烈建议)
  • orderIduserId
  • eventTypeoccurredAt
  • 必要时加 traceId

至少一次投递下的幂等

  • 消费端落库前先做幂等判断:
    • 建唯一键:eventIdorderId+eventType
    • 插入成功才执行发券;重复插入冲突则直接 ack
  • 或 Redis setnx 做去重(但要考虑过期与可靠性)

常见坑

  • 只靠"业务上判断一下"但没有唯一约束,重复消费必然出错。

8)OpenFeign + Resilience4j:超时/重试/熔断/隔离

超时

  • Feign/HTTP 客户端设连接超时、读超时(如 200ms/500ms 级别)

重试

  • 仅对"幂等请求"重试(GET、可重试的查询)
  • 对"创建订单/扣库存"这类非幂等操作要谨慎:
    • 必须有幂等 token/业务唯一键

熔断(Circuit Breaker)

  • 下游错误率或超时率超过阈值→短路一段时间
  • 配合降级:返回兜底数据/静态配置

隔离(Bulkhead)

  • 线程池隔离/信号量隔离,避免一个下游拖垮全局线程

常见坑

  • 无脑重试把下游打死(雪上加霜)。

第三轮答案(可观测性 + RAG)

9)Micrometer + Prometheus/Grafana:定位"支付成功但订单未更新"

你要看的不仅是资源,而是"业务链路指标"

  • 订单服务:
    • order_create_totalorder_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)

  1. 文档加载:从 Markdown/PDF/网页/数据库加载(Document Loader)
  2. 切分:按段落/标题切 chunk(避免超上下文)
  3. 向量化(Embedding)
    • OpenAI embedding 或 Ollama 本地模型
  4. 写入向量库:Milvus/Chroma/Redis Vector
  5. 检索:根据 query 做相似度搜索 topK
  6. 提示填充(Prompt Template)
    • 把"检索到的片段 + 用户问题 + 约束规则"组合
  7. 生成回答:调用 LLM

向量库选型考量

  • Milvus:大规模、性能强、运维成本较高
  • Chroma:轻量、适合原型和中小规模
  • Redis Vector:如果你已经重度 Redis,运维简单;但要关注容量/成本

常见坑

  • 不切分直接塞全文→token 爆炸、回答飘、成本高。

12)降低 Hallucination(幻觉)的工程化手段

检索增强

  • 强制"只基于检索内容回答":prompt 加约束 + 系统消息
  • 提升检索质量:
    • 更合理切分(chunk size、overlap)
    • 元数据过滤(类目/地区/时间)
    • rerank(重排序)

引用与可解释

  • 回答中输出引用片段/文档链接/条款编号
  • UI 展示"依据来源"

置信度与兜底

  • 相似度阈值:低于阈值→返回"未检索到依据,建议转人工"
  • 关键问题(退款/赔付)强制人工确认

观测与回放

  • 记录:query、检索结果、prompt、模型输出、用户反馈
  • 用于持续评估与修正

常见坑

  • 只靠一句"不要编造"→模型仍会编,只是编得更委婉。

建议学习路线(按本文出现的技术点串起来)

  1. Spring Boot + MVC 分层、DTO/VO/Entity
  2. HikariCP + MySQL 慢查询与连接池指标
  3. Redis 缓存与一致性、Spring Cache
  4. Kafka/RabbitMQ:事件设计、幂等、消费重试
  5. Resilience4j:超时/重试/熔断/隔离
  6. Micrometer + Prometheus/Grafana + Jaeger/Zipkin + ELK
  7. Spring AI:RAG、Embedding、向量库、反幻觉工程
相关推荐
phltxy1 小时前
RabbitMQ 入门与安装
分布式·rabbitmq
Oj92q85H51 小时前
如何在Dev-C++中设置TDM-GCC为默认编译器
java·jvm·c++
逸Y 仙X1 小时前
文章二:Elasticsearch跨集群能力探查
java·大数据·服务器·elasticsearch·搜索引擎·全文检索
阿标在干嘛1 小时前
政策快报如何让推荐准确率从8%提升到16%?画像系统实践
java·大数据·人工智能
Miss roro1 小时前
企业合同管理系统选型的核心维度:功能完整性、协作效率与安全合规
java·数据库·安全·法律科技
JAVA社区1 小时前
Java进阶全套教程(一)—— 数据框架Mybatis详解
java·开发语言·面试·职场和发展·mybatis
lzp07911 小时前
C#如何优雅处理引用类型的深拷贝(贰)
spring boot·后端·ui
JAVA学习通1 小时前
《大营销平台系统设计实现》 - 营销服务 第10节:不超卖库存规则实现
java·数据库·oracle·责任链模式·codex
阿坤带你走近大数据1 小时前
Kafka的基本概念,基本用法及常见使用场景
分布式·kafka