从电商到AI智能客服:一场大厂Java面试里的微服务、Redis、Kafka与RAG实战解析
从电商到AI智能客服:一场“水货程序员”硬刚大厂 Java 面试的完整实录
场景设定:某互联网大厂,主营电商 + 内容社区 + AI 智能客服。面试官老王以严肃著称,候选人小Y号称“什么都会一点”。
技术栈覆盖:Java、Spring Boot、微服务、Redis、Kafka、MySQL、Elasticsearch、Docker/K8s、RAG 智能客服等。
文章结构:
- 面试现场 3 轮问答对话(有剧情、有反转)
- 末尾给出所有问题的标准解读 + 场景分析,小白也能看懂。
一、第一轮:基础电商下单场景(Java & Spring & 数据库基础)
业务背景:
公司有一个典型电商业务:用户在内容社区刷到带货短视频,点击跳转电商详情页,下单购买。你要负责下单服务的开发与优化。
第 1 轮 · 第 1 题:Java 基础与集合
面试官: 我们先简单点。电商下单页面会展示用户最近浏览的 10 个商品,你会怎么在 Java 里存这类“有序且可能去重”的数据?说说你常用的集合,以及选型考虑。
小Y:
这个简单!我就用 ArrayList,大家都用这个,性能也很好。要去重的话,我就new HashSet(list)一下,反正都挺快的。
面试官(点点头):
ArrayList 能说出来还行。但你考虑过有序 + 去重同时满足吗?以及电商高并发场景下线程安全呢?待会儿我们展开。
第 1 轮 · 第 2 题:Spring Boot 与 MVC 设计
面试官: 下单时,你会设计哪些 Spring MVC 层级?简单描述 Controller、Service、Repository 的职责划分,并举个接口的例子。
小Y: 嗯……我一般就写一个 Controller,然后里面把业务逻辑写完,再调一下 Mapper。Service 层嘛,有时候就省了,更清爽。比如:
@RestController
public class OrderController {
@PostMapping("/order")
public String createOrder(@RequestBody OrderRequest req) {
// 校验参数
// 调用 mapper 插入数据库
// 调用库存接口
// 返回成功
return "ok";
}
}
反正能跑就行,我之前项目都这么写的。
面试官(皱眉): 能跑和能维护是两回事。后面我们会聊到复杂业务下的分层演进。
第 1 轮 · 第 3 题:事务与库存一致性
面试官: 用户下单时要同时写订单表、扣减库存。如果订单插入成功了,库存扣减失败,你怎么保证一致性?说说你了解的解决方案。
小Y:
这个嘛……我就用 Spring 的 @Transactional,把两个操作放一起,失败就回滚,应该就没问题吧。分布式的……我们可以重试一下?
面试官:
本地事务说得还行,但在订单服务和库存服务分离、走 RPC 或消息队列的时候,@Transactional 就不够了。这个问题我们放到后面一轮细聊。
第 1 轮 · 第 4 题:数据库与索引
面试官:
订单量大了以后,我们查用户历史订单经常会慢。订单表字段有:id, user_id, status, create_time, amount ...,你会怎么建索引?为什么?
小Y:
我就给所有字段都建个索引,这样查什么都快。像user_id、status、create_time都可以来一个单列索引,保险。
面试官(忍住笑): “全加索引”是很多初级同学的通病。索引不是越多越好,待会儿我给你讲一个经典的订单查询索引设计。
第 1 轮 · 第 5 题:简单缓存应用
面试官: 热门商品详情页访问量很大,你会怎么用 Redis 减轻数据库压力?简单说说思路。
小Y:
嗯,这个我熟,我就把商品信息用 String 存在 Redis 里,key 就是 product:id,查不到再去 MySQL 查一下然后塞回 Redis。TTL 的话就先不管了,慢慢再调。
面试官(微微点头): 思路有了,但还缺一些细节,比如缓存击穿、雪崩、穿透之类。我们后面接着问。
二、第二轮:高并发与微服务拆分(缓存、MQ、微服务、监控)
第一轮勉强过关,老王决定从“能跑”升级到“能扛”,考察高并发与微服务。
第 2 轮 · 第 1 题:缓存击穿、雪崩、穿透
面试官: 刚才提到 Redis 缓存商品详情。现在有 3 个问题:缓存穿透、缓存击穿、缓存雪崩,分别是什么?你会怎么解决?
小Y(开始发虚): 呃……穿透就是……穿过去了?击穿就是打穿了?雪崩就是都崩了?
解决嘛,我一般就是……多加几个 if 判断,然后 Redis 掉了就重启一下。具体名字我记不太清……
面试官(叹气): 名词不重要,但场景要清楚。等会儿我给你用电商商品、库存的例子讲一下。
第 2 轮 · 第 2 题:使用消息队列削峰填谷
面试官: 双十一大促,上新秒杀活动,QPS 冲到几万。我们打算用 Kafka 来削峰填谷。你会把 Kafka 放在下单链路的哪个位置?起到什么作用?
小Y: Kafka 我知道,就是一个更高级的队列。我就把所有请求丢进 Kafka,然后消费者慢慢消费,这样就不会挂。具体分区、消费组啥的,我……还在学。
面试官: 大方向对,但你得知道哪里能异步,哪里必须同步返回,否则用户体验会很差。
第 2 轮 · 第 3 题:微服务拆分与 Spring Cloud
面试官: 我们现在把系统拆成:用户服务、商品服务、订单服务、库存服务、支付服务等。用 Spring Cloud 来做服务治理。你知道哪些组件?注册发现、负载均衡、熔断限流会怎么做?
小Y:
Spring Cloud 有 Eureka、Zuul……然后还有 Ribbon?好像现在都不用了。我们可以用 Nacos 吧?熔断就用 Hystrix,不过听说也过时了。我不是很确定,反正我会 @LoadBalanced 和 @FeignClient。
面试官(勉强认可): 能叫出几个名词还不错,但你得知道现在主流用什么,比如 Spring Cloud + OpenFeign + Resilience4j 等组合。
第 2 轮 · 第 4 题:监控与链路追踪
面试官: 微服务拆了之后,调用链变复杂。我们在用 Prometheus + Grafana + Zipkin 做监控与链路追踪。你会埋哪些指标?哪些场景用到分布式追踪?
小Y:
指标嘛,就 CPU、内存、QPS、RT 这些……链路追踪,我之前就是加些日志,System.out.println 看看。Zipkin 之类的我没怎么用过,听说挺酷的。
面试官: 日志不是不行,但规模一大就不够了。这个问题等会儿一起讲给你听。
第 2 轮 · 第 5 题:分布式事务与最终一致性
面试官: 回到刚才的订单+库存。现在订单服务、库存服务、支付服务都拆成独立微服务,通过 Kafka/Feign 通信。你会怎么保证最终一致性?说说你知道的方案,比如本地消息表、事务消息、TCC、可靠事件等。
小Y(开始胡扯): 我觉得可以……大家约定好失败就互相回滚?或者写一个超级大的分布式事务,把所有服务都包进来,一起提交一起回滚。至于 TCC,好像是 Try Catch Cancel?
面试官(扶额): 行,起码知道有 TCC。待会儿我会用一个**“订单超卖和库存回滚”**的例子讲一下几种方案的区别。
三、第三轮:AI 智能客服与 RAG 场景(向量检索、Agent、风控)
老王发现小Y只能算“能写业务”的水平,于是决定考察公司的重点项目:AI 智能客服 + 风控,用到了 Spring AI、RAG、向量数据库、Agent 工作流等。
第 3 轮 · 第 1 题:AI 智能客服整体架构
面试官: 我们有一个 AI 智能客服系统,支持:
- 订单查询
- 售后进度查询
- 商品知识问答
- 物流问题
技术栈包括:Spring Boot、Spring AI、向量数据库(Milvus/Redis)、RAG、Agent 工作流。你能描述一下整体架构和调用链吗?
小Y(开始飘): 这个我经常用 ChatGPT,差不多知道。就是前端发问题到后端,后端丢给大模型,大模型就回答。至于向量数据库嘛,就是一个更快的数据库;RAG 就是……随机增强?Agent 就是智能一点的服务?
大概流程就是:
- 用户提问
- 后端转发到大模型
- 返回答案
挺简单的。
面试官(表情凝固): ……你这个是“纯大模型聊天”,还不算企业级的 AI 系统。我们要可控、可追踪、能接业务系统的 AI,不是个聊天机器人而已。
第 3 轮 · 第 2 题:RAG 与向量化检索
面试官: 那说说你对 RAG(检索增强生成)的理解,以及向量数据库的作用。假设用户问“我这单什么时候送到?”我们怎么让模型基于真实物流数据来回答,而不是胡编?
小Y: RAG……我记得是从网络上检索一下再生成?向量数据库就是把文本变成数字,方便算相似度。具体实现的话,就是查一下物流表,然后拼接给大模型,让它别乱说。
面试官(勉强点头): 方向有点意思,但实际实现还有许多关键细节,比如向量化、召回、重排序、提示填充、减少幻觉等。
第 3 轮 · 第 3 题:多 Agent 工作流与工具调用
面试官: 我们的客服系统是 Agentic RAG 架构:
- 一个总控 Agent 负责理解用户意图
- 工具 Agent 负责调用订单服务、物流服务、支付服务
这些工具调用通过 HTTP/gRPC 封装成标准 Schema。你怎么设计这个“工具调用标准化”?
小Y: 这个……我觉得可以把每个服务都做成一个 REST 接口,然后给大模型一个文档,让它自己调?或者用 JSON 格式发一发。至于是啥标准,我就 JSON 标准吧。
面试官: 还停留在“文档驱动瞎调”的阶段。现在更常见的是设计一套统一的工具描述协议(类似 OpenAPI/JSON Schema,或者 MCP 模型上下文协议)。
第 3 轮 · 第 4 题:AI 幻觉与风控
面试官: AI 模型有幻觉(Hallucination)问题。比如,用户问“我这笔退款下来了没”,模型胡说“已经到账”,结果实际上没到账,可能会引发投诉甚至法律风险。你会怎么从架构或产品设计上降低这类风险?
小Y(开始乱答): 这个……我会在页面上加一句“仅供参考,以实际为准”。然后……让模型尽量诚实一点?或者给它更多数据?
面试官(冷笑): “免责声明”当然要,但还远远不够。你得在调用链和权限上做限制,关键结果必须来源于业务系统,而不是模型猜的。
第 3 轮 · 第 5 题:CI/CD、灰度发布与回滚
面试官: 最后一个问题。我们的 AI 客服系统频繁更新 Prompt、模型版本、召回策略,使用 Jenkins + GitLab CI + Docker + Kubernetes。你会怎么设计上线与灰度流程,避免一次策略修改导致全站崩溃?
小Y: 我一般就是打个镜像,发到 K8s,然后多开几台,出问题就手动回滚。灰度的话,可以让运维同学盯着,一旦有问题就切回去。具体流程文档我没写过……
面试官(深吸一口气): 好的,今天就到这里吧。
面试官总结: 你有一定 Java & Spring 基础,但在高并发、分布式事务、AI 架构上掌握得比较浅。回去可以好好补补,我等会儿把一些关键概念发你。回去等通知吧。
—— 面试结束
四、逐题详解:从零搭建电商 + AI 智能客服的技术体系
下面是给“后来人”的学习指南:
- 每个问题先讲业务场景
- 再讲技术方案与关键点
- 不要求你现在都会,但看完要知道“为什么要这样做”。
1. Java 集合选型:最近浏览的 10 个商品
业务场景:
- 在内容社区、UGC 电商里,用户经常浏览很多商品
- 页面要展示“最近浏览的 10 个商品”,有顺序、不能重复
- 有可能多线程并发写(比如埋点上报、异步任务)
技术要点:
- 有序 + 去重:
- 可以使用
LinkedHashSet:既保持插入顺序,又不重复 - 需要控制长度=10,可以在插入新元素后判断 size>10 时移除最旧
- 可以使用
- 多线程安全:
- Web 层面通常每个请求一个集合,不用特别同步
- 如果放在共享内存中,可使用
Collections.synchronizedSet或ConcurrentLinkedDeque + HashSet
- 为什么不能“所有都用 ArrayList + HashSet”?
ArrayList:有序但可以重复HashSet:去重但无序- 两者组合会让代码复杂,
LinkedHashSet更自然
示例:
Set<Long> recent = new LinkedHashSet<>();
public void viewProduct(Long productId) {
recent.remove(productId); // 保证去重后再插入
recent.add(productId);
if (recent.size() > 10) {
Iterator<Long> it = recent.iterator();
it.next();
it.remove(); // 移除最旧的
}
}
2. Spring MVC 规范分层:Controller / Service / Repository
业务场景:
- 下单流程涉及:参数校验、库存校验、优惠券、风控、持久化
- 如果把所有逻辑写在 Controller 中,后期需求变化(加优惠、改库存逻辑)会非常难维护
推荐分层:
-
Controller 层
- 职责:
- 接收 HTTP 请求
- 基础参数校验
- 调用业务服务
- 统一返回结果
- 职责:
-
Service 层
- 职责:
- 编排业务流程(订单创建、扣库存、发消息)
- 事务控制(
@Transactional) - 调用其他服务 / 领域对象
- 职责:
-
Repository / DAO 层
- 职责:
- 访问数据库(MyBatis、JPA、Spring Data 等)
- 职责:
示例结构:
@RestController
@RequestMapping("/api/order")
public class OrderController {
private final OrderService orderService;
@PostMapping
public OrderCreateResp create(@Valid @RequestBody OrderCreateReq req) {
return orderService.createOrder(req);
}
}
@Service
public class OrderService {
@Transactional
public OrderCreateResp createOrder(OrderCreateReq req) {
// 1. 校验商品和库存
// 2. 计算价格、优惠
// 3. 落库订单
// 4. 发送订单创建事件
}
}
@Repository
public interface OrderRepository {
void save(Order order);
}
3. 订单与库存的一致性问题
简单场景:同库同服务
- 订单表与库存表在同一个数据库里,由同一个服务操作
- 可以使用 Spring 的
@Transactional保证本地事务:- 任一步失败,整个事务回滚
@Transactional
public void createOrderAndDeductStock(...) {
orderRepository.save(order);
stockRepository.deduct(productId, count);
}
复杂场景:微服务拆分后的分布式事务
- 订单服务和库存服务分别有自己的数据库
- 通过 RPC(Dubbo/gRPC/OpenFeign)或消息队列(Kafka/RabbitMQ)通信
@Transactional无法跨服务
常见方案:
-
本地消息表 + 消息队列
- 在订单服务本地事务里同时写订单表 + 本地消息表
- 定时任务扫描本地消息表,发消息到 MQ
- 库存服务消费消息,扣减库存
- 失败可重试,保证最终一致
-
事务消息(如 RocketMQ 事务消息)
- 先发“半消息”,本地事务提交后再确认
-
TCC(Try-Confirm-Cancel)
- Try:预扣库存
- Confirm:订单创建成功后确认扣减
- Cancel:订单失败后释放库存
4. 订单查询的索引设计
业务场景:
- 用户中心查看“我的订单”列表:按
user_id+ 时间倒序分页 - 偶尔还按状态筛选(已支付、已完成)
索引设计:
-
主查询条件:user_id + create_time
- 组合索引:
(user_id, create_time desc) - 能高效支持:
where user_id=? order by create_time desc limit ?,?
- 组合索引:
-
加状态筛选
- 如果状态维度不多,可以放在组合索引中:
(user_id, status, create_time desc)
- 如果状态维度不多,可以放在组合索引中:
为什么不能“所有字段都建索引”?
- 写入放大:每次 insert/update 要维护所有索引
- 影响缓存命中与执行计划
- 要根据查询场景,设计少量高价值组合索引
5. Redis 缓存商品详情:击穿 / 雪崩 / 穿透
业务场景:
- 商品详情高频访问,要用 Redis 缓存
- 异常流量时如果都打到 DB,会打挂数据库
三大问题:
-
缓存穿透
- 请求的 key 在缓存和数据库中都不存在(比如恶意请求随机商品 ID)
- 每次都打 DB
- 解决:
- 对不存在的数据在 Redis 中缓存一个“空值”+短 TTL
- 对参数做基础校验(ID 格式检查)
- 使用布隆过滤器(Bloom Filter)提前拦截不存在的 key
-
缓存击穿
- 某个非常热点的 key 过期瞬间,大量请求同时打到 DB
- 解决:
- “互斥锁” + 单线程回源
- 或者“逻辑过期”+后台异步更新
-
缓存雪崩
- 大量 key 在同一时间集中过期
- 解决:
- 设置随机 TTL,错开过期时间
- 多级缓存(本地 + Redis)
6. Kafka 在秒杀活动中的位置:削峰填谷
业务场景:
- 用户秒杀下单:涉及库存扣减、订单记录、积分发放、消息通知
- 用户请求需要快速返回,不能等所有逻辑处理完
典型方案:
-
前台下单接口
- 同步校验基础条件(登录、库存是否大于 0、活动有效)
- 快速写入一条“秒杀请求事件”到 Kafka
- 立刻返回“排队中/下单受理中”给用户
-
后台消费者
- 消费 Kafka 事件
- 真正扣减库存、写订单、发优惠券等
这样:
- 高峰期请求压力集中在 Kafka 上,而不是数据库
- 削峰填谷:峰值请求排队慢慢处理
7. Spring Cloud 微服务组件与治理
常见组件:
- 注册中心:Eureka、Nacos、Consul
- 配置中心:Spring Cloud Config、Nacos Config
- 负载均衡:Ribbon(历史)、Spring Cloud LoadBalancer
- 网关:Spring Cloud Gateway(替代 Zuul)
- 远程调用:OpenFeign
- 熔断限流:Hystrix(历史)、Resilience4j、Sentinel
典型调用链:
order-service通过 OpenFeign 调用stock-service- 服务发现通过 Nacos/Eureka
- 接口超时、失败通过 Resilience4j 做熔断、重试、限流
8. 监控与链路追踪:Prometheus + Grafana + Zipkin
监控指标(Micrometer):
- QPS:接口每秒请求数
- RT:响应时间
- 错误率:5xx 比例
- JVM 指标:堆内存、GC 次数
- 业务指标:下单成功率、支付成功率
使用 Prometheus 抓取指标,Grafana 可视化告警。
分布式追踪(Zipkin/Jaeger):
- 每个请求一个 TraceId
- 经过多个服务(网关 → 用户服务 → 订单服务 → 库存服务)
- 在 Zipkin 里看到完整调用链、耗时分布
- 用于排查“哪个服务慢/失败”的问题
9. 分布式事务与最终一致性:订单 + 库存 + 支付
业务场景:
- 用户下单成功 → 扣减库存 → 支付完成 → 改订单状态
- 每步分布在不同微服务
常见技术组合:
-
可靠事件 + 本地事务
- 订单创建成功后写入本地消息表
- 定时发消息到 Kafka
- 库存、支付服务消费事件,自行保证幂等
-
幂等性设计:
- 每次扣减库存带上
biz_id(订单号) - 同一个
biz_id重复请求只处理一次
- 每次扣减库存带上
-
失败补偿:
- 定期扫“长时间未完成”的订单
- 尝试补偿或关闭订单、回补库存
10. AI 智能客服整体架构(Spring AI + RAG)
业务场景:
- 用户可以用自然语言询问:订单、物流、退换货、商品咨询
- 希望机器人回答专业、基于最新业务数据,避免瞎说
典型架构:
-
客户端(小程序 / H5 / APP)
- WebSocket/HTTP 与后端对话
-
对话服务(Spring Boot + Spring AI)
- 接收用户问题
- 调用 意图识别 Agent(或分类器)
-
RAG 检索层
- 使用 Embedding 模型将 FAQ、文档、商品说明向量化
- 存入向量数据库:Milvus / Chroma / Redis Vector
- 根据用户问题做语义检索,取回相关文档片段
-
业务工具调用层
- 封装订单查询、物流查询、退款状态等为标准工具
- 工具接口可用 REST/gRPC,并用统一 Schema 暴露给模型
-
大模型层
- 调用 OpenAI / Ollama 等模型
- 通过“提示填充”,将检索结果 + 业务系统返回拼进 Prompt
-
结果后处理
- 检查是否涉及金额、风控等敏感问题,如是则必须以业务系统返回为准
11. RAG 细节:向量化、检索与减少幻觉
RAG 核心流程:
-
向量化(Embedding)
- 将文档分段,调用 Embedding 模型(如 text-embedding-3-large)变成向量
- 存入向量数据库(Milvus/Chroma/Redis)
-
查询检索
- 用户问题向量化
- 与向量库做相似度检索(cosine、dot-product)
- 返回最相关的文档片段
-
提示填充(Prompting)
- 将检索到的文档塞入 Prompt:
- “你必须严格根据以下文档回答,不知道就说不知道。”
-
结合业务系统数据
- 对于“订单状态”“是否到账”这类问题:
- 先通过工具调用真实业务系统
- 将结果写入 Prompt
- 限制模型不能自行推断
- 对于“订单状态”“是否到账”这类问题:
这样可以显著减少幻觉,提高可信度。
12. Agentic RAG 与工具调用标准化(MCP 思路)
需求:
- 让 AI 能调用多个后端服务(订单、物流、支付、风控),但调用方式要统一、可控
工具调用标准化思路:
-
工具描述 Schema
- 类似 OpenAPI/JSON Schema:
{ "name": "getOrderStatus", "description": "查询订单状态", "input_schema": { "type": "object", "properties": { "order_id": {"type": "string"} }, "required": ["order_id"] } } -
统一协议(如 MCP 模型上下文协议)
- 所有工具都按统一协议暴露端点
- Agent 只需根据协议调用,不关心底层实现
-
多 Agent 工作流
- 总控 Agent:负责理解意图、规划调用顺序
- 工具 Agent:负责实际调用具体服务
13. AI 幻觉与风控策略
典型风险场景:
- 退款到账、优惠券金额、支付结果等敏感问题
- 模型胡说会引发资金与法律风险
治理方式:
-
强制数据来源
- 与金额、账号相关的回答必须来自业务系统
- 模型只负责“解释”结果,不负责“生成”金额
-
规则与策略引擎
- 在模型回复前做规则校验
- 比如:金额不得大于业务系统返回金额
-
敏感问题转人工
- 遇到高风险场景(大额退款、投诉纠纷)强制转人工客服
-
显式提示 + 审计日志
- 页面提示“以下为智能客服回答,重要信息以订单详情为准”
- 记录全部对话,便于事后追责
14. CI/CD、灰度与回滚:Jenkins + Docker + K8s
流程示例:
-
Git 提交 → GitLab CI/Jenkins 构建
- 单元测试(JUnit5 + Mockito)
- 接口测试(REST Assured)
- 构建 Docker 镜像
-
推送镜像到仓库
-
Kubernetes 部署(Helm/ArgoCD)
- 使用 Deployment 滚动更新
- 配置探针(liveness/readiness)
-
灰度发布
- 先将 5% 流量导到新版本
- 通过 Prometheus + Grafana 监控错误率/RT
- 无异常逐步扩大流量
-
快速回滚
- K8s 支持
rollback到上一版本 - Prompt 策略可以做配置中心热更新(无需重启服务)
- K8s 支持
五、写在最后
这场面试里,小Y 能答对的,往往是“能跑”的那部分;答不清的,基本都是“能扛、能扩展、能治理”的那一层。
无论是传统电商还是今天的 AI 智能客服系统,本质上都是:
- 从业务场景出发(下单、支付、物流、咨询)
- 用合适的技术(Java、Spring Boot、微服务、Redis、MQ、RAG、Agent)
- 构建一个 可用、可靠、可演进 的系统。
如果你也在准备大厂 Java 面试,可以按本文的问题列表,自测一遍:
- 哪些题你能像小Y一样“能写业务”?
- 哪些题你能比小Y回答得更清楚、更体系化?
差距在哪,复习就从哪开始。
更多推荐



所有评论(0)