AI+Java中级开发面试题库
生成时间:2026-05-20 22:52
【面试题】1. 说说RAG检索增强生成的核心原理,为什么比纯大模型调用更适合企业知识问答场景?
【标准答案】RAG的核心思路是"检索+生成"两步走:先把用户问题通过向量检索从知识库召回相关文档片段,再把这些上下文拼接到Prompt里送给大模型生成答案。
相比纯大模型,RAG有三个关键优势:
- 知识可更新:企业业务文档天天变,RAG改索引就行,不用重新训练模型
- 答案可追溯:能给出来源文档链接,用户能验证真假,这是企业合规刚需
- 幻觉可控:模型基于检索到的真实文档回答,不会凭空编造
技术栈上,一般用向量数据库(如Milvus、Pinecone)做语义检索,Embedding模型(如BGE、M3E)把文本转成向量,大模型只负责理解和生成。成本也低,不需要微调,一个中等规模的RAG系统,部署成本可能只是微调模型的十分之一。
【面试题】2. Spring Boot中@Transactional注解在什么情况下会失效?生产环境怎么规避?
【标准答案】失效场景主要有四类:
- 方法不是public:Spring AOP代理要求public方法,private/protected直接穿透
- 同类内部调用:this.method()绕过了代理对象,事务增强没机会介入
- 异常被catch吞掉:默认只在RuntimeException回滚,手动catch又不抛出,事务提交了
- 传播行为配置错误:比如嵌套调用时用了REQUIRES_NEW,内层事务独立提交,外层回滚影响不到
生产规避措施:
- 统一加AOP切面校验方法访问权限,非public直接告警
- 同类调用改用
ApplicationContext.getBean()或AopContext.currentProxy()获取代理对象 - 异常处理统一用全局异常处理器,不要在业务代码里随意catch
- Code Review环节强制检查事务传播行为配置
【面试题】3. 设计一个电商大促场景下的库存扣减方案,怎么保证不超卖?
【标准答案】这是个经典的高并发一致性问题,核心方案是"Redis预扣+异步落库+兜底校验":
第一层:Redis原子扣减
// Lua脚本保证原子性
if redis.call('GET', stock_key) >= order_count then
return redis.call('DECRBY', stock_key, order_count)
else
return -1
endRedis扛流量,Lua脚本保证不超卖。
第二层:异步消息落库 Redis扣减成功后发MQ消息,异步扣MySQL库存。消费者做幂等处理,避免消息重复导致库存错误。
第三层:兜底校验 下单前再查一次MySQL真实库存,如果Redis和MySQL不一致,说明有数据漂移,触发补偿流程。
为什么不用分布式锁?锁粒度太粗,并发上不去。Redis原子操作+异步落库这套组合拳,能扛住万级QPS,而且数据最终一致。
【面试题】4. 解释一下向量数据库的HNSW索引原理,为什么查询速度比传统倒排索引快?
【标准答案】HNSW(Hierarchical Navigable Small World)是个分层的图索引结构,核心思想是"跳表+小世界网络":
结构设计:
- 分多层,顶层节点少、连接稀疏,底层节点多、连接密集
- 查询时从顶层入口节点开始,贪心搜索到最接近目标向量的节点,再跳到下一层继续找
- 类似地图的"先看城市,再看街道,再看门牌号",逐层缩小范围
为什么快:
- 传统倒排索引是精确匹配,向量检索是相似度计算,倒排索引帮不上忙
- HNSW把高维空间搜索复杂度从O(N)降到O(log N),百万级数据几毫秒返回
- 图结构支持增量更新,不用全量重建索引
工程取舍:
- 内存占用大,适合百万级数据,十亿级要分片
- 构建索引慢,适合读多写少场景
- 参数调优(M、efConstruction)直接影响召回率和查询速度,生产环境需要压测验证
【面试题】5. ConcurrentHashMap在Java 8中的实现有什么变化?put操作流程是什么?
【标准答案】Java 7 vs Java 8的重大区别:
Java 7:分段锁(Segment数组),每个Segment继承ReentrantLock,并发度受Segment数量限制
Java 8:CAS+synchronized,锁粒度更细,直接锁桶节点
put流程六步:
- 计算key的hash,定位桶位置
- 桶为空,CAS尝试插入新节点
- 桶不为空,synchronized锁住头节点
- 遍历链表,key相同则覆盖value,不同则尾插
- 链表长度>=8且数组长度>=64,转红黑树
- 插入完成后检查是否需要扩容(多线程协助扩容)
关键优化:
- 锁粒度从Segment级别降到桶级别,并发度上限从Segment数量(默认16)变成数组长度
- 扩容时多线程协同处理,每个线程负责一部分桶的迁移
- 读操作完全无锁,用volatile保证可见性
【面试题】6. 提示词工程的Chain-of-Thought(思维链)技术是什么?在实际项目中怎么用?
【标准答案】CoT的核心是让模型"分步思考",把复杂问题拆成推理链,而不是直接输出答案。
原理: 大模型本质是概率预测下一个token,单步推理容易跳步出错。加一句"让我们一步步思考",模型会显式展开推理过程,中间步骤的推理误差能在后续步骤自我修正。
实际应用三步走:
- Few-shot CoT:Prompt里给2-3个带推理过程的示例,模型学会模仿
- Zero-shot CoT:直接加"请一步步分析并给出推理过程",适合通用场景
- Self-Consistency:让模型生成多个推理路径,投票选最一致的答案
工程落地注意:
- CoT增加输出长度,成本和延迟都上升,生产环境要控制思考步数
- 简单分类任务不需要CoT,反而增加幻觉风险
- 推理过程可能暴露敏感逻辑,对外输出的产品要考虑脱敏
【面试题】7. MySQL联合索引(a,b,c)在哪些查询场景能用上?最左前缀原则的底层原理是什么?
【标准答案】索引能用的场景:
WHERE a = 1✅ 前缀匹配WHERE a = 1 AND b = 2✅ 等值前缀WHERE a = 1 AND b = 2 AND c = 3✅ 全覆盖WHERE a = 1 AND c = 3⚠️ 只用到a,c用不上(中间断了)WHERE b = 2 AND c = 3❌ 不满足最左前缀WHERE a = 1 AND b LIKE 'xx%'✅ 范围查询后还能用WHERE a = 1 AND b > 2 AND c = 3⚠️ a和b能用,c用不上(范围查询后失效)
底层原理: B+树索引是按字段顺序排列的,联合索引先按a排序,a相同再按b排序,b相同再按c排序。就像电话簿"省-市-区"排序,知道省能定位,知道省市能更精确,但只知道区是没法快速查找的。
优化建议:
- 等值查询字段放前面,范围查询字段放后面
- 排序字段如果能复用索引顺序,能避免filesort
- 不要盲目建联合索引,索引维护有成本,高频查询才值得
【面试题】8. 设计一个AI Agent的任务规划模块,怎么让模型能拆解多步骤任务并执行?
【标准答案】AI Agent的核心是"感知-规划-执行"循环:
架构设计三模块:
规划器(Planner)
- 用户意图转结构化任务图(DAG)
- 每个节点标注:动作类型(搜索/计算/API调用)、输入参数、依赖关系
- 用ReAct(Reasoning + Acting)范式:Thought → Action → Observation循环
执行器(Executor)
- 工具调用层:封装搜索引擎、数据库查询、API调用等
- 沙箱隔离:外部调用超时控制、异常捕获、重试机制
- 输出标准化:统一返回JSON格式结果
记忆层(Memory)
- 短期记忆:当前对话上下文,存储在内存
- 长期记忆:历史任务结果存向量库,相似任务复用
- 工作记忆:任务中间状态,支持断点续执行
关键流程:
用户输入 → 意图识别 → 任务分解(LLM生成DAG) → 拓扑排序执行 → 结果聚合 → 输出工程难点:
- 任务依赖错误处理:某步失败是否回滚?部分失败如何通知用户?
- 工具调用权限控制:不能让Agent随意调用敏感API
- 成本控制:多步骤任务可能触发数十次LLM调用,需要设置步数上限
【面试题】9. Kafka消息丢失的场景有哪些?生产环境怎么保证消息不丢?
【标准答案】消息可能在三个阶段丢失:
生产端丢失:
- 发送方式用异步不关心结果,网络抖动消息发了但没成功
- 阻塞队列满,消息被丢弃
Broker端丢失:
- 多副本没同步完成,Leader挂了,新Leader数据不全
- unclean.leader.election.enable=true,ISR外副本当选Leader
消费端丢失:
- 自动提交offset,消息还在处理,应用挂了,重启后消息丢了
生产不丢方案:
生产端:
acks=all,等待所有ISR副本确认retries配置重试次数- 用带回调的send方法,失败时记录本地日志重试
Broker端:
replication.factor >= 3,至少3副本min.insync.replicas > 1,至少2个副本同步完成unclean.leader.election.enable=false,禁止ISR外副本当选
消费端:
- 关闭自动提交,业务逻辑执行完手动提交offset
- 消费失败时发送到死信队列(DLQ)人工处理
【面试题】10. JVM新生代为什么要分Eden区和Survivor区?对象晋升老年代的流程是什么?
【标准答案】为什么分Eden和Survivor:
- Eden区:新对象都在这里分配,大部分对象"朝生夕死",一次Minor GC能回收90%+
- Survivor区:存活的"中年对象",经历多次GC还没死,有资格进老年代
- 为什么两个Survivor:解决内存碎片。如果只有一个Survivor,GC时存活对象要压缩,开销大。两个Survivor用复制算法,from区存活对象复制到to区,from区直接清空,没有碎片
晋升老年代条件:
- 年龄达标:默认15次GC(-XX:MaxTenuringThreshold),每次GC在Survivor区年龄+1
- 空间担保失败:Survivor区存不下,直接晋升(动态年龄判定)
- 大对象直接进老年代:超过-XX:PretenureSizeThreshold的对象,避免在新生代来回复制
- 空间分配担保:老年代剩余空间 > 新生代所有对象总和,Minor GC才安全,否则触发Full GC
调优实践:
- Eden:S0:S1默认8:1:1, SurvivorRatio可调
- 新生代太小导致对象过早晋升老年代,频繁Full GC
- 新生代太大导致Minor GC停顿时间长,找平衡点
- 用jstat -gcutil监控各区域使用率,别等OOM再排查
本轮题库覆盖考点:
- AI:RAG架构、向量数据库HNSW、提示词工程CoT、AI Agent规划、生产级消息不丢方案
- Java:Spring事务、ConcurrentHashMap源码、MySQL索引优化、Kafka消息可靠性、JVM内存模型