PostgreSQL 做向量数据库:pgvector 在 RAG 中的实战与多场景适配

你可能不需要一个独立的向量数据库。


目录


为什么是 PostgreSQL

做 RAG 的第一步是选向量数据库。市面上的选项很多:Milvus、Qdrant、Weaviate、Pinecone、Chroma……但对于已经在用 PostgreSQL 的 Java 项目来说,有一个选项经常被低估——pgvector。

pgvector 是 PostgreSQL 的一个扩展,给 PG 加上了向量存储和相似度检索的能力。装上它之后,你的关系型数据库就同时承担了结构化数据存储和向量检索两个角色,不需要额外部署一套向量数据库。

这意味着几件事:

  • 少一套基础设施。不用再维护 Milvus 的 etcd + MinIO + pulsar,不用再给 Qdrant 单独配持久化。向量数据和业务数据在同一个库里,备份、迁移、监控一套搞定。
  • 事务一致性。业务数据和向量数据在同一个事务里写入,不存在"数据写进去了但向量还没生成"的中间态。
  • SQL 的全部能力。向量检索的结果可以直接 JOIN 业务表做元数据过滤,不需要在应用层拼接。比如"找到和用户问题最相似的 5 条知识,且只返回当前用户有权限看的",一条 SQL 搞定。

这三点在 RAG 场景下特别有价值。RAG 不是纯向量检索——它需要把检索结果和业务上下文组合起来喂给 LLM。如果向量数据库和业务数据库是两套系统,这个组合过程会变得很复杂。


pgvector 核心能力速览

pgvector 目前的稳定版本是 0.7.x,核心能力如下:

支持的距离度量

度量 运算符 适用场景
余弦距离(cosine) <=> 文本 embedding 最常用,衡量方向相似性
L2 欧氏距离 <-> 图像 embedding,衡量绝对距离
内积(inner product) <#> 已归一化的向量,等价于余弦但更快
L1 曼哈顿距离 <+> 稀疏向量场景

支持的向量类型

类型 维度上限 存储 适用场景
vector 16000 float32 标准向量,通用场景
halfvec 16000 float16 存储减半,精度损失极小,大规模数据首选
sparsevec 16000 稀疏存储 稀疏 embedding(如 SPLADE)
bit 16000 二进制 二值量化后的向量,存储最小

halfvec 是 0.7.0 引入的,存储空间直接砍半,对百万级以上的数据集意义很大。bit 类型配合二值量化,可以在牺牲少量精度的情况下把存储再压缩 32 倍。


索引策略:HNSW vs IVFFlat

pgvector 提供两种索引类型,选错了会严重影响查询性能。

HNSW(推荐)

Hierarchical Navigable Small World,当前最主流的 ANN(近似最近邻)索引算法。

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 200);
  • m:每个节点的最大连接数,越大召回率越高但索引越大。默认 16,生产环境建议 16-32。
  • ef_construction:构建时的搜索宽度,越大索引质量越好但构建越慢。默认 64,建议 200。
  • 查询时参数 ef_search:运行时通过 SET hnsw.ef_search = 100; 调整,越大召回率越高但查询越慢。

HNSW 的特点:查询速度快(毫秒级),召回率高(95%+),但索引构建慢、内存占用大。适合数据写入不频繁但查询密集的 RAG 场景——知识库文档写入一次,反复被检索。

IVFFlat

Inverted File with Flat Quantization,基于聚类的索引。

CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);
  • lists:聚类中心数量。经验法则是数据行数的平方根到 4 倍之间。
  • 查询时需要先执行 SET ivfflat.probes = 10;,probes 越大召回率越高但越慢。

IVFFlat 的特点:索引构建快,内存占用小,但召回率不如 HNSW,且对数据分布敏感。适合数据量大、写入频繁、对召回率要求不那么苛刻的场景。

实际选型:做 RAG,绝大多数情况选 HNSW。知识库文档的写入频率远低于查询频率,HNSW 的查询性能优势远大于构建成本。


和专用向量数据库的对比

这是大家最关心的问题:pgvector 和 Milvus、Qdrant、Weaviate 这些专用向量数据库比,差在哪?

性能基准

以下数据基于 ANN-Benchmarks 和各官方 benchmark 的综合参考,百万级 768 维向量、单机环境:

数据库 查询延迟(p95) 召回率(@10) 索引构建时间 内存占用
pgvector 0.7(HNSW) 5-10ms 95-98% 中等 中等
Qdrant 2-5ms 97-99%
Milvus 2.x 3-8ms 96-99% 中等
Weaviate 5-12ms 95-98% 中等 中等
Chroma 10-30ms 90-95%
Elasticsearch(dense_vector) 8-15ms 93-97%

pgvector 的查询延迟比 Qdrant 和 Milvus 慢 2-3ms,召回率差 1-2 个百分点。对于 RAG 场景来说,这个差距几乎无感——LLM 的推理延迟在秒级,向量检索的 5ms 和 3ms 的差异用户感知不到。

功能对比

能力 pgvector Milvus Qdrant Weaviate
向量检索
元数据过滤 ✅ SQL 原生
混合检索(向量+关键词) ✅ pg_trgm + tsvector ✅ Sparse + Dense
多向量字段 ✅ 多列
流式数据摄入 ❌ 批量更优
GPU 加速
分布式水平扩展 ❌(需 Citus) ✅ 原生
事务支持 ✅ ACID
SQL JOIN ✅ 原生

pgvector 的优势

不需要额外基础设施。这是最大的优势。如果你的项目已经在用 PostgreSQL,加一个扩展就行,不用再引入一套新的数据库系统。

事务一致性。写入文档的同时生成向量,在同一个事务里完成。专用向量数据库做不到这一点——你写完业务数据还得异步把向量推过去,中间有个时间窗口数据是不一致的。

SQL 的表达能力。RAG 的检索结果经常需要和业务数据组合。“找到最相似的 5 条知识,排除已归档的,只返回当前用户所在部门的”——pgvector 一条 SQL 搞定,专用向量数据库得在应用层拼两次查询。

运维成本低。一套数据库,一套备份策略,一套监控。不用学新的查询语言,不用维护额外的集群。

pgvector 的短板

没有原生分布式。单节点 PG 的向量检索性能有上限。数据量超过千万级,查询延迟会明显上升。专用向量数据库(Milvus、Qdrant)原生支持分片和水平扩展,能线性提升吞吐。

没有 GPU 加速。百万级以上数据集的索引构建,Milvus 的 GPU 模式比 pgvector 快一个数量级。但对 RAG 场景来说,索引构建是一次性的,查询才是高频操作,这个差距影响有限。

流式摄入能力弱。pgvector 的 HNSW 索引在高频写入时性能下降明显。如果你的场景是实时流式写入(比如日志分析),专用向量数据库更合适。


RAG 召回效果:pgvector 够不够用

RAG 的效果取决于两个环节:召回生成。pgvector 负责的是召回环节——从知识库中找到和用户问题最相关的文档片段,喂给 LLM 生成回答。

召回质量由三个因素决定,pgvector 在其中的角色各不相同。

1. Embedding 模型选择

向量的质量取决于生成它的 embedding 模型。pgvector 只负责存储和检索,不负责生成向量。但它决定了你能用什么维度的向量——pgvector 支持最高 16000 维,覆盖了市面上所有主流 embedding 模型。

模型 维度 适用场景
OpenAI text-embedding-3-small 1536 通用文本,性价比最高
OpenAI text-embedding-3-large 3072 高精度文本检索
BGE-M3 1024 中文场景首选
GTE-Qwen2 768-1536 中文长文本
CLIP ViT-L/14 768 图文多模态
ColPali 128 文档图像端到端检索

2. 分块策略

文档需要切分成小块(chunk)再 embedding。分块大小直接影响召回精度:

  • 太小(< 200 字):语义完整度不够,检索到了但 LLM 缺少上下文
  • 太大(> 1000 字):噪声多,检索精度下降
  • 推荐:300-500 字,带 50-100 字重叠

pgvector 在这个环节没有特殊优势或劣势——分块在应用层完成,和向量数据库无关。但 pgvector 的 SQL 能力让你可以在存入时就标记 chunk 的来源文档、段落位置、章节标题,检索时直接通过 SQL 过滤,这比专用向量数据库的元数据过滤更灵活。

3. 检索策略

这是 pgvector 真正发挥作用的地方。

纯向量检索:把用户问题 embedding 后,查 top-K 最相似的 chunk。

SELECT content, 1 - (embedding <=> query_vector) AS similarity
FROM documents
ORDER BY embedding <=> query_vector
LIMIT 5;

混合检索(Hybrid Search):向量检索 + 关键词检索,两者结果合并排序。这对 RAG 效果提升很大——用户问"Redis 的过期策略怎么配",纯向量检索可能召回语义相似但不是用户想要的内容,关键词检索能精确匹配"Redis"和"过期策略"。

pgvector 0.7.0 引入了稀疏向量支持(sparsevec 类型),可以直接在数据库里做 sparse + dense 混合检索:

-- RRF (Reciprocal Rank Fusion) 混合检索
WITH vector_results AS (
  SELECT id, content,
         ROW_NUMBER() OVER (ORDER BY embedding <=> query_vector) AS rank
  FROM documents
  ORDER BY embedding <=> query_vector
  LIMIT 20
),
keyword_results AS (
  SELECT id, content,
         ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank
  FROM documents
  WHERE search_vector @@ query
  ORDER BY ts_rank(search_vector, query) DESC
  LIMIT 20
)
SELECT id, content,
       1.0 / (60 + vector_results.rank) + 1.0 / (60 + keyword_results.rank) AS rrf_score
FROM vector_results
FULL OUTER JOIN keyword_results USING (id, content)
ORDER BY rrf_score DESC
LIMIT 5;

带权限过滤的检索:这是 pgvector 的强项。

SELECT content, 1 - (embedding <=> query_vector) AS similarity
FROM documents
WHERE department_id = :user_dept
  AND is_archived = false
ORDER BY embedding <=> query_vector
LIMIT 5;

专用向量数据库做这件事需要先检索再在应用层过滤,或者用它们自带的元数据过滤功能——但表达能力远不如 SQL。


多场景 RAG:文字、图片、视频

RAG 不只是"文字搜文字"。随着多模态 embedding 模型的成熟,图片和视频也可以参与 RAG 召回。pgvector 在不同场景下的适配度不一样。

文字 RAG

这是最标准的场景,也是 pgvector 最擅长的。

流程:文档 → 分块 → embedding → 存入 pgvector → 用户提问 → 检索 top-K → 喂给 LLM。

pgvector 的表现:完全够用。百万级文档 chunk 的检索延迟在 5-10ms,配合混合检索和元数据过滤,召回质量不输专用向量数据库。

关键优化点:

  • halfvec 类型替代 vector,存储减半,对召回率影响极小
  • HNSW 索引的 ef_search 参数根据召回率要求调整,RAG 场景建议 80-120
  • 开启 pg_trgm 扩展做模糊关键词匹配,配合向量检索做 hybrid search

图片 RAG

场景:用户上传一张图片(比如一张截图、一个设计稿),系统从知识库中找到相关的文档、教程或相似图片。

流程:图片 → CLIP/SigLIP embedding → 存入 pgvector → 用户上传查询图片 → 检索 → 返回相关文档。

pgvector 的表现:完全胜任。CLIP 的 embedding 维度是 768,pgvector 处理这个维度毫无压力。图片的 L2 距离检索和文本的余弦距离检索可以共存在同一张表里。

-- 图文混合知识库
CREATE TABLE multimodal_docs (
  id UUID PRIMARY KEY,
  content TEXT,                    -- 文本内容(文本 chunk 时有值)
  image_url TEXT,                  -- 图片 URL(图片时有值)
  modality VARCHAR(10),           -- 'text' | 'image'
  text_embedding vector(768),     -- 文本 embedding
  image_embedding vector(768),    -- 图片 embedding(CLIP)
  metadata JSONB                  -- 其他元数据
);

-- 用图片查相似图片
SELECT image_url, 1 - (image_embedding <=> query_image_vector) AS similarity
FROM multimodal_docs
WHERE modality = 'image'
ORDER BY image_embedding <=> query_image_vector
LIMIT 5;

-- 用图片查相关文本(跨模态检索,CLIP 的核心能力)
SELECT content, 1 - (text_embedding <=> query_image_vector) AS similarity
FROM multimodal_docs
WHERE modality = 'text'
ORDER BY text_embedding <=> query_image_vector
LIMIT 5;

跨模态检索是 CLIP 的核心价值——用一张图去搜文字,或者用一段文字去搜图。pgvector 不需要任何特殊配置就能支持,因为对它来说 768 维的向量就是 768 维的向量,不管它来自文本模型还是视觉模型。

视频 RAG

场景:用户问一个问题,系统从视频知识库中找到最相关的视频片段,返回给用户。

流程:视频 → 关键帧提取 → 每帧 CLIP embedding → 存入 pgvector → 用户提问 → 检索 → 返回相关视频片段。

pgvector 的表现:能用,但需要注意数据量管理。

视频 RAG 的特殊之处在于数据量。一个 10 分钟的视频,按每 2 秒提取一帧,会产生 300 个向量。100 个视频就是 3 万个向量。这个量级 pgvector 完全没问题。但如果视频库规模到万级甚至十万级,向量数量会到千万级,这时候 pgvector 的查询延迟会开始上升。

优化策略:

  • 降采样:不需要每帧都存。场景变化检测(基于帧间差异)后只存有变化的关键帧,可以减少 50-70% 的向量量
  • 分层索引:先用粗粒度的场景级 embedding 做第一轮筛选,再在候选范围内用帧级 embedding 精排
  • 归档策略:把不常访问的旧视频的向量数据移到冷存储(可以用 PG 的分区表实现),保持热数据表的索引效率
-- 视频知识库
CREATE TABLE video_segments (
  id UUID PRIMARY KEY,
  video_id UUID,
  start_time INTERVAL,           -- 片段起始时间
  end_time INTERVAL,             -- 片段结束时间
  frame_embedding vector(768),   -- 关键帧 embedding
  transcript TEXT,               -- 该片段的语音转文字
  transcript_embedding vector(1536), -- 转文字的 embedding
  metadata JSONB
);

-- 用文字搜视频片段(跨模态)
SELECT video_id, start_time, end_time, transcript,
       1 - (frame_embedding <=> query_text_vector) AS visual_similarity,
       1 - (transcript_embedding <=> query_text_vector) AS text_similarity,
       GREATEST(
         1 - (frame_embedding <=> query_text_vector),
         1 - (transcript_embedding <=> query_text_vector)
       ) AS best_similarity
FROM video_segments
ORDER BY best_similarity DESC
LIMIT 5;

视频 RAG 的召回策略通常是"视觉 + 语音转文字"双通道。pgvector 可以在同一个查询里同时比较两种 embedding 的相似度,取最高分,这比在应用层分别查两次再合并要简洁得多。

多场景适配度总结

场景 pgvector 适配度 说明
文字 RAG ★★★★★ 完全胜任,百万级 chunk 毫无压力
图片 RAG ★★★★★ CLIP embedding 存储检索完全够用
视频 RAG(小规模) ★★★★☆ 千级视频没问题,注意分帧策略
视频 RAG(大规模) ★★★☆☆ 万级以上建议用专用向量数据库或混合方案

Spring AI + pgvector 实战配置

如果你的项目用 Spring AI(和我一样),集成 pgvector 非常简单。

依赖

implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter'

配置

spring:
  ai:
    vectorstore:
      pgvector:
        index-type: HNSW           # 索引类型
        distance-type: COSINE_DISTANCE  # 距离度量
        dimensions: 1536            # embedding 维度
        table-name: documents       # 向量表名
        schema-name: public
        initialization-mode: always # 自动建表
    openai:
      api-key: ${LLM_API_KEY}
      base-url: ${LLM_BASE_URL}

使用

@Autowired
private VectorStore vectorStore;

// 写入
List<Document> docs = List.of(
    new Document("NVC 四步法:观察、感受、需要、请求",
        Map.of("category", "nvc-theory", "source", "book"))
);
vectorStore.add(docs);

// 检索
List<Document> results = vectorStore.similaritySearch(
    SearchRequest.query("什么是非暴力沟通的观察步骤")
        .withTopK(5)
        .withSimilarityThreshold(0.7)
);

Spring AI 的 VectorStore 抽象让你可以无缝切换底层实现——从 pgvector 换到 Milvus 或 Qdrant,只改依赖和配置,代码不用动。这降低了后期迁移的成本。


选型建议

先用 pgvector,遇到瓶颈再换。

大多数 RAG 项目的知识库规模在几万到几百万 chunk 之间,pgvector 的 HNSW 索引在这个量级下的查询性能完全够用。过早引入专用向量数据库会增加运维复杂度,而你可能根本用不到它的分布式和 GPU 加速能力。

什么时候该考虑专用向量数据库

  • 向量数据超过千万级,pgvector 的查询延迟开始影响用户体验
  • 需要实时流式写入(比如实时日志分析、实时推荐)
  • 需要 GPU 加速索引构建
  • 团队已经有 Milvus/Qdrant 的运维经验

混合方案:热数据放 pgvector(最近的文档、高频访问的知识库),冷数据放专用向量数据库。这种方案兼顾了性能和运维成本。

pgvector 不是要取代 Milvus 或 Qdrant。它是在"再加一套基础设施"和"凑合用现有数据库"之间提供了一个务实的中间选项——对你已有的 PostgreSQL 做一个扩展,就能获得 80% 的向量检索能力,而运维成本几乎为零。

对大多数 RAG 项目来说,这个 trade-off 是值得的。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐