大厂Java面试实录:Spring Boot/Cloud、JVM、Redis、Kafka、MyBatis 到 RAG/Agent 的三轮连环问(含答案详解)
场景:某互联网大厂「内容社区 + 音视频 + AIGC 智能客服」业务线。
角色:严肃面试官(M),搞笑水货程序员小Y(Y)。
规则:三轮面试,每轮 3~5 题,问题有业务衔接、逐步加深。
第一轮:内容社区 + 音视频上传(基础能力核验)
Q1:登录态怎么做?JWT 放哪儿?
M: 你们社区要支持 App/Web 登录,后端 Spring Boot。你会怎么做登录态?JWT 放哪里?怎么刷新?
Y: JWT 嘛......就发给前端存起来。一般放 localStorage?然后每次请求带上。刷新......再发一个新的?
M:(皱眉)"存起来"不等于"安全"。你至少要说清楚 Web 与 App 的差异、XSS/CSRF 风险与刷新策略。
Y: 那我改口:放 Cookie?HttpOnly 的那种。刷新就......加个 refresh token?
M: 这回答终于像个后端了。继续。
Q2:Spring MVC 和 WebFlux 的选择:上传大文件、并发高怎么选?
M: 音视频上传比较大、并发也高。Spring MVC 和 Spring WebFlux 你怎么选?为什么?
Y: WebFlux 是异步的,肯定快,所以都用 WebFlux。
M:(摇头)"肯定快"属于面试大忌。什么时候 WebFlux 真有收益?什么时候反而更复杂?
Y: 呃......线程少的时候?I/O 多的时候?
M: 方向对了。你先记住:I/O 密集 + 非阻塞链路完整,WebFlux 才体现优势。
Q3:数据库表怎么设计?MyBatis/JPA 怎么选?
M: UGC 内容:帖子、评论、点赞、收藏。你给一个简化的表设计,并说下 MyBatis 和 JPA 怎么选。
Y: 表就:post、comment、like。点赞表就 user_id + post_id 做联合主键。MyBatis 比较灵活,JPA 自动化。
M: 还行。那你会加哪些索引?分页怎么做?
Y: 索引......给 post.created_time 加?分页就 limit offset。
M:(叹气)基础题你可以,但"limit offset"在大表会翻车。下一轮我们就从这里加深。
Q4:连接池你用 HikariCP 还是 C3P0?为什么?
M: Spring Boot 默认 HikariCP,你了解原因吗?
Y: 因为快?
M:(点头)至少知道"快"。那快在哪里?
Y: 这个......实现比较优秀?锁少?
M: 好,知道大方向即可。进下一轮。
第二轮:高并发 feed + 消息队列 + 缓存(架构进阶)
Q1:社区首页 feed 流怎么做?缓存怎么用?
M: 首页 feed:关注的人发帖要快速出现。你用 Redis 怎么设计?用 Spring Cache 还是自己写?
Y: Redis 当然缓存帖子列表。Spring Cache 一加注解就好了,@Cacheable。
M:(严肃)"注解一加就好了"是事故的起点。你要解释:
- 缓存 key 设计;2) 过期策略;3) 缓存击穿/穿透/雪崩;4) 写一致性。
Y: key 就 feed:userId,过期 5 分钟。击穿......加锁?穿透......布隆过滤器?雪崩......随机过期?一致性......最终一致。
M: 这题回答得不错,继续。
Q2:点赞高并发:如何防止重复点赞与计数不准?
M: 点赞 QPS 很高,怎么保证不重复点赞?计数怎么准?
Y: 数据库加唯一约束 user_id + post_id,重复插入就失败。计数用 update post set like_count=like_count+1。
M: 你说的是"能跑",但在热点帖子上会被打爆。你会用 Redis 吗?
Y: 用 Redis set 存点赞用户?然后计数用 incr?
M: 对,这就是方向。那 Redis 和 DB 怎么对账?
Y: 定时任务同步?
M: 可以。记住要讲清楚"写路径"和"读路径"。
Q3:Kafka / RabbitMQ / Pulsar 选哪个?用于什么?
M: 你要做"发帖后异步生成封面、转码、审核、推送 feed",消息队列怎么选?
Y: Kafka 吞吐高,所以用 Kafka。
M:(追问)Kafka 的"至少一次"语义会导致什么问题?如何做到幂等?
Y: 会重复消费。幂等就......消费端判断一下?
M: 判断什么?用什么存?说不出来就扣分。
Y: 用 Redis 记录 messageId?如果处理过就跳过。
M: 可以,这是常见方案。更严谨的还有:业务唯一键 + 去重表 + 事务外盒(outbox)等。
Q4:链路追踪与监控怎么落地?
M: 微服务多了,怎么做监控告警?
Y: Prometheus + Grafana。链路追踪 Jaeger。
M: Micrometer 在哪里?日志怎么统一?
Y: Micrometer 采指标。日志用 ELK。
M: 这题回答不错。
第三轮:AIGC 智能客服(RAG/Agent + 工具调用 + 安全与工程化)
业务:社区新增"智能客服",用户问"视频上传失败怎么办""如何申诉封禁"。客服要能查企业知识库(SOP/FAQ),必要时调用内部工单系统。
Q1:你会如何设计一个 RAG(检索增强生成)链路?
M: 给我一个端到端 RAG 方案:从文档到回答。
Y: RAG 就是"先搜再问"。把文档丢进去,然后向量化,最后让大模型回答。
M:(严肃)太笼统。具体:文档怎么切分?embedding 用什么?向量库选什么?召回后怎么拼提示词?
Y: 切分就按段落......embedding 用 OpenAI?向量库用 Milvus?提示词就把内容塞进去。
M:(记录)你知道关键名词,但缺工程细节。继续。
Q2:Agent(智能代理)和"工具执行框架"怎么做?
M: 客服需要"查询订单状态/工单状态/用户封禁原因",这不是纯问答。你怎么让模型调用工具?
Y: 用函数调用。Spring AI 有工具调用。
M: 那"工具调用标准化"怎么保证?参数校验、权限、审计怎么做?
Y: 参数就校验一下......权限用 Spring Security?审计写日志。
M: 这题你答得还行,但不够系统。
Q3:怎么降低 AI 幻觉(Hallucination)?
M: 模型胡说八道怎么办?
Y: 提示词写严一点:不要编。
M: 只有提示词不够。你还要说:检索置信度阈值、引用来源、拒答策略、结构化输出校验。
Y: 那就加个阈值,检索不到就让它说"不知道"。然后把引用链接贴出来。
M: 方向正确。
Q4:聊天会话内存怎么存?如何做多租户隔离?
M: 客服要记住用户上下文(会话内存),你存哪?Redis?数据库?怎么做多租户?
Y: 存 Redis,key 带上 tenantId + userId。设置过期时间。
M:(点头)简单场景可行。复杂场景还要考虑:敏感信息脱敏、加密、访问审计。
Q5:你们要上生产:CI/CD、灰度、回滚怎么做?
M: 从提交代码到上线,你的流水线怎么设计?
Y: GitLab CI 或 Jenkins。打包 Docker,部署 Kubernetes。灰度......按比例发布?回滚就回到上一版镜像。
M: 可以。至少没把"手动 scp"当最佳实践。
面试结束
M: 今天先到这。你基础面还可以,但复杂问题需要更具体的落地细节。回去等通知,有结果我们会在一周内联系。
Y: 好的好的,我回去就把"幂等"和"RAG 切分"背熟......不是,研究透。
题目答案详解(按业务场景讲透,让小白能学会)
说明:以下为"标准可落地回答"。面试时不必全背,但要能讲清:为什么这样做、关键风险点、工程落地细节。
第一轮详解:登录、Web 框架、ORM、连接池
1) 登录态:JWT + Spring Security + OAuth2(或自建)
业务场景:社区 App/Web 登录后,调用发帖、点赞等接口。
推荐方案(Web):
access_token(JWT)放 HttpOnly + Secure Cookie,降低 XSS 盗 token 风险。- 防 CSRF:
- 若 Cookie 携带 token:使用 SameSite=Lax/Strict + CSRF Token(Spring Security 自带)
- 或改为 Authorization Header(前端存内存/安全容器)以规避 CSRF,但要防 XSS。
refresh_token:- 单独存放(也建议 HttpOnly Cookie),生命周期更长
- 刷新时校验 refresh token、签发新 access token
推荐方案(App):
- token 通常放系统安全存储(Keychain/Keystore)。
技术点:
- Spring Security 过滤器链:
OncePerRequestFilter解析 JWT,构造Authentication。 - JWT 关键字段:
sub(用户)、exp(过期)、jti(唯一 ID,便于吊销/黑名单)。 - 吊销策略:Redis 黑名单(短期)/refresh token 旋转(长期)。
2) Spring MVC vs WebFlux:取决于链路是否"全异步/非阻塞"
业务场景:音视频上传大文件、并发较高。
-
Spring MVC(Servlet):线程模型直观,生态成熟。
- 优点:对 JDBC/MyBatis 等阻塞式依赖兼容最好。
- 缺点:高并发 I/O 时线程占用高。
-
Spring WebFlux(Reactor):适合 I/O 密集、连接数多、下游也是非阻塞(如 R2DBC、Reactive Redis、WebClient)。
- 如果你用了阻塞 JDBC/MyBatis,却在 WebFlux 中调用,会把阻塞塞进事件循环/弹性线程池,复杂且收益变小。
结论(常见落地):
- 上传服务可用 MVC + Nginx/对象存储直传(最常见、最稳)。
- 若强需求长连接/高并发:WebFlux + 全链路 Reactive 才值得。
3) UGC 表设计、索引与分页
简化表:
post(id, author_id, title, content, created_time, status, like_count, comment_count, ...)comment(id, post_id, user_id, content, created_time, status, ...)post_like(id, post_id, user_id, created_time)+ 唯一约束(post_id, user_id)
索引建议:
post(author_id, created_time):查用户发布列表post(created_time, id):按时间翻页comment(post_id, created_time):帖子评论列表post_like(post_id, user_id):去重与判断是否点过赞
分页建议:
- 小数据:
limit offset可以。 - 大数据:用 游标分页/Keyset Pagination :
where created_time < ? and id < ? order by created_time desc, id desc limit 20- 性能稳定,不会 offset 越大越慢。
MyBatis vs JPA:
- MyBatis:复杂 SQL、动态条件、性能可控。
- JPA/Hibernate:开发效率高,但要避免 N+1、懒加载坑。
- 大厂常见组合:核心链路 MyBatis,后台管理 JPA。
4) HikariCP 为什么是默认?
关键点:
- 性能好、实现精简、锁竞争少、连接获取快。
- 配置合理即可:最大连接数、连接超时、泄漏检测。
第二轮详解:Feed、缓存、MQ、可观测
1) Feed 流:Redis Key 设计 + 三大问题
业务场景:关注的人发帖后,粉丝首页快速出现。
常见两类模式:
- 推模式(Fanout on write):发帖时把 postId 推到粉丝的 feed 列表(Redis ZSET/List)。读快写慢。
- 拉模式(Fanout on read):读首页时现算关注列表 + 查帖子。写快读慢。
折中:大V用拉,小用户用推。
Redis Key:
feed:{userId}-> ZSET(score=时间戳, value=postId)
缓存三大问题:
- 穿透:查不存在的 postId -> 布隆过滤器/空值缓存。
- 击穿:热点 key 过期瞬间 -> 互斥锁/逻辑过期。
- 雪崩:大量 key 同时过期 -> 过期随机化、分批预热。
一致性:
- 读路径优先:Redis miss -> DB -> 回填。
- 写路径:DB 成功后删缓存(或更新缓存),并配合 MQ 异步修复。
2) 点赞高并发:Redis 去重 + 异步落库 + 对账
业务场景:热点帖子点赞 QPS 高。
推荐写路径:
- Redis Set:
SADD like:set:{postId} userId(返回 1 表示首次点赞) - Redis 计数:
INCR like:count:{postId}(或用 Hash) - 发送 Kafka 消息:
like_event(postId,userId,action) - 消费端批量落库(MyBatis batch)
幂等:
- DB 层唯一约束
(post_id,user_id)兜底 - 消费端去重:Redis
SETNX dedup:{eventId}或去重表
对账:
- 定时任务把 Redis 计数与 DB 汇总对齐(以 DB 为准或以 Redis 为准取决于业务)
3) MQ 选型:Kafka / RabbitMQ / Pulsar
发帖后异步链路:转码、封面、审核、推送 feed。
- Kafka:高吞吐、分区顺序、生态强(大数据)。适合日志/事件流。
- RabbitMQ:路由灵活、延迟队列等,适合业务消息。
- Pulsar:多租户、分层存储、队列/流一体。
至少一次语义问题:
- 可能重复消费 -> 必须幂等。
幂等常见做法:
- 业务唯一键(如
postId+action) - 去重存储:Redis
SETNX(带 TTL)、或 DB 去重表 - Outbox 模式:业务写 DB 与消息记录同事务,异步投递,减少"写库成功但消息丢"的不一致。
4) 可观测:Micrometer + Prometheus + Grafana + ELK + Trace
- 指标:Micrometer 埋点 -> Prometheus 抓取 -> Grafana 看板与告警。
- 日志:SLF4J 门面 + Logback/Log4j2 实现;集中到 ELK。
- 链路追踪:OpenTelemetry(或 Zipkin/Jaeger)串起 TraceId。
第三轮详解:RAG、Agent、工具调用、安全与工程化
1) RAG 端到端:文档加载 -> 切分 -> 向量化 -> 检索 -> 生成
业务场景:客服回答必须基于企业 SOP/FAQ,不能瞎编。
流程:
- 文档加载:PDF/网页/Confluence/数据库(Spring AI 支持多种 DocumentLoader)
- 清洗:去导航、去噪、保留标题层级与来源 URL
- 切分(chunking) :
- 按标题/段落 + 最大 token(如 300~800 tokens)
- 适当 overlap(如 50~100 tokens)减少上下文断裂
- 向量化(Embedding) :
- OpenAI / 本地 Ollama embedding 模型
- 向量库:Milvus/Chroma/Redis Vector(看规模与运维能力)
- 检索:相似度召回 topK + 过滤(租户/权限/文档类型)
- 重排(可选):用 reranker 提升相关性
- 提示填充(prompt stuffing) :
- system:约束必须基于引用回答
- user:问题
- context:检索片段 + 来源
- 输出:结构化(JSON)+ 引用来源
落地关键:
- 评估:命中率/可用率/拒答率
- 版本:知识库增量更新(定时或事件驱动)
2) Agent + 工具执行框架:让模型"会办事"
业务场景:用户问"我账号为什么被封""工单进度"。需要调用内部系统。
设计要点:
- 工具(Tool)定义:
- 名称、描述、入参 JSON Schema、出参结构
- 工具调用标准化:
- 统一网关/SDK
- 参数校验(JSR-380/自定义校验)
- 超时/重试/熔断(Resilience4j)
- 权限:Spring Security + OAuth2(或 JWT)
- 工具调用要带用户身份与租户信息
- 审计:
- 记录谁在何时调用了什么工具、输入输出摘要(注意脱敏)
Spring AI:
- 可用 Tool Calling 将方法暴露给模型;也可对接 MCP(模型上下文协议)/A2A 进行跨服务工具编排。
3) 降低幻觉:让模型"不会就说不会"
组合拳:
- 检索相似度阈值:低于阈值直接拒答或转人工
- 强制引用:回答必须包含引用片段 ID/URL
- 结构化输出校验:JSON Schema 校验失败就重试/拒答
- 黑白名单与敏感词:避免越权回答
- 小结:幻觉不是"提示词能解决的全部",要用工程约束。
4) 会话内存:Redis/DB + 多租户隔离
简单方案:
- Redis:
chat:mem:{tenantId}:{userId}:{sessionId}TTL 30min - 存:最近 N 轮对话摘要(Summary Memory)而非全量,以控 token。
安全要点:
- 脱敏:手机号/身份证
- 加密:敏感字段应用层加密(Bouncy Castle)
- 审计与权限:租户隔离(tenantId 强制贯穿)
5) CI/CD:从代码到生产
- Git 提交 -> CI(单测 JUnit5 + Mockito)
- 构建:Maven/Gradle 打包
- 镜像:Docker build + 扫描
- 发布:Kubernetes 滚动/灰度(按流量比例)
- 回滚:上一版本镜像 + 数据库变更用 Flyway/Liquibase 可回退策略
- 观测:发布后关注 SLO、错误率、延迟、消费堆积。
文章小结(给小白的学习路线)
- 先补齐基础:Spring MVC、MyBatis、索引与分页、HikariCP。
- 再学并发与中间件:Redis 三大问题、Kafka 幂等、可观测体系。
- 最后上 AI 工程:RAG 切分与评估、Agent 工具调用、幻觉治理与权限审计。