RAG向量数据库对比:MySQL扛100万向量的真相
老李那只透明玻璃杯里,浮着密密麻麻的枸杞,我扫了一眼,少说也得一百来粒。
没等我缓过神,老李开门见山:“你那套 RAG 检索系统,数据库用的什么?”
“MySQL。”
老李差点把刚含进嘴里的水喷我脸上:“就 MySQL?向量检索你也敢用 MySQL?”
“怎么了李哥?MySQL 就不能管向量了?一百万条 chunk 我照样能收拾得服服帖帖。”
看他那副急眼的样子,我笑了:“李哥,开个玩笑嘛,活跃一下气氛,这下我不紧张了。向量检索那块我用的 ElasticSearch,既能做语义检索,又能做关键字索引,混合检索一把梭哈。”
老李这人真不错,愣是没发火,依旧和颜悦色。接着问:“那你这个 RAG 系统,检索精确率怎么评估的?具体怎么测?”
(内心独白:这下算是碰到真面试了,要是所有面试官都像老李这么温和就好了。)
核心内容
01、检索精确率如何评估?具体测试方法是什么?
“李哥,我在派聪明 RAG 项目里专门做过一轮评估。评估检索效果,行业里常用的指标有三个:精确率(Precision)、召回率(Recall)和 MRR(Mean Reciprocal Rank)。”
精确率衡量的是检索返回的文档中真正相关的比例。比如返回了 10 个 chunk,其中 7 个和问题相关,精确率就是 70%。
召回率看的是所有相关文档中被成功检索出来的比例。比如知识库中总共有 15 个相关 chunk,只返回了 7 个,召回率就是 46.7%。
MRR 关注的是第一个正确结果出现的排名,排名越靠前分值越高。用户最在意的往往是“第一条结果能否命中需求”,MRR 恰好刻画这一点。
具体测试流程是这样:先构建评估数据集。标注 200 到 500 组 QA 对,每组包含一个问题和对应的标准答案 chunk。然后批量将这些问题输入检索模块,获取返回结果,再与标注结果做对比,计算出上述三个指标。派聪明 RAG 上线前跑了一轮评估,精确率从最初的 68% 优化到了 89%,主要靠两个手段:一是换用了更优的 embedding 模型,二是引入重排模型对检索结果做二次排序。”
老李追问:“重排模型用的哪一款?”
“用了 bge-reranker-v2-m3,这个模型对中文语义理解能力很强,推理速度也可以接受,P95 延迟在 50ms 以内。”
老李点点头:“评估体系搭建得还算扎实。”
02、上传文件的数量上限是多少?
老李接着问:“上传文件数量上限和单个文件大小上限分别是多少?”
“这个需要分两层来讲——业务层限制和系统层限制。业务层面,我们限制单次上传不超过 20 个文件,单文件不超过 50MB。用户主要上传 PDF 和 Word 文档,50MB 基本能覆盖 99% 的常见文档。”
“系统层面,真正的瓶颈在于文档解析和向量化过程。一个 50MB 的 PDF 可能有几百页,解析成文本、切分为 chunk、逐个向量化,整个流程可能需要几分钟。如果用户一次上传 20 个这种大文件,后台任务队列会迅速堆积。所以我们做了异步处理:用户上传后立即返回成功,后台用 Kafka 慢慢消化。前端展示一个进度条,处理完成后通知用户。”
老李追问:“如果有人恶意上传大量垃圾文件怎么办?”
“我们做了多重限流。按用户维度限制每天上传总量不超过 500MB,按 IP 维度限制并发上传数,同时还设定了每天的 Embedding 额度上限。文件类型也做了白名单,只允许 PDF、Word、TXT、Markdown 这几种。”
03、文档数量达到百万级怎么办?
老李靠在椅子上,表情变得认真:“如果文档数量到了百万级,存储和检索架构怎么设计?压力很大怎么处理?”
“这个问题要分层来看。派聪明的架构是 MySQL 存储文档元数据,ElasticSearch 存储向量和关键词,两者协同工作。”
先说存储层。MySQL 只存文档元数据——文件名、文件路径、上传时间、文档分类、所属用户等。向量数据全部放在 ES 里,MySQL 没有大字段,存储压力很小,百万级文档也就是几 GB 的数据量,单表就能扛住。如果真要优化,可以按业务分库,比如法律文档一个库、技术文档一个库;或者做冷热分离,超过 6 个月未被访问的文档归档到冷存储。不过当前量级还用不上这些。
真正的存储大头在 ES。百万级文档,假设每个文档平均切成 50 个 chunk,那就是 5000 万条 chunk。每条 chunk 存一个 2048 维向量,光向量数据就是 5000 万 × 2048 × 4 字节 ≈ 400GB。ES 的存储优化有几个方向。第一,使用稠密向量索引。ES 8.x 之后支持 HNSW 索引,向量检索不再需要暴力遍历,建索引后检索速度能提升 10 倍以上。第二,冷热分层。ES 支持热数据放 SSD,冷数据放 HDD,兼顾检索性能和存储成本。”
检索层怎么优化?ES 天然支持向量检索和关键词检索,混合检索可直接实现。压力大的话有几个优化思路。第一,先过滤再向量搜索。用户的查询通常附带元数据条件,比如“帮我找法律相关的文档”。先在 ES 里用 term 查询缩小候选集,再做向量相似度计算,计算量就小很多。第二,调整 HNSW 参数。ES 的 HNSW 索引有两个关键参数:m(每层邻居数)和 ef_search(检索时的候选数)。m 越大索引越准但内存占用越大,ef_search 越大检索越准但速度越慢。实测下来 m=16、ef_search=100 能取得准确率和延迟之间的较好平衡。第三,缓存热门查询。很多 RAG 系统 80% 的查询集中在 20% 的热门问题上。把这些问题的检索结果缓存在 Redis 里,命中率能到 40% 以上,直接省掉向量计算。”
老李追问:“2048 维的向量,检索延迟能接受吗?”
“2048 维确实比 1024 维慢一点,但差异没有想象中大。HNSW 索引的检索复杂度是 O(log N),与维度关系不大。压测下来,5000 万条 2048 维向量,P99 延迟在 100ms 以内,完全可以接受。”
老李点点头:“ES 存向量 + MySQL 存元数据,这个架构没有问题。”
04、文档上传时切片大小设置为多少?
老李继续追问:“文档上传时切片大小设定的多少?为什么选这个值?为什么不用 1MB 或 10MB?”
“chunk 大小设置的是 512 token,overlap 设为 64 token。”
为什么是 512?第一,检索精度。我们用的通义 text-embedding-v4 模型,虽然最大支持 8192 token 输入,输出 2048 维向量,但实测下来 chunk 越长,语义越分散,embedding 的区分度反而下降。512 token 是在精确率和信息量之间反复调参后找到的最优值。第二,语义完整性。太小的 chunk(比如 100 token)容易把一句话切断,检索出来的内容缺乏上下文,模型无法理解。太大的 chunk(比如 2000 token)包含的主题过多,检索精度下降——一个 chunk 里聊了三个话题,究竟因为哪个话题被检索出来?第三,token 预算。大模型上下文窗口有限。如果每个 chunk 太大,能塞进去的 chunk 数量就少。512 token 的 chunk,一次检索返回 5 个,加上 prompt 模板,总共不到 4000 token,给生成留足了空间。”
“为什么不用 1MB 或 10MB?因为 chunk 的单位是 token 而不是字节。1MB 的文本大约有 50 万字,那已经不是 chunk 了,而是整本书。10MB 更离谱。说白了,切片的目的是把长文档拆成‘语义单元’,每个单元表达一个相对独立的主题。这个粒度与存储大小无关,与语义有关。按字节切跟按句号切一样不靠谱,只有按语义切才有意义。后来我们还做了一个优化——递归切分。先按段落切,如果段落超过 512 token 再按句子切。这样尽量保证每个 chunk 都是完整的段落或几个完整句子,不会把一句话从中间劈开。”
老李满意地点了点头。
05、多机部署怎么做?
老李话锋一转:“多机部署考虑过吗?需要改动哪些地方?”
“考虑过,但派聪明 RAG 目前还是单机部署,因为业务量还没到需要多机的程度。不过架构设计时已经考虑了扩展性。如果要做多机部署,主要考虑三个地方。第一个是文件存储。我们已经用了 MinIO 做对象存储,多机环境下天然支持共享访问,这块不用改。第二个是任务队列。文档解析和向量化是异步任务,我们已经用了 Kafka,多台机器可以消费同一个 topic,天然支持多机。但要注意 partition 的分配策略,确保任务不会被重复消费。第三个是 Session 管理。Session 要从本地存储迁移到 Redis,实现 Session 共享。”
06、水平扩展怎么做?
老李紧跟着问:“怎么做到水平扩展?”
“水平扩展的核心是无状态。只要服务是无状态的,挂再多的机器都行,前面加个负载均衡就完事。RAG 系统里有三类服务:文档上传服务、检索服务、生成服务。检索服务天然是无状态的——接收一个 query,去数据库里搜,返回结果。任何一台机器都能处理,不依赖本地状态。生成服务也是无状态的——接收 prompt,调大模型 API,返回结果。文档上传服务稍微复杂一点,因为涉及文件落盘和异步任务。但我们文件存 MinIO、任务走 Kafka,所以它也是无状态的。因此水平扩展的改造重点就是把状态外置——文件存 MinIO、任务存 Kafka、Session 存 Redis、数据存 MySQL。服务本身不保存任何状态,随时可以扩缩容。”
07、哪些接口是无状态的?
老李接着问:“有哪些接口是无状态的?”
“搜索接口是无状态的。输入 query,返回检索结果,不依赖任何本地状态。生成接口是无状态的。输入 prompt + context,调 LLM API,返回生成结果。文档列表查询接口是无状态的。读 MySQL,返回数据。健康检查接口是无状态的。返回 OK 就完事。有状态的主要是两个:文件上传接口(如果文件存本地的话)和 WebSocket 连接(因为 WebSocket 是长连接,连接状态绑定在具体的服务器实例上)。”
08、视频音频怎么做 RAG 检索?
老李突然换了个方向:“视频音频怎么检索?语音转文字成本很高怎么解决?”
“视频音频 RAG 的核心思路是先把它们转成文本或其他可向量化的表示,再走传统 RAG 流程。核心流程是这样的:把音频通过 ASR(自动语音识别)转成文字稿,文字稿按时间戳切分成片段,每个片段做 Embedding 向量化存到向量库里。用户提问时做语义检索,找到最相关的文字片段,然后基于这些片段生成回答,同时返回对应的时间戳。”
“ASR 模型的选择有几个主流方向。开源的可以用 Whisper(OpenAI 开源,效果极佳,支持多语言)、FunASR(阿里开源的中文 ASR)、SenseVoice(阿里最新开源的多模态语音模型)。商用 API 可以用阿里云语音识别、腾讯云 ASR、火山引擎 ASR 等。Whisper 是综合性价比最高的选择,自己部署成本不高,效果接近商用 API。这个方案适合纯音频场景,比如播客检索、客服通话分析、会议录音问答。视频场景如果不需要画面信息也可以用这个方案,把视频里的音轨提取出来做 ASR 就行。”
“如果视频里的画面信息很重要,比如教学视频里的板书、商品视频里的展示、监控视频里的事件,那纯 ASR 就不够了,需要把画面信息也利用起来。核心流程是这样的:把视频按一定频率抽帧(比如每秒抽一帧或每个关键帧抽一张),用多模态 Embedding 模型把每一帧图像转成向量,存到向量库里。用户提问时用同一个多模态 Embedding 模型把问题向量化(多模态模型可以同时处理文本和图像),做语义检索找到最相关的视频帧,然后基于这些帧的时间戳定位到视频片段。”
“多模态 Embedding 模型的选择主要是 CLIP 系列。OpenAI 的 CLIP 是开山之作,中文场景下可以用 Chinese-CLIP 或者 BGE-M3 的多模态版本。这个方案的好处是能利用画面信息,缺点是计算成本高,因为视频帧的数量通常很大。一个一小时的视频按每秒一帧抽就是 3600 帧,每帧都要做 Embedding 和存储,对资源消耗比较大。”
有没有多模态方案?最新的趋势是用视觉大模型直接理解视频内容生成结构化描述。比如用 Qwen-VL、InternVL、GPT-4V 这类模型,把视频片段送进去让模型生成详细的文字描述(包括画面内容、人物动作、场景变化),然后把这些描述按时间戳切分做向量化检索。这个方案的好处是描述质量非常高,因为视觉大模型能理解画面里的语义关系,不只是单纯的图像 Embedding。坏处是成本高,每段视频都要调用大模型生成描述,处理速度慢,token 消耗大。”
09、为什么用 WebSocket
老李问了最后一个问题:“为什么用 WebSocket?WebSocket 多服务器怎么部署?”
“SSE 是单向的,服务端往客户端推数据,客户端只能被动接收。大部分 AI 产品用 SSE 就够了,因为场景就是‘我问你答’,服务端流式吐 token,客户端渲染就完事。但 RAG 场景有一个关键需求——客户端需要主动中断生成。用户问了一个问题,大模型还在吐 token,用户一看方向不对,想点‘停止生成’。SSE 做不到优雅中断,客户端只能关闭整个连接再重新建连。WebSocket 是双向通信,客户端随时可以发一条 stop 消息给服务端,服务端收到后立刻停止生成,连接还在,下一轮对话直接复用。另外,RAG 的处理流程比较长——文档检索 → 结果重排 → prompt 拼装 → 大模型生成,整个链条可能要好几秒。用 WebSocket 可以在每个阶段给前端推送进度,比如‘正在检索...’、‘正在生成...’,客户端也可以在任何阶段发消息取消任务,交互体验比 SSE 灵活很多。”
“WebSocket 多服务器怎么部署?WebSocket 是长连接,用户 A 连到了服务器 1,后续所有消息都得发给服务器 1,不能发给服务器 2——因为服务器 2 上没有这条连接。解决方案有两种。第一种是粘性会话(Sticky Session)。在负载均衡器(比如 Nginx)上配置,让同一个用户的请求始终路由到同一台服务器。配置很简单,Nginx 加一行 ip_hash 就行。缺点是某台服务器挂了,上面的所有连接都断了。第二种是消息广播。多台服务器之间用 Redis Pub/Sub 或者消息队列做消息同步。服务器 1 收到要推送给用户 A 的消息,先发布到 Redis 频道,所有服务器都订阅这个频道,持有用户 A 连接的那台服务器负责推送。这种方案更优雅,服务器挂了用户重连到其它服务器就行。派聪明 RAG 目前用的是第一种,因为简单,够用。如果后续要做高可用,会切到第二种。”
简历要点
很多小伙伴做完 RAG 项目,简历上只写一句“基于 LLM 实现了一个 RAG 问答系统”,一笔带过。面试官看到这种描述根本提不起兴趣。简历得有区分度,要让面试官看完就想追问细节。这里提供一个参考模板:
项目名称:派聪明 RAG 智能问答系统
项目简介:基于 Spring Boot + MySQL + Redis 构建的企业级 RAG 系统,支持多格式文档上传、智能切片、向量化检索和大模型流式问答,已在技术社区实际部署运行。
技术栈:Spring Boot、MySQL、Redis、Kafka、ElasticSearch、MinIO、text-embedding-v4(Embedding,2048 维)、bge-reranker-v2-m3(重排)、WebSocket
核心职责:
- 设计并实现文档切片引擎,采用 512 token 递归切分策略,结合段落感知和句子边界检测,检索精确率从 68% 提升到 89%
- 基于 ES HNSW 向量索引 + 混合检索架构,支撑百万级 chunk 的语义检索,2048 维向量 P99 延迟控制在 100ms 以内
- 引入 bge-reranker-v2-m3 重排模型对检索结果做二次排序,Top-5 命中率提升 23%,MRR 从 0.61 提升到 0.78
- 设计 WebSocket + Redis Pub/Sub 的流式推送架构,支持多服务器部署下的实时消息同步和连接高可用
- 构建异步文档处理流水线(Kafka + MinIO),实现文件上传、解析、向量化的全流程异步化,单日处理能力达 5000+ 文档
结语
写到这里,想跟正在准备 AI 面试的小伙伴说几句实在话。
别刻意去追 Agent,别总觉得 RAG 已经过时了——实际上如果深挖,RAG 能触达很多底层原理。
AI 时代浮躁得很,技术迭代也极快。就像 OpenClaw 已经被爱马仕 Agent 取代一样,但底层原理始终相通。不要让自己一直追逐新名词,而是要沉下心来啃透核心原理。也只有这样,你才能真正驾驭这个 AI 时代。