返回案例和资讯
技术分享2026/07/147 分钟阅读华胥星辰团队

向量库为什么要和主库分开:一次存储分层的取舍

向量库只存 chunk_id、user_id 和向量本身,正文和元数据留在主库。以及为什么我们同时用了关系库、向量库和图数据库三套存储。

pgvectorNeo4j存储架构检索
向量库为什么要和主库分开:一次存储分层的取舍

写在前面 做 RAG 的人大多会遇到同一个问题:向量、正文、元数据到底放在一起还是分开?这篇讲我们的选择和理由,也讲这个选择带来的麻烦。


一、最省事的做法

最直接的方案是:一张表,同时存正文、元数据和向量。查询时一次搞定,代码简单。

小规模下这样完全没问题。我们最初也是这么做的。

问题出现在数据量上来之后:

  • 向量列很占空间。 一个高维向量(如 1536 维)本身就不小,和正文放在一起会让表迅速膨胀。一条记录的正文通常是几千到几万字符,而一个向量就是 6KB。当切片很细时,向量的存储体积可能超过正文。
  • 检索和读取的访问模式完全不同。 向量检索是计算密集的、扫描全表的;读正文是点查、走索引的。放在一起,两种负载互相干扰。
  • 索引重建代价高。 向量索引需要定期重建,而它和业务数据在同一张表上,重建期间会影响正常读写。
  • 备份策略冲突。 向量可以从正文重新生成,正文丢了就真丢了。它们的备份优先级不一样,却被绑在一起。

一个具体的数据对比

假设 10 万条文档切片,每条正文平均 2000 字符,向量 1536 维:

  • 放一起:单表约 1.5GB(正文) + 0.6GB(向量) = 2.1GB,加上向量索引额外 1-2GB
  • 分开:主库 1.5GB(只需关注业务读写),向量库 0.6GB(专门为检索优化)

2GB 的表在单机上是完全可控的。但 100 万条切片时,情况就不同了——向量存储 6GB,向量索引 10-20GB,重建索引可能需要几分钟。放在一张表上,这几分钟里所有依赖这张表的查询都会受影响。


关系库 / 向量库 / 图数据库的职责边界
关系库 / 向量库 / 图数据库的职责边界


二、我们的拆法

现在的结构是:

主库(PostgreSQL)——存资源元数据、分段正文、用户、权限、订单等全部业务数据。这是权威数据源。

向量库(独立的 pgvector 实例)——只存三样东西:chunk_id、拥有者 user_id、向量本身。

检索流程变成两步:

代码
① 在向量库里按相似度检索  →  拿到一组 chunk_id
② 用 chunk_id 回主库读正文和元数据  →  组装上下文

多了一次查询,但换来几件事:

  • 向量库可以按检索负载独立扩容,不影响主库
  • 向量索引重建不会波及业务读写
  • 主库的备份和迁移策略不受向量数据拖累
  • 向量数据可以随时从正文重新生成,丢了不致命

user_id 冗余在向量库里,是为了在检索阶段就能做权限过滤——不能先检索出全部结果、回主库读完再过滤。那样越权数据已经进入过候选集,风险和成本都不划算。

性能收益

拆分之后,主库的读写性能得到了明显改善:

  • 向量索引重建不再影响业务查询——重建期间主库的写操作无感知
  • 主库的备份体积缩小了约 40%,备份和恢复时间同步缩短
  • 检索压力独立扩容——如果某天搜索量激增,只需扩向量库实例,不影响其他业务

三、代价

这个拆分不是白拿的:

  • 多一跳网络往返。 在延迟敏感的场景需要通过批量读取来摊平。我们的做法是一次性拿到 chunk_id 列表后,用 IN 查询批量读回正文,把 N 次查询合并为 1 次。
  • 两套数据要保持一致。 正文删了,向量得跟着删;正文改了,向量得重新生成。我们靠任务队列来做这件事,但仍然需要定期的一致性校对。
  • 运维对象变多。 两个数据库实例,两套监控。

一致性校对的实现

我们写了一个定时任务,每天晚上低峰期运行:

  1. 从主库导出所有 chunk_id(约 20 万条,分页查询)
  2. 从向量库导出所有 chunk_id
  3. 做差集比较:
    • 向量库有、主库没有的 → 标记为"孤儿向量",加入清理队列
    • 主库有、向量库没有的 → 标记为"缺失向量",加入重建队列
  4. 两个队列分别处理

这个校对任务跑一次大概 3-5 分钟,对线上无影响。


四、为什么还要一个图数据库

除了主库和向量库,我们还用了图数据库存记忆图谱的拓扑。三套存储,职责是这样分的:

存储回答的问题
关系库"这份资料的元数据是什么、正文在哪、谁有权限"
向量库"和这个问题语义最接近的片段是哪些"
图数据库"这个概念和哪些概念相关、隔几跳能连到哪里"

第三类查询用关系型数据库做多表自连接也能实现,但写起来痛苦、性能也差。多跳关系查询和可视化是图数据库的主场。

一个图查询的例子

"找出和『强化学习』相关的所有主题,以及这些主题相关的洞察,以及这些洞察引出的行动项"——这是三跳查询。

在 SQL 里相当于三次 JOIN,每次 JOIN 都是一次自连接。在 Cypher 里是一行:

cypher
MATCH (t:Topic {name: '强化学习'})
  -[:RELATES_TO*1..2]->(related:Topic)
  -[:RELATES_TO]->(insight:Insight)
  -[:LEADS_TO]->(action:Action)
RETURN related.name, insight.content, action.content

不是 SQL 做不了,是图数据库在这个场景下语义更自然、查询更快。

需要说明的是:图谱的权威数据仍然存在关系库里,图数据库存的是同步过去的拓扑副本。这样做的原因是权限、事务和溯源这些能力在关系库里更成熟,而图数据库只需要服务查询和展示。


五、一个判断标准

如果你在纠结要不要拆,可以问自己三个问题:

  1. 向量数据和业务数据的增长速度是否差异很大? 差异大就该拆。
  2. 是否需要独立扩容检索能力? 需要就该拆。
  3. 团队能否承担多一套存储的运维? 不能就先别拆。

架构没有标准答案,只有和当前规模匹配的选择。我们拆是因为已经被前面几个问题实际困扰过——不是因为拆开更"专业"。

想在你的业务里试试同样的做法?

告诉我们业务问题与数据现状,我们会给出方案建议与落地路径。