项目学习
项目学习笔记
1. AI医疗服务平台
1.0 整体
说说RAG、MCP的技术流程和技术交互栈分别是怎样的?
- RAG:解决 大模型知识更新与外部知识接入问题
- MCP:解决 大模型如何标准化调用工具/数据源的问题
RAG技术流程:
- 数据准备:将知识库文档进行解析、文本切分。
- 向量化:通过 Embedding 模型将文本转为向量。
- 向量存储:存入向量数据库(如 Milvus)。
- 检索阶段:用户问题向量化后进行相似度搜索。
- 上下文构建:取 TopK 文档作为上下文。
- 生成阶段:将问题 + 检索内容一起发送给大模型生成答案。
MCP技术流程:
- LLM生成 Tool Call请求
- MCP Server解析请求
- 调用对应工具(数据库/API/函数)
- 返回结果给LLM
- LLM整合结果生成最终回答
MCP和Fuction call的区别
| 对比 | MCP | Function Calling |
|---|---|---|
| 本质 | 工具调用协议 | 模型能力 |
| 作用范围 | 跨系统 | 单模型 |
| 标准 | 统一协议 | 各家不同 |
| 资源来源 | 远程MCP Server | 本地代码 |
| 扩展性 | 极高 | 有限 |
Function Calling
是大模型生成函数调用意图,由应用程序执行函数。
MCP
是一个标准化协议,让大模型可以通过统一接口调用外部工具和资源。
什么是向量化?
向量化是指将文本、图片等数据通过 Embedding模型 转换成高维向量表示。
这样语义相近的文本在向量空间中的距离会更近,从而可以通过向量相似度计算实现语义检索。
Embedding模型
- text-embedding-3-large (1024维)
为什么向量维度越高,语义表达能力越强?
- 信息容量:维度越多,可编码的信息越多
- 维度越多 → 能表达的语义特征越多。
- 特征空间:维度越高,语义区分能力越强
做向量化匹配时的主要标准是什么?
主要是 向量相似度计算:
- 余弦相似度(Cosine Similarity)(最常用)
- 欧氏距离
- 内积(Inner Product)
向量越接近,相似度越高。
MCP协议的本质构成是什么?
MCP本质是一个 标准化工具调用协议,主要包含:
Tool Schema
描述工具的功能和参数Tool Call
LLM发起工具调用请求
- Tool Response
返回调用结果
本质是 LLM + Tool 的标准接口协议。
尝试过在本地部署AI服务吗?
尝试过,比如:
- 使用 Ollama
- 部署开源模型(如 Llama / Qwen)
通过 HTTP API 调用模型服务。
一般会结合:
- GPU
- Docker
- 向量数据库
搭建本地 AI 服务。
部署本地大模型服务时需要注意哪些问题?
- 硬件资源:GPU显存/CPU/内存
- 模型大小:7B/13B/70B
- 推理性能:QPS/延迟
简单谈谈Transfomer和注意力机制?
注意力机制是一种让模型在处理序列数据时关注更重要信息的机制
Transformer 是一种 基于注意力机制的深度学习模型架构
完全用注意力机制替代 RNN 和 CNN 来处理序列数据
1 | |
该医疗系统能承受的QPS是多少,如何测试?
可以通过 压测工具测试:JMeter
- 构造并发请求
- 模拟真实用户访问
- 监控系统指标
指标包括:
- QPS
- RT(响应时间)
- CPU
系统QPS取决于接口复杂度和硬件资源(CPU/GPU/内存)
我主要压测了药品搜索的接口,应为是用到了二级缓存,所以大概能承受上万的QPS。
其次还压测了基于ES的分页查询的接口,能承受1000的QPS
我们一般通过压测工具进行测试,例如JMeter。压测流程通常是:
- 编写接口压测脚本
- 设置并发线程数和持续时间
- 逐步提高并发量进行压测
- 观察QPS、响应时间、错误率以及CPU和内存指标
- 找到系统稳定QPS和极限QPS,并分析系统瓶颈。
你认为当前这个医疗平台存在哪些优化点、做得不好的地方?
1️⃣ 性能与并发优化
潜在问题:
- AI 模型调用是瓶颈,高并发下容易出现响应延迟甚至服务拒绝(你提到过 1000 用户同时问诊)。
- ChatMemory、MongoDB 写入压力大,如果没有做好分库分表或缓存优化,可能导致系统阻塞。
- 秒杀/高并发场景下,如果 AI 触发下单直接调用业务链路,可能绕过原有的并发保护。
优化点:
- 请求削峰:使用消息队列、异步处理、令牌桶等策略,避免瞬时流量打满 AI 服务。
- 模型负载均衡:多实例部署 AI 服务,并结合 GPU 资源调度。
- 缓存热点数据:RAG 的 embedding、FAQ、药品说明等可以放在 Redis 或 Caffeine,减少重复计算。
- 分布式数据库优化:MongoDB 可以考虑分片,MySQL 可使用读写分离和索引优化。
2️⃣ 数据一致性与可靠性
潜在问题:
- MySQL 与 Elasticsearch 的同步是最终一致,存在短时间数据延迟,可能影响搜索结果。
- 高并发更新可能导致 Elasticsearch 写入乱序或数据丢失。
- 缓存雪崩、穿透、击穿风险,如果没有二级缓存或预加载策略。
优化点:
- 保证顺序性:按主键分区,binlog 顺序消费,单分区单线程写入 ES。
- 错误重试和幂等:同步 ES 写入操作增加幂等逻辑。
- 缓存策略:热点药品、用户信息使用 Caffeine + Redis 二级缓存,结合 TTL + 预加载。
- 降级兜底:缓存失效或 DB 宕机时返回默认值或历史数据。
什么是大模型幻觉问题,你的项目中是如何尝试解决的?
好的,我们来认真梳理这个问题。你的问题涉及 大模型的“幻觉”问题(hallucination),以及在你的医疗问诊项目中具体的解决方案。
1️⃣ 什么是大模型幻觉(Hallucination)?
定义:大语言模型在生成回答时,可能给出 与事实不符、没有依据、甚至完全虚构的内容。
特点:
- 内容看起来合理、流畅、符合语法,但实际上是错误的。
- 在医疗或金融等领域风险更高,可能导致误导用户。
- 幻觉常发生在模型缺乏明确事实支撑或训练数据中信息不足时。
举例(概念性,不是实际患者数据):
用户问:“某药物的标准剂量是多少?”
模型回答:“推荐剂量是 1000mg,每天三次。”
—— 但实际药典显示标准剂量是 500mg。这个就是幻觉。
2️⃣ 为什么会发生?
- 模型 基于概率生成,并不真正“理解”事实。
- 模型的训练数据可能 不完整、过时或含噪声。
- 问题涉及 冷门或专业领域信息,模型容易自由发挥。
- Prompt 或上下文提供的信息不足,模型需要“填空”,容易出错。
3️⃣ 项目中解决幻觉问题的方法
在你的医疗问诊平台中,你已经做了一些技术尝试来 减少幻觉的发生:
(1)RAG(Retrieval-Augmented Generation)检索增强
思路:模型回答之前先 从可信知识库(药典、指南、问诊 FAQ)检索相关文档。
效果:模型生成的回答可以引用具体文档,减少凭空生成内容。
实现细节:
- 将药品说明、医疗指南向量化(embedding)并存入向量数据库。
- 用户问诊时先召回最相关文档,然后将文档内容和问题一起作为 prompt 输入模型。
- 这样模型生成回答就有事实支撑。
(2)多路检索 + reranker
- 思路:不同检索器(关键词、embedding)召回候选文档,再用 cross-encoder 模型排序,选最可信的文档提供给大模型。
- 效果:进一步保证输入信息的质量,减少模型使用无关信息“编故事”。
(3)上下文与提示优化(Prompt Engineering)
明确告诉模型:
“请基于提供的医疗文档回答,不允许凭空生成剂量或疗程信息。”
效果:减少自由发挥的可能性,但不能完全消除幻觉。
(4)二次校验 / 规则过滤
对敏感信息(药品剂量、检查标准)增加规则:
- 回答中关键数据必须 在知识库中找到匹配值。
- 如果模型回答未匹配到事实,返回提示“请参考官方药典”。
效果:保证不会直接输出错误医疗建议。
(5)用户可视化引用
- 对模型回答中引用的知识文档进行 高亮或标注来源。
- 用户可以直接点击查看原始文档,增加透明度和可信度。
4️⃣ 整体总结
大模型幻觉是生成式 AI 的本质问题,尤其在专业领域高风险。
解决策略:
- RAG 检索增强 → 提供事实依据
- 多路检索 + reranker → 提高文档质量
- Prompt 指令约束 → 限制自由发挥
- 二次校验 / 规则过滤 → 数据安全兜底
- 引用透明化 → 用户可验证信息
该医疗平台在医疗问诊场景下对用户的核心价值是什么?
1️⃣ 便捷的医疗咨询
价值:用户可以随时在线获取问诊服务,无需排队、预约医院。
体现方式:
- 高并发 AI 问诊支持多用户同时咨询。
- 智能问答可以快速理解用户症状,提供初步分析。
- 对于轻微病症或常见问题,用户无需等待医生即可得到参考建议。
用户收益:节省时间、提升体验,尤其是在夜间或偏远地区。
2️⃣ 准确可靠的医疗信息
价值:减少错误或不准确信息的风险。
体现方式:
- 使用 RAG + 知识库检索,确保 AI 回答基于权威文档(药典、指南、FAQ)。
- 高亮引用源,用户可以直接验证信息来源。
- 规则校验和数据过滤保障关键医疗数据(如剂量、疗程)可靠。
用户收益:获取可信、可验证的医疗信息,降低因误诊或错误信息产生的风险。
CoT/ReAct了解过吗?
一、CoT(思维链)
核心定义:2022 年谷歌提出的基础推理范式,引导大模型把复杂问题拆解为线性、连续的中间推理步骤,先输出 “思考过程” 再给答案,替代 “问题→答案” 的直接输出。
工作流程
输入复杂问题(数学、逻辑、推理题)
模型按 “第一步→第二步→…→最后一步” 输出推理链
汇总步骤,给出最终答案
二、ReAct(Reason + Act)
核心定义:融合推理(Reason)与行动(Act)的高级范式,让模型形成Thought → Action → Observation → Thought闭环,可主动调用搜索、API、计算器等外部工具。
工作流程
Thought:分析问题、规划下一步(如 “我需要查南京今天天气”)
Action:执行工具调用(如search(“南京天气 2026-03-21”))
Observation:接收工具返回结果(如 “晴,12–22℃”)
循环直至得出最终答案
1.1 大模型对话 / Prompt / 上下文记忆
如何对大模型的回答和行为进行约束?
可以通过:输入/输出/模型本身/RAG/Fuction call
- Prompt约束:System Prompt(系统提示词)
定义模型角色,例如:
1 | |
- 输出格式约束
通过 Prompt 强制结构化输出:例如 JSON 输出。
1 | |
- 内容过滤
对模型输出进行安全检测。
- 模型层约束:指令微调
1 | |
模型会学习:
- 如何回答问题
- 什么内容应该拒绝
- 系统层约束
在 模型外部增加控制逻辑 - RAG知识范围限制: 只能基于知识库回答
- 如果模型可以调用工具(Tool/Fuction call),需要限制权限。
使用提示词(Prompt)时需要注意哪些要点?
主要有几点:
- 角色设定
通过 角色设定可以显著提升回答质量。
1 | |
- 任务描述
Prompt 必须 明确告诉模型要做什么任务
1 | |
- 提供足够的上下文
大模型本身 没有长期记忆,如果上下文不足容易产生 幻觉
1 | |
- 明确输出格式
约束返回结构。
1 | |
1 | |
1 | |
若大模型给出错误医疗建议,如何从系统层面发现/拦截?
一、输入层拦截(问题识别)
首先识别用户问题是否属于 医疗高风险问题。
二、RAG知识约束(防止胡编)
医疗场景必须限制模型 只能基于权威知识回答。
三、输出层安全检测(关键)
在 LLM 输出之后,需要进行 自动安全检测。
如果回答不合规,就终止回答。
如何解决大模型对话的上下文超限问题?
常见方法:
- 对话摘要(Conversation Summary)
当对话变长时,可以 对历史对话进行总结压缩。
- 滑动窗口:只保留最近N轮对话
例如最近10轮。
- 向量记忆
将历史对话 向量化存储,只在需要时检索。
只加载相关记忆
1 | |
为什么使用Mongodb存储大模型对话上下文,而不是Mysql
在 大模型对话系统(ChatGPT 类似应用) 中,很多系统会选择用 MongoDB 存储对话上下文,而不是 MySQL。核心原因主要是:数据结构、写入模式、扩展能力、查询方式更适合 MongoDB。我们从工程角度详细分析。
一、对话上下文的数据结构特点
大模型对话通常是这种结构:
1 | |
特点:
- 嵌套结构
- 数组结构
- 字段不固定
这种结构在 MongoDB 中非常自然,因为 MongoDB 本身就是 Document(JSON)数据库。
二、MongoDB vs MySQL 结构对比
1 MongoDB存储方式
1 | |
优点:
- JSON天然支持
- 一条记录就是一个会话
- 读取非常快
2 MySQL存储方式
通常需要拆表:
会话表
1 | |
消息表
1 | |
查询最近对话:
1 | |
缺点:
- 表结构复杂
- 需要 join 或排序
- SQL操作较重
三、写入模式差异(非常关键)
大模型对话有一个特点:
写多读少
每一句对话都会写入:
1 | |
一轮对话:
1 | |
MongoDB:
1 | |
只需要追加数组
而 MySQL:
1 | |
虽然也可以,但:
- 高并发写入下 MongoDB 更轻量
- JSON append 更方便
四、读取方式差异
大模型推理时需要:
读取最近N条对话
例如:
1 | |
MongoDB:
1 | |
直接返回:
1 | |
或者:
1 | |
一次查询完成。
MySQL:
1 | |
需要:
- 排序
- limit
性能相对差一点。
五、扩展性(分布式能力)
聊天数据有一个特点:
1 | |
例如:
1 | |
就是:
1 | |
MongoDB:
天然支持:
- Sharding 分片
- 水平扩展
可以:
1 | |
MySQL:
虽然也可以:
1 | |
但实现复杂度更高。
六、数据结构灵活性
对话数据可能不断扩展字段:
例如:
1 | |
MongoDB:
不需要修改表结构:
1 | |
MySQL:
需要:
1 | |
扩展性较差。
使用 MongoDB 存储大模型对话上下文的原因:
| 原因 | 说明 |
|---|---|
| JSON结构天然匹配 | 对话数据是嵌套JSON |
| 写入效率高 | append数组 |
| 读取简单 | 一次查询整个会话 |
| 扩展性强 | 支持分片 |
| 结构灵活 | 不需要改表 |
所以:
MongoDB 更适合存储聊天记录这种半结构化、写多读少、结构灵活的数据。
MongoDB和MySQL的核心区别是什么?
主要区别:
数据模型
MySQL:
- 关系型数据库
- 表结构固定
MongoDB:
- 文档数据库
- JSON/BSON存储
- Schema灵活
查询方式
MySQL:
- SQL
MongoDB:
- JSON查询
使用场景
MongoDB更适合:
- 日志
- 对话记录
- 非结构化数据
1.2 RAG
RAG检索增强的好处是什么?
RAG可以:
- 解决大模型知识过期问题
- 引入私有知识库
- 减少幻觉
没有RAG直接调用大模型会存在什么问题?
主要问题:
- 知识过期
- 无法访问私有数据
- 幻觉问题严重
**RAG(Retrieval-Augmented Generation,检索增强生成)**的核心思想是:在大模型生成回答之前,先从外部知识库检索相关信息,再结合这些信息进行回答。它的主要好处可以简要概括为以下几点:
1️⃣ 减少大模型“幻觉”
大模型直接生成答案时可能会编造内容,而 RAG 通过先检索真实知识(如文档、数据库等),再生成回答,可以显著降低错误信息。
2️⃣ 支持私有知识接入
可以将企业内部文档、数据库、知识库等接入检索系统,使模型能够回答训练数据中没有的领域问题。
3️⃣ 知识更新成本低
不需要重新训练模型,只需更新知识库或向量数据库(如 Milvus、Elasticsearch)即可让模型获得最新知识。
4️⃣ 提高回答准确性和可控性
模型回答会基于检索到的上下文内容,更容易做到有依据、可溯源。
5️⃣ 降低模型训练成本
无需频繁微调大模型,只通过外部检索即可扩展知识能力,节省算力和时间。
除了RAG,还有哪些方式能为大模型提供领域上下文?
常见方法:
- Prompt Engineering
- Fine-tuning(微调)
- 知识图谱
- Tool调用
1️⃣ Prompt Engineering(提示词工程)
通过在提示词中加入领域背景、规则或示例(Few-shot),让模型在生成时参考这些上下文。
2️⃣ Fine-tuning(模型微调)
使用领域数据对大模型进行微调,使模型内部参数学习到专业知识。
3️⃣ System Prompt / 角色设定
在系统提示词中固定模型的身份和知识范围,例如设定为“医疗专家助手”。
5️⃣ 工具调用(Tool / Function Calling)
通过调用外部系统(数据库、搜索引擎、API)实时获取信息,再由模型整合生成答案。
6️⃣ 长期记忆(Memory)机制
将用户历史对话或长期知识存储到数据库(如 MongoDB),在后续对话中作为上下文使用。
微调有哪些技术手段?
- 全参数微调(Full Fine-tuning)
对模型的全部参数进行训练更新,效果最好,但计算成本高、显存需求大。 - 部分参数微调
只更新模型的一小部分参数,其余参数保持冻结。
适合在单机或少量GPU环境下训练
可以从训练阶段的角度来这样划分
1️⃣ 继续预训练(Continued Pre-training / Domain Pre-training)
在模型预训练阶段之后继续使用领域数据训练,但仍然保持原来的训练目标(如语言建模)。
作用是让模型学习领域知识分布。
例如:
- 用大量医疗文献、法律文本、金融数据继续训练模型
- 让模型更理解专业术语和语境
特点:
- 数据量通常很大
- 不改变模型任务,只增强领域知识
2️⃣ 指令微调(Instruction Tuning / SFT)
使用指令-回答数据对模型进行监督微调,使模型学会按照指令回答问题。
例如:
1 | |
特点:
- 数据量相对较小
- 目标是让模型更会对话、更会执行任务
1.3 多模态 / 文件上传
图片上传时若出现图片过大、超时问题,该如何解决?
1 | |
可以从 客户端、上传方式、服务端配置 三个方面解决:
客户端压缩图片
在上传前对图片进行压缩或降低分辨率,例如通过 Canvas 或压缩库减少图片体积,降低上传时间。分片上传 / 断点续传
对大图片采用 分片上传,将文件拆分成多个 chunk 上传,服务端再合并,可以避免单次上传过大导致的超时问题。直传 OSS
采用 客户端直传 OSS(预签名 URL / STS),避免图片先经过业务服务器,减少服务器带宽压力,提高上传速度。调整服务端限制
在 Spring Boot / Nginx 中适当增加max-file-size、max-request-size或请求超时时间。
设计C端多张大图同时上传的方案?
- 客户端图片压缩
在上传前对图片进行压缩或降低分辨率,例如通过 Canvas 或图片压缩库将图片质量压缩到 70%~80%,减少上传体积,提高上传速度。
- 并发上传控制
多张图片不要一次性全部并发上传,而是采用 并发队列控制:
- 将图片放入上传队列
- 控制最大并发数(如 3~5 个)
示例流程:
1 | |
优点:
- 避免网络拥塞
- 防止浏览器或服务器压力过大
- 分片上传(大图)
如果单张图片较大(如 >5MB),采用 分片上传:
流程:
1 | |
优点:
- 避免单次上传超时
- 支持断点续传
- 直传对象存储(OSS)
推荐采用 客户端直传 OSS:
1 | |
优点:
- 减少业务服务器带宽压力
- 上传速度更快
- 支持 CDN 加速
- 上传进度与用户体验
前端需要展示:
- 单图上传进度
- 总体上传进度
- 失败重试提示
提升用户体验。
1.4 Nacos / 微服务
Nacos作为注册中心的作用是什么?
主要作用:
- 服务注册
- 服务发现
- 配置中心
Nacos 作为注册中心的主要作用是 实现微服务的服务注册与服务发现。
具体作用:
服务注册
微服务启动时会将自己的 IP、端口、服务名等信息注册到 Nacos。服务发现
其他服务调用时可以 从 Nacos 获取目标服务实例列表,而不需要写死地址。负载均衡支持
客户端(如 Spring Cloud LoadBalancer、OpenFeign)从 Nacos 获取多个实例后,可以进行 负载均衡调用。健康检查
Nacos 会通过 心跳机制检测服务实例是否存活,自动剔除异常实例。
服务调用时是否需要实时从Nacos拉取服务信息?
不需要。
一般会 缓存服务实例列表。
客户端通过 本地缓存 + 心跳机制更新。
服务启动时会 从 Nacos 拉取服务实例列表并缓存到本地,之后服务调用一般是 从本地缓存中获取实例信息并进行负载均衡,而不是每次都实时访问 Nacos。
当服务实例发生变化(如实例上下线)时,Nacos 会通过 推送机制或客户端定时拉取 更新本地缓存。
总结:
服务调用通常使用 本地缓存的服务列表,只有在实例变化时才会与 Nacos 同步更新,而不是每次调用都实时拉取。
注册中心存在什么坏处?
主要问题:
系统复杂度增加
网络依赖增强
注册中心可能成为单点故障
引入系统复杂度
需要额外部署和维护注册中心(如 Nacos、Eureka),增加系统架构复杂度。可能成为单点故障
如果注册中心集群不可用,新的服务实例可能无法注册,服务治理能力下降。一致性与延迟问题
服务实例上下线后,客户端缓存更新可能存在 短暂延迟,导致调用到失效实例。运维成本增加
需要维护注册中心集群、监控其健康状态和数据同步。
注册中心挂了服务会不可用吗?
不一定。
如果 注册中心挂了:
已有服务通常仍然可以调用
因为服务消费者会 缓存本地的服务实例列表,调用时使用本地缓存进行负载均衡。新的服务实例无法注册
新启动的服务无法注册到注册中心,其他服务也无法发现它。服务实例变化无法同步
如果有服务下线或扩容,客户端缓存无法及时更新,可能会调用到失效实例。
总结:
注册中心挂掉后,已有服务短时间内仍然可以正常调用,但服务治理能力会下降,新服务注册和实例更新会受到影响。
注册中心的注册表该如何设计?
注册中心的 注册表(Service Registry) 主要用于存储所有服务实例信息,一般采用 服务维度 → 实例维度 的结构设计。
- 核心数据结构
通常设计为 Map + 实例列表:
1 | |
示例:
1 | |
- 实例信息设计
每个 ServiceInstance 一般包含:
serviceName:服务名ip:服务IPport:端口instanceId:实例唯一IDmetadata:元数据(版本、环境等)status:健康状态lastHeartbeatTime:最后心跳时间
示例:
1 | |
能否用Caffeine做本地注册表?
可以,但不推荐。
因为:
- 本地缓存无法保证多节点一致性
- 更新不及时
Caffeine作为本地注册表的好处坏处?
优点:
- 访问速度快
- 减少网络调用
缺点:
- 数据一致性问题
- 更新延迟
注册中心和本地注册表之间的更新机制?
注册中心和本地注册表之间通常通过 订阅 + 推送 / 定时拉取 的方式进行更新。
基本机制:
服务启动时拉取
服务启动后从注册中心(如 Nacos)拉取服务实例列表,并缓存到本地注册表。订阅服务变更
客户端会向注册中心 订阅服务实例变化事件。变更推送更新
当有服务实例 上线、下线或健康状态变化时,注册中心会 推送变更通知给客户端。更新本地注册表
客户端收到通知后 更新本地缓存的服务实例列表。兜底定时同步
同时客户端还会 定时拉取注册表作为兜底机制,防止推送丢失。
1.5 OpenFeign / Sentinel
1.6 Elasticsearch
项目中如何构建ES搜索服务?
可以按 “数据同步 → 索引设计 → 查询实现 → 功能增强” 的流程回答:
1. 构建索引结构
根据药品信息设计 Elasticsearch 索引,例如字段包括:
drugName(药品名称,text 类型,用于全文检索)spec(规格)manufacturer(生产厂家)description(药品说明)createTime(时间字段)
对需要搜索的字段使用 分词器(如 IK 分词器)。
2. 数据同步到 ES
将数据库中的药品数据同步到 ES:
- 项目启动时进行 全量同步
- 新增或修改药品信息时,通过 异步消息或业务逻辑同步更新 ES 索引
3. 构建搜索接口
在后端实现搜索服务,例如:
- 使用
multi_match或match实现 关键词全文检索 - 使用
fuzzy或wildcard实现 模糊匹配
4. 支持高亮与分页
- 高亮:使用 ES
highlight功能对命中的关键词进行高亮显示 - 分页:通过
from + size实现分页查询
5. 返回搜索结果
将 ES 返回的结果解析为 DTO,返回给前端展示。
在项目中我主要做了三步:
- 设计 ES 索引结构,对药品名称和说明等字段使用 IK 分词实现全文检索
- 将 MySQL 中的药品数据同步到 ES,启动时全量同步,新增或修改时增量更新
- 实现搜索接口,使用
match/multi_match做关键词检索,并结合 highlight 实现关键词高亮、from+size 实现分页查询
这样就实现了药品信息的 全文检索、模糊匹配、高亮展示和分页查询。
深度分页如何优化?
在 Elasticsearch 中,深度分页(如 from + size 很大)会导致 性能下降和内存占用增加,因为 ES 需要先排序并丢弃大量数据。常见优化方式有:
- 使用
search_after(推荐)
基于上一页最后一条数据的 排序值继续查询,而不是使用 from。
特点:
- 不需要跳过大量数据
- 适合 无限滚动或翻页场景
只适合顺序翻页,不支持随机跳页,但可以避免深度分页性能问题。
- 限制最大分页深度
通过 index.max_result_window 限制最大分页,例如默认 10000 条,防止用户请求过深分页。
1.7 Canal
MySQL中binlog的作用是什么?
MySQL 中 **binlog(二进制日志)**主要用于记录数据库的 数据变更操作(如 INSERT、UPDATE、DELETE)。
主要作用:
主从复制
从库通过读取主库的 binlog,同步数据,实现 数据复制。数据恢复
结合全量备份,可以通过 重放 binlog 实现数据恢复(Point In Time Recovery)。数据同步 / 数据订阅
一些系统(如 Canal、Debezium)可以解析 binlog,实现 数据同步到 ES、MQ 等系统。
binlog模式
MySQL 的 binlog 有三种模式:
STATEMENT(语句模式)
记录执行的 SQL 语句,例如UPDATE user SET age=20 WHERE id=1。- 优点:日志体积小
- 缺点:某些函数或非确定性语句可能导致 主从数据不一致
ROW(行模式)
记录 每一行数据的变化(修改前和修改后)。- 优点:数据一致性好
- 缺点:日志体积较大
MIXED(混合模式)
MySQL 会 自动选择使用 STATEMENT 或 ROW。- 一般情况使用 STATEMENT
- 不安全语句使用 ROW
✅ 面试简答版:
MySQL 的 binlog 有 STATEMENT、ROW、MIXED 三种模式,分别记录 SQL 语句、行数据变化、或两者混合。生产环境通常推荐使用 ROW 模式以保证主从数据一致性。
1.8 二级缓存
Redis更新后多节点Caffeine数据不一致如何解决?
如果 Redis 更新后,多节点的 Caffeine 本地缓存出现数据不一致,常见解决方案有:
- 使用消息通知(推荐)
当 Redis 数据更新时,通过 MQ 或 Redis Pub/Sub 发送缓存失效通知,各节点收到消息后 删除或更新本地 Caffeine 缓存。
流程:
1 | |
- 设置本地缓存过期时间
给 Caffeine 设置 TTL(过期时间),即使未及时同步,也会在一定时间后自动失效,作为兜底方案。
- 定时刷新缓存
通过 定时任务定期刷新本地缓存,保证最终一致性。
✅ 面试简答版:
通常通过 发布订阅或消息队列实现缓存失效通知:当 Redis 更新时发送消息,各服务节点收到通知后删除本地 Caffeine 缓存,同时配合 TTL 过期机制作为兜底,保证缓存最终一致。
2 智能高并发秒杀系统
2.0 整体
高并发秒杀系统的整体设计流程是什么?
1️⃣ 活动准备阶段
在秒杀开始前,将秒杀商品和库存信息初始化到 MySQL,并提前预热到 Redis 中,减少活动开始后的数据库压力。
2️⃣ 活动开启
系统开启秒杀活动,用户开始发送秒杀请求。
3️⃣ 用户抢购请求处理
用户请求到达后,系统在 Redis 中进行库存扣减和资格校验,通过原子操作快速判断是否还有库存。
4️⃣ 库存抢购阶段
多个用户并发抢夺库存,当 Redis 中的库存被扣减到 0 时,说明商品已经售罄。
5️⃣ 最终计算阶段
系统统计成功抢购的用户请求,并生成对应的订单数据。
6️⃣ 异步落库阶段
通过 消息队列(MQ) 将成功订单异步写入 MySQL,完成订单持久化,同时更新相关状态。
7️⃣ 流程结束
库存售罄后秒杀结束,系统返回用户秒杀结果。
2.1 高并发秒杀策略
展开讲秒杀系统中多种高并发策略的实现、区别、优缺点,以及不同场景的选型依据?
1. synchronized 同步锁
实现方式:
在应用层对秒杀逻辑加 synchronized 锁,同一时间只允许一个线程对同一个商品执行库存扣减和订单创建。
优点:
- 实现简单
- 能保证线程安全,强一致性
- 逻辑清晰
缺点:
- 只能在 单机环境生效
- 锁竞争严重,并发能力差
- 不适合高并发场景
适用场景:
- 低并发秒杀
- 单体应用或功能演示场景
2. 数据库乐观锁
实现方式:
在数据库表中增加 version 字段或通过 update stock where stock > 0 的方式进行 CAS 更新库存,更新成功才算抢购成功。
优点:
- 依赖数据库保证一致性
- 强一致性
- 不需要应用层加锁
- 实现相对简单
缺点:
- 高并发时 大量请求会更新失败
- 数据库压力较大
- 吞吐量有限
适用场景:
- 中等并发场景
- 对数据一致性要求较高
3. 线程池 + Redis原子计数 + MQ异步落库
实现方式:
- 将库存预热到 Redis
- 用户请求进入 线程池 进行并发控制
- 使用 Redis原子计数(如 INCR ) 增加库存
- 抢购成功后将订单消息发送到 MQ
- 消费者 异步创建订单并落库 MySQL
优点:
- Redis保证 高并发库存扣减性能
- MQ实现 削峰填谷
- 大幅降低数据库压力
- 支撑高并发秒杀
缺点:
- 系统复杂度较高
- 需要处理 消息重复、幂等、缓存一致性问题
适用场景:
- 高并发秒杀系统
- 电商大促等高流量场景
仅判断库存大于零能否解决CAS的ABA问题?
不能解决。
原因:
CAS 的 ABA 问题指的是:一个值从 A → B → A 发生变化,但 CAS 只比较当前值是否等于 A,无法感知中间是否被修改过。
如果只判断 库存是否大于 0,只能保证库存不会变成负数,但无法判断库存是否在中间被其他线程修改过,因此 仍然可能存在 ABA 问题。
常见解决方案:
- 使用 版本号(version)或时间戳
- 通过 AtomicStampedReference 等带版本的 CAS 机制
不用原子类仅用Integer能否解决ABA问题?
不能解决。
- ABA问题本质
- ABA 问题发生在 CAS(Compare-And-Swap)操作中:某个变量值从 A → B → A,CAS 比较时只看当前值是否等于 A,无法感知中间是否被其他线程修改过。
- 如果你仅用
Integer(普通对象)进行比较或判断,比如if (stock > 0),这只是一个 值检查,而不是原子操作。
- Integer 的局限性
- 普通
Integer操作不是原子性的,多线程同时读写会出现 竞态条件。 - 即使用
Integer对象包装,也无法记录“中间是否被修改过”的版本信息。
- 正确做法
解决 ABA 问题需要 带版本号的 CAS 或 原子类,例如:
AtomicInteger+ 版本号/时间戳AtomicStampedReference
仅靠普通
Integer判断库存大于零 无法感知中间变化,ABA 问题依然存在。
分布式场景下如何解决ABA问题?
在 MySQL 分布式环境下,单机的乐观锁(基于 version 或 stock > 0 的 CAS 更新)本身 无法保证全局一致性,也容易出现 ABA 问题,因为多个节点可能同时读到同一个库存值或版本号,然后都尝试更新,导致冲突无法感知。解决方法通常有以下几种:
1️⃣ 全局版本号或时间戳
做法:
- 在库存表增加 全局唯一版本号(version 或 timestamp)字段
- 更新时使用:
1 | |
- 每次更新前读取最新版本号,确保并发节点看到的版本号不同
- 若版本号不匹配,则更新失败,需要重试
优点:
- 保留乐观锁思路
- 保证节点之间不会因为 ABA 问题重复扣减
缺点:
- 高并发下可能导致大量更新失败重试
- 数据库压力增加
2️⃣ 分布式锁 + 乐观锁
做法:
- 使用 Redis / ZooKeeper / etcd 等分布式锁,对库存修改操作加锁
- 在锁保护下执行 乐观锁 CAS 更新
优点:
- 分布式环境下可以保证全局唯一性
- ABA 问题消失
缺点:
- 分布式锁管理复杂,需要考虑锁超时、节点宕机、锁续期等
用线程池削峰填谷的好处、坏处分别是什么?
用 线程池削峰填谷 是秒杀系统中常见的手段,主要是对瞬时高并发请求进行平滑处理。可以这样简要说明:
✅ 好处
- 削峰平滑请求
- 将瞬时高并发的请求排队到线程池中,避免直接冲击后端资源(数据库、缓存等)。
- 控制并发数量
- 通过线程池大小限制同时处理的请求数,避免系统过载。
- 提高系统稳定性
- 避免高并发导致应用线程过多、CPU/内存消耗过大或崩溃。
- 资源复用
- 线程池复用线程,减少线程频繁创建和销毁的开销,提高性能。
❌ 坏处 / 局限性
- 不能保证全局顺序
- 多节点分布式场景下,每个节点独立线程池,无法统一全局顺序。
- 不能解决库存一致性问题
- 仅控制并发数量,库存仍需要 Redis 原子操作或数据库乐观锁保障。
- 线程池满时请求被拒绝或阻塞
- 如果瞬时请求过大,线程池队列满,会出现请求丢失或等待超时,需要额外限流策略。
- 复杂度增加
- 需要合理设置线程池大小、队列长度、拒绝策略,否则可能成为系统瓶颈。
线程池队列满了会发生什么?
线程池会触发 拒绝策略。
常见策略:
AbortPolicy(默认)
- 直接抛异常
CallerRunsPolicy
- 由调用线程执行
DiscardPolicy
- 丢弃任务
DiscardOldestPolicy
- 丢弃最旧任务
服务器扩容后,单机线程池在分布式场景下会导致什么问题?
问题是:
线程池隔离
每个实例的线程池是独立的。
可能导致:
- 流量不均衡
- 某些实例线程池满
- 某些实例空闲
在分布式场景下,如果服务器扩容了新节点,但每台服务器仍使用单机线程池,可能会导致以下问题:
- 请求分布不均:单机线程池只能控制本机并发,无法感知集群整体负载,可能导致部分节点过载,而其他节点空闲。
- 削峰失效:线程池的“削峰填谷”效果仅在单机生效,整个集群仍可能出现瞬时请求高峰,无法全局平滑流量。
- 资源利用不均:新节点可能闲置,而老节点线程池满载,整体资源利用率低。
- 延迟不稳定:部分节点排队等待线程执行,可能出现响应延迟差异,影响用户体验。
核心问题是:单机线程池只能做本地并发控制,无法协调分布式系统的整体负载。
单机场景下对比三种秒杀方案优劣
| 方案 | 优点 | 缺点 |
|---|---|---|
| synchronized | 实现简单 | 并发能力差 |
| 数据库乐观锁 | 数据一致性好 | 数据库压力大 |
| Redis+MQ | 并发能力强 | 系统复杂 |
秒杀场景中选择MongoDB而非MySQL的原因?
主要原因:
- 写入性能高
MongoDB适合高并发写入。
- 文档结构灵活
订单结构变化时无需修改表结构。
- 水平扩展能力强
支持分片。
MongoDB和MySQL核心区别
MySQL:
- 关系型数据库
- 强事务
- 表结构固定
MongoDB:
- 文档数据库
- JSON结构
- 灵活 schema
MongoDB的集群部署机制有哪些?
主要有三种:
- Replica Set(副本集)
主从复制。
- Sharding(分片集群)
水平扩展。
- Standalone
单节点。
MongoDB数据分片策略有哪些?
MongoDB主要有三种:
- Range Sharding
范围分片。
- Hash Sharding
哈希分片。
- Zone Sharding
区域分片。
MongoDB 的分片(Sharding)策略主要用于水平扩展,将数据分布到多个分片(Shard)上,提高读写吞吐量和存储能力。MongoDB 提供了几种主要的 分片键策略,决定了数据如何分布:
- 基于哈希的分片(Hashed Sharding)
原理:对分片键的值进行哈希计算,然后根据哈希值把数据均匀分布到各个分片。
优点:
- 自动均衡数据,避免热点分片。
- 写入均匀,不容易集中到某个节点。
缺点:
- 范围查询效率低,因为连续的值被打散。
- 不适合需要大量范围扫描的场景。
适用场景:高并发写入、均匀分布的键值。
- 基于范围的分片(Range Sharding)
原理:根据分片键的范围把数据划分到不同分片,例如用户ID 0–9999 在分片A,10000–19999 在分片B。
优点:
- 范围查询高效,连续数据落在同一个分片。
- 易于进行有序扫描。
缺点:
- 写入可能集中在部分分片(热点问题),导致负载不均衡。
适用场景:范围查询频繁、读操作多的业务场景。
如何保证MQ中的消息不丢失?
在消息队列(MQ)中保证消息不丢失,可以从 生产端、消息中间件和消费端 三个环节控制:
生产端
- 开启 消息持久化(Persistent),确保消息写入磁盘。
- 使用 同步发送(Sync Send)而非异步发送,保证发送成功确认。
消息中间件
- 持久化存储:将消息写入磁盘或可靠存储。
- 主从复制/集群模式:当节点宕机时,从节点可以接管。
- 幂等机制:保证重复消息不会影响业务。
消费端
- 手动确认(ACK):消费者处理完消息后再确认,避免处理失败导致消息丢失。
- 重试机制:消费失败时可重试或入死信队列(DLQ)。
核心原则:消息在任意环节失败时,都能依赖持久化、确认和重试机制确保消息最终不丢失。
2.2 责任链模式
基于责任链模式做的预处理器机制有什么好处?
职责单一、解耦清晰
- 每个预处理器只负责自己的一类初始化任务(如数据库、缓存、状态机),彼此独立。
- 新增或修改某一环节时,不会影响其他环节,便于维护。
灵活可扩展
- 可以动态调整责任链的顺序或增加新的预处理器,例如增加日志、限流、验证等步骤。
- 无需修改核心流程逻辑,只需新增节点即可。
统一处理流程
- 责任链模式统一管理各个初始化步骤,保证秒杀前所有必要资源都被正确准备,减少遗漏。
增强容错与可控性
- 可以在某个节点失败时中断责任链,或做重试、补偿处理,提高系统稳定性。
代码复用性高
- 各个预处理器可以在不同业务场景复用,例如秒杀、团购、预约活动都可共享同一套链式初始化逻辑。
为什么选择责任链而非状态机模式?
原因:
责任链适合 流程型处理。
- 责任链模式的用途
关注点:顺序执行、模块化处理、解耦初始化逻辑。
适用场景:秒杀前的 预处理,比如:
- 数据库初始化
- 缓存加载
- 状态机状态设置
特点:每个节点只做自己的事情,按顺序执行,链条可以动态扩展或插入节点。
核心价值:保证初始化步骤按正确顺序依次完成,且每个步骤独立、可复用、易维护。
- 状态机模式的用途
关注点:状态管理 + 事件驱动,处理流程的合法性。
适用场景:秒杀活动的 运行时流程,比如:
- 秒杀开始、抢购中、秒杀结束
- 用户下单、支付、发货等状态流转
特点:用状态和事件驱动流程转换,保证流程合法性和可追踪。
核心价值:控制整个秒杀活动的状态变化,防止非法操作或状态异常。
预处理逻辑本质是“顺序任务执行”,而不是“状态流转”
- 数据库、缓存、状态机初始化只要按顺序执行即可,不需要复杂的状态和事件控制。
责任链更灵活
- 可以动态增加、删除或重排初始化步骤,状态机实现起来复杂且不必要。
关注点分离
- 责任链关注初始化任务
- 状态机关注运行时流程和状态合法性
- 两者结合使用,既保证初始化正确,又保证活动执行安全。
还了解哪些设计模式?
好的,我给你把你列的这几个设计模式做一个 简明扼要的总结,包括核心概念和典型应用场景(适合秒杀/分布式系统理解):
1. 策略模式(Strategy)
- 核心思想:定义一系列算法(策略),将每个算法封装起来,使它们可以互相替换。
- 特点:算法独立、可动态切换、符合开闭原则。
- 应用:秒杀库存扣减策略(Redis Lua 脚本、乐观锁、CAS),根据不同活动动态选择策略。
2. 责任链模式(Chain of Responsibility)
- 核心思想:将请求沿链传递,每个处理节点决定处理或交给下一个节点。
- 特点:解耦发送者与接收者,灵活可扩展、顺序执行。
- 应用:秒杀预处理器(数据库初始化 → 缓存加载 → 状态机设置),请求验证、订单处理链条。
3. 状态机模式(State)
- 核心思想:允许对象在内部状态改变时改变行为,把状态封装成独立对象。
- 特点:状态清晰、流程可控、避免大量 if/else。
- 应用:秒杀活动状态管理(未开始、进行中、已结束)、订单状态流转(待支付 → 已支付 → 已完成)。
4. 单例模式(Singleton)
- 核心思想:确保类只有一个实例,并提供全局访问点。
- 特点:全局唯一、节约资源、控制访问。
- 应用:全局配置管理、Redis 客户端、MQ 连接池、日志管理。
5. 代理模式(Proxy)
- 核心思想:为对象提供代理,控制访问或增强功能,而不修改原对象。
- 特点:可做权限控制、懒加载、接口增强。
- 应用:接口限流、权限校验、RPC 远程调用代理。
6. 模板方法模式(Template Method)
- 核心思想:定义算法骨架,将具体步骤延迟到子类实现。
- 特点:复用算法框架、子类定制实现细节。
- 应用:秒杀流程框架(统一执行流程:校验库存 → 扣减库存 → 下单 → 通知),不同活动逻辑由子类实现。
✅ 总结表格
| 模式 | 核心 | 特点 | 秒杀/分布式应用场景 |
|---|---|---|---|
| 策略模式 | 封装可替换算法 | 动态切换算法 | 库存扣减策略 |
| 责任链模式 | 顺序处理请求 | 解耦、可扩展 | 预处理器初始化 |
| 状态机模式 | 根据状态改变行为 | 流程可控 | 活动/订单状态管理 |
| 单例模式 | 全局唯一实例 | 节约资源、全局访问 | 配置、连接池、日志 |
| 代理模式 | 代理控制访问 | 权限/增强/懒加载 | 接口限流、RPC代理 |
| 模板方法模式 | 算法骨架 + 子类实现 | 复用框架 | 秒杀流程统一框架 |
实现两个经典的线程安全的单例模式?
饿汉式单例
1 | |
懒汉式单例
1 | |
2.3 状态机
为什么选择Spring StateMachine?
原因:
Spring生态集成好
支持状态与事件驱动
支持持久化
状态流转逻辑清晰
与 Spring 框架无缝集成
- 依赖注入:可以直接注入 Bean 到状态机的动作(Action)中,方便访问服务、DAO、缓存等。
- 配置统一:可使用
@Configuration+ Java DSL 或 XML 配置状态机,和 Spring 其它组件风格一致。 - 事务支持:Action 执行可以参与 Spring 事务管理,保证状态变更与业务操作一致。
- 支持持久化
基于 Redis 持久化
将状态机上下文序列化存入 Redis
目标:状态机的当前状态、上下文变量等信息可以存储到外部存储中(数据库、Redis等)。
作用:系统宕机或重启后,可以恢复状态机到上次的状态,避免流程丢失或错误。
秒杀系统中状态机的状态有哪些?
常见状态:
1 | |
依靠什么事件流转?
事件驱动:
例如:
1 | |
事件触发后状态发生转换。
为什么采用状态机模式?
传统写法:
1 | |
问题:
- 状态逻辑混乱
- 可维护性差
状态机好处:
- 状态清晰
- 状态转换明确
- 扩展方便
采用 状态机模式(State Pattern) 的核心原因是 将复杂流程和状态管理明确化、可控化,并解耦业务逻辑,尤其适合秒杀、订单或活动管理这类有明确状态和状态流转的业务场景。具体原因如下:
1️⃣ 明确状态和事件
秒杀系统或订单系统通常有多种状态:
- 秒杀活动:
未开始 → 进行中 → 已结束 - 订单:
待支付 → 已支付 → 已完成/已取消
- 秒杀活动:
状态机模式把 状态和事件分离出来,每个状态只关心自己允许的事件和转换规则,避免用大量 if/else 判断。
2️⃣ 状态和行为绑定
每个状态可以绑定进入/离开动作(Action),事件触发动作(Action),保证业务逻辑和状态切换一致:
- 例如:活动进入“进行中”状态 → 初始化库存、通知前端
- 用户下单 → 库存扣减、生成订单
3️⃣ 避免状态错误
状态机模式强制定义状态转换图(State + Event):
- 防止非法操作或非法状态切换
- 易于在高并发下保证流程正确性
5️⃣ 可维护性和扩展性高
- 新增状态或事件只需修改状态机配置或新增状态类
- 不需要修改核心业务逻辑,符合 开闭原则
1. AI医疗服务平台
1.0 整体
说说RAG、MCP的技术流程和技术交互栈分别是怎样的?
什么是向量化?
做向量化匹配时的主要标准是什么?
MCP协议的本质构成是什么?
尝试过在本地部署AI服务吗?
部署本地大模型服务时需要注意哪些问题?
该医疗系统能承受的QPS是多少,如何测试?
你认为当前这个医疗平台存在哪些优化点、做得不好的地方?
什么是大模型幻觉问题,你的项目中是如何尝试解决的?
该医疗平台在医疗问诊场景下对用户的核心价值是什么?
1.1 大模型对话/Prompt工程/对话上下文记忆/MongoDB
如何对大模型的回答和行为进行约束?
使用提示词(Prompt)时需要注意哪些要点?
若大模型给出错误医疗建议,如何从系统层面发现/拦截?
如何解决大模型对话的上下文超限(无法回话)问题?
MongoDB和MySQL的核心区别是什么?
自定义Memory组件的作用是什么,为何选用MongoDB存储对话上下文而非MySQL?
医疗问诊场景下的提示词工程体系是如何构建的?
除了角色定义、结构化输出约束,提示词工程还有哪些优化方法?
1.2 RAG
RAG检索增强的好处是什么?没有RAG直接调用大模型会存在什么问题?
除了RAG,还有哪些方式能为大模型提供领域上下文?
微调有哪些技术手段?
详细介绍AI医疗微服务平台的核心功能,重点讲RAG的落地实现?
RAG知识库的数据源来自哪里?
各类医疗文档(PDF/Word/TXT)如何转化为RAG的知识库,核心步骤是什么?
1.3 多模态/OSS/文件上传
图片上传时若出现图片过大、超时问题,该如何解决?
设计C端多张大图同时上传的方案,该如何设计?
项目中图像识别功能的识别对象、核心目的是什么?
1.4 Nacos/微服务/网关/Sa-token
Nacos作为注册中心的作用是什么?为什么需要注册中心?
服务调用时是否需要实时从Nacos拉取服务信息?有什么机制减少对Nacos的请求?
注册中心存在什么坏处?如果注册中心挂了,所有服务会不可用吗?
注册中心的注册表该如何设计?用什么存储类型/数据结构实现?
Redis挂了该如何处理?能否用Caffeine做本地注册表?
Caffeine作为本地注册表的好处、坏处分别是什么?
注册中心和本地注册表之间的更新机制是什么?
1.5 OpenFeign/Sentinel
1.6 ES
项目中是如何构建一个ES搜索服务的?
数据量特别大时,分页查询该如何优化(深度分页问题)?
1.7 Canal
MySQL中binlog的作用是什么?有几种模式,分别是什么?
1.8 二级缓存
二级缓存(Caffeine+Redis)中,如何解决Redis更新后多节点Caffeine数据不一致的问题?
2. 智能高并发秒杀系统
2.0 整体
高并发秒杀系统的整体设计流程是什么?
2.1 高并发秒杀策略
展开讲秒杀系统中多种高并发策略的实现、区别、优缺点,以及不同场景的选型依据;
仅判断库存大于零能否解决CAS的ABA问题?不用原子类仅用Integer能否解决ABA问题?分布式场景下如何解决ABA问题?
用线程池削峰填谷的好处、坏处分别是什么?线程池队列满了会发生什么?
服务器扩容后,单机线程池在分布式场景下会导致什么问题?
单机场景下对比synchronized锁、数据库乐观锁、Redis+MQ异步落库三种秒杀方案的优劣;
秒杀场景中选择MongoDB而非MySQL的原因?二者核心区别是什么?
MongoDB的集群部署机制有哪些?数据分片的策略有哪些,该策略的专业名称是什么?
如何保证MQ中的消息不丢失?
2.2 责任链模式
基于责任链模式做的预处理器机制有什么好处?没有该模式会有什么问题?
除了责任链模式,还有哪些更好的设计模式解决if-else嵌套问题?为什么选择责任链而非状态机模式?
除了责任链、状态机模式,还了解哪些设计模式?
2.3 状态机
选择Spring StateMachine做状态机管理的原因?调研过其他状态机框架吗?该框架的优势是什么?
秒杀系统中状态机的具体状态有哪些,依靠什么事件流转?
为什么采用状态机模式处理秒杀业务,与传统写法相比的好处是什么?
2.4 AI对话
3. 八股
3.1 JAVA基础
讲一下Java对象的生命周期?
用过HashMap吗?讲一下HashMap中哈希的原理?
equals和==的区别是什么?
哈希冲突的解决办法有哪些?HashMap是如何解决哈希冲突的?
HashMap的初始容量、负载因子分别是什么?为什么初始容量是2的幂次方?为什么负载因子是0.75?
HashMap是线程安全的吗?
Hashtable相较于ConcurrentHashMap解决线程安全问题有什么不好的地方?
3.2 JUC
线程池的参数有哪些,你都是怎么设置的?
讲一下你对synchronized的了解?
你理解的线程安全是什么?线程安全的三要素是什么?
synchronized保证了线程安全的哪些特性?它是怎么保证可见性的?
synchronized可以修饰什么东西?修饰静态方法时锁的是什么?修饰非静态方法时锁的是什么?
synchronized有哪些特点(可重入、公平性等)?
synchronized的锁膨胀优化过程是什么?
什么是CAS操作?
synchronized锁和Redis分布式锁的区别及各自原理?
使用线程池的注意事项有哪些,核心参数该如何设置?
Java为何会出现并发问题,需要用锁来处理?
若在Controller中定义int字段,每次请求对其+1会出现什么问题?
3.3 JVM
介绍一下JVM内存结构?
谈一下你对垃圾回收的理解?
垃圾的定义有两种方法,除了可达性分析还有一种是什么?分别详细讲解这两个方法。
垃圾回收算法的种类(新生代、老年代相关GC)有哪些,分别讲讲?
详细说说标记清除、标记整理、复制这三种垃圾回收算法?
Minor GC、Full GC一般分别用哪种垃圾回收算法,结合存活对象/垃圾对象的数量分析?
对于Java的内存优化有什么理解和想法?有没有用过内存友好的数据结构?如何避免内存泄漏的经验?
Java对象头的作用是什么,与锁有什么关联?
JVM中工作内存和主内存的区别是什么?
哪些对象会进入JVM的老年代?
若新生代内存不足,会如何处理?
介绍Java的类加载机制,核心步骤有哪些?
讲解Java的双亲委派机制,包含哪些类加载器、核心原理及设计好处?
3.4 JAVA框架
介绍一下IOC和AOP?
3.5 数据库
MySQL的索引有哪些类型,分别有什么特点?
聚簇索引和非聚簇索引的区别是什么?
B+树作为索引结构的核心特点是什么,与哈希表、B树的区别?
3.6 Redis
介绍一下redis持久化的机制?
讲解Redis跳表的底层数据结构,以及Redis为何选用跳表而非B+树、红黑树等数据结构?
简述红黑树的核心规则和特性?
3.7 微服务
4. 算法
- 反转链表
- 二叉树的层序遍历
- 实现线程安全的单例模式
- 将一个从小到大的数组,和一个从大到小的数组,合并成一个从小到大的数组
5. 面经
5.1 快手JAVA一面 KPI面(30分钟/3.9)
- 问个人情况?
- 问论文,介绍一下论文?
- 讲一下你的项目流程?
- 线程池的参数有哪些,你都是怎么设置的?
- 写一个动态规划算法,一般要注意什么?
- 介绍一下IOC和AOP?
- 介绍一下redis持久化的机制?
- 介绍一下你的项目中是如何构建一个ES搜索服务的?
- 介绍一下JVM内存结构?
算法:反转链表
5.2 B站客户端一面 (30分钟/3.11)
(一)背景与岗位匹配类
- 你做过客户端开发的工作吗?
- 你是只用过Java,Kotlin这门语言你听过吗?
(二)Java基础 - JVM与垃圾回收类
- 讲一下Java对象的生命周期?
- 谈一下你对垃圾回收的理解?
- 垃圾的定义有两种方法,除了可达性分析还有一种是什么?分别详细讲解这两个方法。
- 垃圾回收算法的种类(新生代、老年代相关GC)有哪些,分别讲讲?
- 详细说说标记清除、标记整理、复制这三种垃圾回收算法?
- Minor GC、Full GC一般分别用哪种垃圾回收算法,结合存活对象/垃圾对象的数量分析?
- 对于Java的内存优化有什么理解和想法?有没有用过内存友好的数据结构?如何避免内存泄漏的经验?
(三)Java基础 - 集合与哈希类
- 用过HashMap吗?讲一下HashMap中哈希的原理?
- equals和==的区别是什么?
- 哈希冲突的解决办法有哪些?HashMap是如何解决哈希冲突的?
- HashMap的初始容量、负载因子分别是什么?为什么初始容量是2的幂次方?为什么负载因子是0.75?
- HashMap是线程安全的吗?
- Hashtable相较于ConcurrentHashMap解决线程安全问题有什么不好的地方?
(四)Java基础 - 并发编程类
- 讲一下你对synchronized的了解?
- 你理解的线程安全是什么?线程安全的三要素是什么?
- synchronized保证了线程安全的哪些特性?它是怎么保证可见性的?
- synchronized可以修饰什么东西?修饰静态方法时锁的是什么?修饰非静态方法时锁的是什么?
- synchronized有哪些特点(可重入、公平性等)?
- synchronized的锁膨胀优化过程是什么?
- 什么是CAS操作?
(五)AI应用与认知类
- 你用过OpenAI、ChatGPT等AI工具吗?你是怎么看这些AI工具的?
- 你具体是怎么用AI工具做项目、写代码的?AI生成的代码占比大概有多少?
- 你觉得使用AI工具存在哪些风险和不顺手的地方?
5.3 美团JAVA一面(45分钟/3.12)
(一)高并发秒杀系统项目深挖
- 展开讲秒杀系统中多种高并发策略的实现、区别、优缺点,以及不同场景的选型依据;
- 仅判断库存大于零能否解决CAS的ABA问题?不用原子类仅用Integer能否解决ABA问题?分布式场景下如何解决ABA问题?
- 用线程池削峰填谷的好处、坏处分别是什么?线程池队列满了会发生什么?
- 服务器扩容后,单机线程池在分布式场景下会导致什么问题?
- 单机场景下对比synchronized锁、数据库乐观锁、Redis+MQ异步落库三种秒杀方案的优劣;
- 秒杀场景中选择MongoDB而非MySQL的原因?二者核心区别是什么?
- MongoDB的集群部署机制有哪些?数据分片的策略有哪些,该策略的专业名称是什么?
(二)设计模式相关
- 基于责任链模式做的预处理器机制有什么好处?没有该模式会有什么问题?
- 除了责任链模式,还有哪些更好的设计模式解决if-else嵌套问题?为什么选择责任链而非状态机模式?
- 除了责任链、状态机模式,还了解哪些设计模式?
(三)Spring相关
- 选择Spring StateMachine做状态机管理的原因?调研过其他状态机框架吗?该框架的优势是什么?
(四)AI医疗微服务平台项目深挖
- RAG检索增强的好处是什么?没有RAG直接调用大模型会存在什么问题?
- 除了RAG,还有哪些方式能为大模型提供领域上下文?
- 微调有哪些技术手段?
- Nacos作为注册中心的作用是什么?为什么需要注册中心?
- 服务调用时是否需要实时从Nacos拉取服务信息?有什么机制减少对Nacos的请求?
- 注册中心存在什么坏处?如果注册中心挂了,所有服务会不可用吗?
- 注册中心的注册表该如何设计?用什么存储类型/数据结构实现?
- Redis挂了该如何处理?能否用Caffeine做本地注册表?
- Caffeine作为本地注册表的好处、坏处分别是什么?
- 注册中心和本地注册表之间的更新机制是什么?
(五)缓存相关
- 二级缓存(Caffeine+Redis)中,如何解决Redis更新后多节点Caffeine数据不一致的问题?
(六)算法编程题
- 实现二叉树的层序遍历,分析算法时间复杂度;
- 手写线程安全的单例模式(后续因不会换题)。
5.4 B站客户端二面(20分钟/3.12)
(一)岗位匹配与客户端基础类
- 你有过端上应用级开发/DEMO编写的经验吗?
- 对端上的标准技术栈有了解吗?
- 了解端上的消息通信方案是怎样的吗?
- 安卓中比较常用的本地数据存储方式有哪些?
- 了解端上的一些UI组件吗?
- 对标准的视频播放协议流程有了解吗?
(二)AI核心技术类
- 说说RAG、MCP的技术流程和技术交互栈分别是怎样的?
- 什么是向量化?
- 做向量化匹配时的主要标准是什么?
- MCP协议的本质构成是什么?
- 尝试过在本地部署AI服务吗?
- 部署本地大模型服务时需要注意哪些问题?
(三)服务端高并发与缓存类
高并发业务场景下,设计服务端缓存架构需要注意哪些东西?
(四)方案设计类
如果要做本地数据库的升级方案,过程中需要注意哪些问题?
(五)项目与技术分享类
有没有做过有趣/有技术深度的东西,可以做基础分享?
5.5 高德JAVA一面(55分钟/3.13)
(一)AI医疗微服务平台项目深挖
- 如何对大模型的回答和行为进行约束?
- 使用提示词(Prompt)时需要注意哪些要点?
- 若大模型给出错误医疗建议,如何从系统层面发现/拦截?
- 如何解决大模型对话的上下文超限(无法回话)问题?
- 图片上传时若出现图片过大、超时问题,该如何解决?
- 设计C端多张大图同时上传的方案,该如何设计?
- 数据量特别大时,分页查询该如何优化(深度分页问题)?
- 该医疗系统能承受的QPS是多少,如何测试?
(二)数据库核心知识
- MySQL中binlog的作用是什么?有几种模式,分别是什么?
- MongoDB和MySQL的核心区别是什么?
- MySQL的索引有哪些类型,分别有什么特点?
- 聚簇索引和非聚簇索引的区别是什么?
- B+树作为索引结构的核心特点是什么,与哈希表、B树的区别?
(三)并发编程与锁
- synchronized锁和Redis分布式锁的区别及各自原理?
- 使用线程池的注意事项有哪些,核心参数该如何设置?
- Java为何会出现并发问题,需要用锁来处理?
- JVM中工作内存和主内存的区别是什么?
- 若在Controller中定义int字段,每次请求对其+1会出现什么问题?
- Java对象头的作用是什么,与锁有什么关联?
(四)MQ核心知识
如何保证MQ中的消息不丢失?
(五)高并发秒杀系统项目深挖
- 高并发秒杀系统的整体设计流程是什么?
- 秒杀系统中状态机的具体状态有哪些,依靠什么事件流转?
- 为什么采用状态机模式处理秒杀业务,与传统写法相比的好处是什么?
(六)JVM核心知识
- 哪些对象会进入JVM的老年代?
- 若新生代内存不足,会如何处理?
(七)算法编程题
将一个从小到大、一个从大到小的两个数组,合并成一个整体从小到大的新数组。
5.6 美团大模型应用开发一面(一个小时/3.20)
(一)Java后端核心基础
- 讲解Redis跳表的底层数据结构,以及Redis为何选用跳表而非B+树、红黑树等数据结构?
- 简述红黑树的核心规则和特性?
- 介绍Java的类加载机制,核心步骤有哪些?
- 讲解Java的双亲委派机制,包含哪些类加载器、核心原理及设计好处?
(二)AI医疗微服务平台项目深挖
- 详细介绍AI医疗微服务平台的核心功能,重点讲RAG的落地实现?
- 项目中图像识别功能的识别对象、核心目的是什么?
- 自定义Memory组件的作用是什么,为何选用MongoDB存储对话上下文而非MySQL?
- 医疗问诊场景下的提示词工程体系是如何构建的?
- 除了角色定义、结构化输出约束,提示词工程还有哪些优化方法?
- RAG知识库的数据源来自哪里?
- 各类医疗文档(PDF/Word/TXT)如何转化为RAG的知识库,核心步骤是什么?
- 你认为当前这个医疗平台存在哪些优化点、做得不好的地方?
- 什么是大模型幻觉问题,你的项目中是如何尝试解决的?
- 该医疗平台在医疗问诊场景下对用户的核心价值是什么?
(三)高并发秒杀系统项目
秒杀系统是否和AI做了结合,具体是如何结合的?
(四)AI工具使用与认知
- 了解RAG、Agent吗?是否实际做过Agent相关开发?
- 日常使用AI编程工具(如Coding)的频率和方式是什么?
- AI生成代码的质量如何,使用过程中遇到了哪些问题,如何解决?
(五)算法编程题
- 根据指定字符串构建多叉树;
- 手写算法题:给定整数数组,找出最大子数组和;
- 要求阐述算法思路并完成代码实现。