pgvector 实战:把向量检索"塞"进关系数据库,一条 SQL 搞定联合查询
脱敏说明:本文基于前述销售知识库项目的真实用法整理,不依赖任何特定云厂商,全部 SQL 和代码均可在自建 PostgreSQL 上运行。
一、什么时候不需要独立向量数据库
很多团队一做 RAG 就先部署一套独立向量数据库,但如果你的业务数据本来就在 PostgreSQL 里,引入独立向量库意味着多一个组件、多一套备份、多一次网络跳转,还无法在一条 SQL 里把业务过滤和向量检索合起来。
pgvector 是 PostgreSQL 的向量搜索扩展。一句话总结:把向量数据库的核心能力塞进了关系数据库 。关系数据和向量数据同库同表,WHERE 业务条件 ORDER BY 向量距离 LIMIT N 一条语句完成。对数据量在百万级以内、团队已熟悉 PostgreSQL 的场景,它通常是最优解。
二、安装与启用
使用官方镜像开箱即用(本文写作时 pgvector 已发布 0.8.x 版本):
yaml
services:
db:
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app123
POSTGRES_DB: knowledge
连库后执行一次(建议放到初始化脚本里):
sql
CREATE EXTENSION IF NOT EXISTS vector;
三、核心数据类型 vector 与三种距离
建表时维度必须和你的 Embedding 模型输出一致:
sql
CREATE TABLE knowledge_chunks (
id serial PRIMARY KEY,
content_block text NOT NULL,
embedding vector(1024), -- 1024 维,维度由嵌入模型决定
keywords jsonb,
created_at timestamp DEFAULT now()
);
三种距离运算符,这是 pgvector 最核心的知识点:
| 运算符 | 含义 | 适用场景 |
|---|---|---|
<-> |
L2 欧氏距离 | 通用,值越小越相似 |
<=> |
余弦距离 | 文本语义检索首选 |
<#> |
负内积距离 | 向量已归一化时 |
注意 <=> 返回的是距离(0~2),不是相似度;1 - 余弦距离 才是余弦相似度:
sql
SELECT id,
1 - (embedding <=> CAST(:vec AS vector)) AS similarity
FROM knowledge_chunks
WHERE embedding IS NOT NULL
ORDER BY embedding <=> CAST(:vec AS vector)
LIMIT 15;
Python 侧通过官方驱动与 ORM 集成:
python
from pgvector.sqlalchemy import Vector
from sqlalchemy import Column, Integer, Text
class KnowledgeChunk(Base):
__tablename__ = "knowledge_chunks"
id = Column(Integer, primary_key=True)
content_block = Column(Text)
embedding = Column(Vector(1024))
四、索引:从小规模精确搜索到 HNSW
不建索引
默认全表精确扫描,100% 召回,10 万行以内完全够用。项目初期建议先不建索引------简单、零调参。
HNSW(推荐)
sql
CREATE INDEX idx_chunks_embedding ON knowledge_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
| 参数 | 含义 | 调优方向 |
|---|---|---|
m |
每节点连接数(默认 16) | 越大越准、索引越慢 |
ef_construction |
建索引搜索宽度(默认 64) | 同上 |
查询时用 SET hnsw.ef_search = 100;(默认 40)动态调整搜索精度。HNSW 建索引慢、查询快、支持增量插入,是生产首选。
IVFFlat
sql
CREATE INDEX ... USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
建索引快,但需要先有数据做聚类、新数据增量后质量下降,目前更多被 HNSW 取代。
距离与算子族(ops)必须对应:余弦距离配 vector_cosine_ops,L2 配 vector_l2_ops,内积配 vector_ip_ops。
五、最容易翻车的点:带业务过滤的向量查询
一个典型陷阱:
sql
SELECT * FROM knowledge_chunks
WHERE category = 'objection' -- 高选择性过滤
ORDER BY embedding <=> :vec LIMIT 10;
索引只返回"全局最近"的少量候选,过滤后可能所剩无几,表现为"加了条件后结果又少又差"(即过度过滤问题)。pgvector 0.8 的**迭代索引扫描(Iterative Index Scans)**专门解决这个问题:首轮不满足过滤条件时自动继续扩大扫描,直到凑够结果。这也是升级到 0.8 最实际的理由之一。
六、0.8 版本带来的关键新能力
本文项目落地时用到的主要是基础 vector 类型;而 pgvector 在 0.7、0.8 两代补齐了生产级拼图(2026 年最新稳定版为 0.8.6):
- halfvec 半精度向量。
halfvec(1024)内存占用约为vector的一半,召回损失很小;且支持最高 4000 维向量的索引(普通 vector 上限 2000 维)。 - 二值量化。 bit 向量 + 二值量化可索引最高 64000 维,适合作为极快粗筛层。
- sparsevec 稀疏向量。 适合 BM25/TF-IDF 风格场景,为混合检索提供数据类型基础。
- 过滤与索引增强。 查询计划器在带 WHERE/JOIN 条件时更倾向正确选择向量索引;HNSW 构建和搜索性能持续改进;SQL 内 hybrid search(向量 + 全文)成为现实。
- 配套 PostgreSQL 17。 增量排序、并行查询、JSON_TABLE 等增强让"向量 + 复杂元数据"查询更快;维护内存管理改进让大索引构建更友好。
七、运维速查
sql
SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 版本
SELECT count(*) FROM knowledge_chunks WHERE embedding IS NOT NULL; -- 入库量
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'knowledge_chunks';
SELECT pg_size_pretty(pg_total_relation_size('knowledge_chunks')); -- 占用空间
八、小结
pgvector 的选型逻辑:数据已在 PostgreSQL、规模百万级以内、需要业务字段与向量联合查询------优先用它 。三个必记知识点:<=> 是距离不是相似度、ops 算子族要与距离对应、带过滤条件时关注迭代索引扫描。规模继续增长时,再考虑 halfvec 压缩、二值量化粗筛,或迁移到独立向量库。