13.pgvector入门用PostgreSQL直接实现向量检

pgvector 入门:用 PostgreSQL 直接实现向量检索

码海寻道 · 大模型、智能体与 RAG 工程组件系列第 13 篇

如果项目已经使用 PostgreSQL,是否一定要再部署 Milvus?

不一定。

pgvector 是 PostgreSQL 的向量相似度搜索扩展,可以把向量直接存进 PostgreSQL 表,并使用 SQL 完成近邻查询。对于中小型知识库、原型项目和希望减少基础设施数量的团队,它是一条很有吸引力的路线。

本文从安装、建表、写入、查询和索引开始,搭建一个最小的文本向量检索示例,并说明它适合什么场景、有哪些边界。

一、pgvector 解决什么问题?

PostgreSQL 原生并不是向量数据库。安装 pgvector 扩展后,可以获得:

  • vector 等向量数据类型;
  • L2、内积、余弦等距离运算符;
  • 精确近邻搜索;
  • HNSW 和 IVFFlat 等近似索引;
  • 向量与普通 SQL 条件、JOIN 和事务结合查询。

pgvector 官方项目将其定位为 PostgreSQL 的开源向量相似度搜索扩展,并强调可以把向量与其他业务数据一起存储。

pgvector 是 PostgreSQL 的扩展,不是一个独立的向量服务。它的查询、索引构建、WAL、备份、连接数和资源消耗都发生在 PostgreSQL 体系内。这是它简化架构的原因,也是它需要和业务 SQL 争抢资源的原因。

二、安装和启用扩展

具体安装方式取决于操作系统、PostgreSQL 版本和发行版。可以使用系统包、Docker、源码或托管服务提供的扩展。

安装完成后,在目标数据库中启用:

sql 复制代码
CREATE EXTENSION IF NOT EXISTS vector;

扩展是按数据库启用的。即使服务器上已经安装了 pgvector,如果当前数据库没有执行 CREATE EXTENSION,仍然不能使用 vector 类型。

可以检查扩展版本:

sql 复制代码
SELECT extname, extversion
FROM pg_extension
WHERE extname = 'vector';

生产环境中要记录 PostgreSQL 和 pgvector 版本,升级前先阅读兼容性说明,并准备回滚和备份方案。

三、创建一个向量表

向量字段需要指定维度。假设 Embedding 模型输出 1536 维:

sql 复制代码
CREATE TABLE document_chunks (
    id BIGSERIAL PRIMARY KEY,
    document_id UUID NOT NULL,
    tenant_id UUID NOT NULL,
    content TEXT NOT NULL,
    metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
    embedding VECTOR(1536),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

这里的 VECTOR(1536) 必须与 Embedding 模型实际输出维度一致。模型换成 768 维时,不能直接把 768 维向量写进 vector(1536) 字段。

在 AI 应用中,建议同时保存:

  • 原始文本片段;
  • 文档 ID;
  • 租户 ID;
  • 页码、标题和版本等元数据;
  • Embedding 模型名称和版本。

不要只保存一个向量,否则检索到结果后无法追溯来源和执行权限过滤。

四、写入向量

向量可以使用字符串形式插入:

sql 复制代码
INSERT INTO document_chunks (
    document_id,
    tenant_id,
    content,
    embedding
)
VALUES (
    '00000000-0000-0000-0000-000000000001',
    '00000000-0000-0000-0000-000000000010',
    '员工年休假申请需要通过内部系统提交。',
    '[0.12, -0.03, 0.44, ...]'
);

示例中的省略号只是说明格式,实际写入时必须提供完整的 1536 个数值。

批量建库时,不建议逐条发送 INSERT。可以使用驱动的批量写入或 COPY,并在初始数据加载完成后再创建索引,以减少建库时间。

生产批量导入建议记录批次 ID、文档版本、Embedding 模型版本和成功/失败条数。初始大批量导入可以先写入数据、执行 ANALYZE、再创建索引;日常增量写入则需要观察索引维护、WAL、锁和查询尾延迟。

五、用 SQL 执行最近邻查询

pgvector 使用不同运算符表示不同距离:

运算符 含义
<-> L2 距离
<#> 负内积
<=> 余弦距离
<+> L1 距离

使用余弦距离查询:

sql 复制代码
SELECT
    id,
    document_id,
    content,
    1 - (embedding <=> '[0.12, -0.03, 0.44, ...]') AS cosine_similarity
FROM document_chunks
WHERE tenant_id = '00000000-0000-0000-0000-000000000010'
  AND embedding IS NOT NULL
ORDER BY embedding <=> '[0.12, -0.03, 0.44, ...]'
LIMIT 5;

注意两个细节:

  1. <=> 返回的是余弦距离,距离越小越相似;
  2. 如果要展示余弦相似度,可以使用 1 - distance

pgvector 的 <#> 返回负内积,这是为了配合 PostgreSQL 只能使用升序索引扫描的行为。不要直接把 <#> 的负值当成正向内积解释。

六、把普通 SQL 条件和向量检索结合起来

这是 pgvector 对业务应用很有吸引力的地方:可以在同一条 SQL 中完成租户、状态、版本和时间过滤。

sql 复制代码
SELECT
    id,
    content,
    metadata,
    1 - (embedding <=> $1::vector) AS similarity
FROM document_chunks
WHERE tenant_id = $2
  AND metadata ->> 'status' = 'published'
  AND metadata ->> 'language' = 'zh-CN'
ORDER BY embedding <=> $1::vector
LIMIT $3;

这类查询非常适合小型企业知识库。权限和业务过滤可以和向量检索放在同一个关系查询中,减少应用层拼接多个系统结果的复杂度。

但要通过 EXPLAIN (ANALYZE, BUFFERS) 检查实际执行计划。过滤条件和近似索引之间的组合可能影响召回数量和执行效率。

如果过滤条件很严格,近似索引可能先访问有限候选,导致最终有效结果不足。pgvector 新版本提供了迭代扫描等能力,但是否适用取决于 PostgreSQL 和 pgvector 版本,不能把某个版本的参数直接复制到所有环境。遇到空结果或召回不足时,应同时比较精确搜索基线、近似搜索计划和过滤后候选数量。

七、精确搜索与近似搜索

默认情况下,pgvector 可以执行精确近邻搜索:对候选向量计算距离,再返回最准确的结果。

优点:

  • 召回结果准确;
  • 便于建立评测基线;
  • 小数据量下实现简单。

缺点:

  • 数据量增大后查询成本上升;
  • 高并发下 CPU 压力增大。

当数据规模和查询压力上升,可以建立近似索引。近似搜索通常以少量召回率损失换取更低延迟。

八、HNSW 索引

使用余弦距离时,可以创建:

sql 复制代码
CREATE INDEX document_chunks_embedding_hnsw
ON document_chunks
USING hnsw (embedding vector_cosine_ops);

HNSW 通过多层图结构寻找近邻,通常具有较好的速度---召回率平衡。可以调整构建参数:

sql 复制代码
CREATE INDEX document_chunks_embedding_hnsw_tuned
ON document_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (
    m = 16,
    ef_construction = 64
);

参数越激进,可能提高召回率,但会增加建索引时间、内存和写入成本。具体参数需要压测,不建议直接复制到生产环境。

如果使用 L2 或内积,需要使用对应的 operator class:

sql 复制代码
-- L2
CREATE INDEX ON document_chunks
USING hnsw (embedding vector_l2_ops);

-- Inner Product
CREATE INDEX ON document_chunks
USING hnsw (embedding vector_ip_ops);

同一个查询指标和索引 operator class 必须匹配。

九、IVFFlat 索引

IVFFlat 先把向量划分到若干列表,再只搜索部分列表:

sql 复制代码
CREATE INDEX document_chunks_embedding_ivfflat
ON document_chunks
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

查询时可以调整探测列表数量:

sql 复制代码
BEGIN;
SET LOCAL ivfflat.probes = 10;

SELECT id, content
FROM document_chunks
ORDER BY embedding <=> $1::vector
LIMIT 5;

COMMIT;

probes 越大,通常召回率越高,但查询成本也会上升。

IVFFlat 的列表数和数据量有关。数据量很小时,过早创建过大的 IVFFlat 索引可能导致效果不理想。可以先用精确搜索建立基线,再决定是否需要近似索引。

十、批量导入时的建议顺序

text 复制代码
创建表
  ↓
批量导入文本和向量
  ↓
检查行数、维度和空值
  ↓
创建向量索引
  ↓
执行 ANALYZE
  ↓
用真实问题压测

示意:

sql 复制代码
SELECT COUNT(*) FROM document_chunks;
SELECT COUNT(*) FROM document_chunks WHERE embedding IS NULL;

ANALYZE document_chunks;

初始数据加载完成后再建索引,通常比边写入边构建更高效。生产环境创建大索引时,还要考虑是否使用并发创建以减少对写入的影响。

十一、pgvector 的 Python 查询示例

下面使用伪代码表示应用层流程,真实项目需要根据使用的 PostgreSQL 驱动选择参数绑定方式:

python 复制代码
query_vector = embedding_model.embed_query(user_query)

sql = """
SELECT id, document_id, content,
       1 - (embedding <=> %s::vector) AS similarity
FROM document_chunks
WHERE tenant_id = %s
ORDER BY embedding <=> %s::vector
LIMIT %s
"""

rows = connection.execute(
    sql,
    (query_vector, tenant_id, query_vector, top_k),
).fetchall()

连接层同样重要:使用连接池限制在线查询和批量导入的并发,设置 statement timeout,避免一个慢向量查询长期占用连接。大索引创建和重建应安排在可控时段,并评估是否需要 CREATE INDEX CONCURRENTLY

应用层要确保:

  • 查询向量和文档向量使用同一个 Embedding 体系;
  • 参数使用驱动绑定,避免 SQL 注入;
  • tenant_id 和权限过滤不会被遗漏;
  • 空向量、维度错误和数据库超时有明确处理;
  • 结果保留文档来源和版本信息。

十二、pgvector 的优点

1. 架构简单

业务数据和向量可以在同一个 PostgreSQL 集群中管理,减少服务数量。

2. 事务和 JOIN 方便

文档状态、权限和向量记录可以使用关系查询组织起来。

3. 运维体系成熟

可以复用 PostgreSQL 的备份、复制、监控和权限体系。

4. 适合快速验证

不必一开始部署独立向量数据库,就可以完成语义检索和 RAG 原型。

十三、pgvector 的边界

数据规模和并发

向量数量、查询并发和延迟要求达到一定规模后,需要评估 PostgreSQL 是否会被向量检索拖累。

资源竞争

业务 SQL、事务写入、全文检索和向量搜索共享数据库资源,可能互相影响。

向量索引调优

HNSW、IVFFlat、过滤条件和 PostgreSQL 查询计划需要一起调优。

扩展方式

如果系统需要独立扩展向量查询节点和业务数据库,拆分成 PostgreSQL + Milvus 可能更合适。

备份与恢复

pgvector 的向量和索引属于 PostgreSQL 数据库的一部分,不能只备份业务表结构而忽略扩展版本、向量数据和索引重建时间。恢复演练至少要验证:

text 复制代码
恢复 PostgreSQL 实例
  ↓
确认 vector 扩展和版本
  ↓
检查向量行数、维度和 NULL 比例
  ↓
确认索引可用或执行重建
  ↓
用固定问题集验证 Recall@K 和延迟

下一篇会专门比较两种架构:什么时候继续使用 pgvector,什么时候单独部署 Milvus。

十四、上线前检查清单

  • 已在目标数据库启用 vector 扩展;
  • 向量维度与 Embedding 模型一致;
  • 查询指标和 operator class 匹配;
  • 知道 <=> 是余弦距离而不是相似度;
  • 精确搜索和近似搜索有对比基线;
  • HNSW/IVFFlat 参数经过真实数据压测;
  • 过滤条件包含租户和权限边界;
  • 批量导入和建索引顺序合理;
  • 使用 EXPLAIN (ANALYZE, BUFFERS) 分析慢查询;
  • 已规划备份、升级和索引重建。
  • 使用连接池、查询超时和并发限制;
  • 已验证过滤条件下的精确搜索与近似搜索差异;
  • 已进行包含 pgvector 扩展和索引恢复的演练;

结语:pgvector 是 PostgreSQL 的自然延伸,但不是所有规模的终点

pgvector 让 PostgreSQL 可以直接保存向量并执行相似度搜索。对于中小型 RAG、原型和关系查询占主导的应用,它能以较低的系统复杂度完成完整闭环。

但当向量规模、并发、隔离和扩展需求不断增长时,仍然要用真实数据评估它的边界。

下一篇:

《什么时候用 pgvector,什么时候单独部署 Milvus?》


参考资料

  1. pgvector 官方项目
  2. PostgreSQL Documentation:Indexes
  3. PostgreSQL Documentation:Concurrency Control
  4. PostgreSQL Documentation:EXPLAIN

本文为"码海寻道"原创技术文章。pgvector 的扩展版本、支持维度和索引参数会变化,生产环境请以实际版本文档和压测结果为准。

相关推荐
Csvn43 分钟前
第 6 章 Agent 主循环
人工智能·aigc·agent
JavaPub-rodert1 小时前
Ontop 详解:不搬数据库,也能把 MySQL / PostgreSQL 变成知识图谱
数据库·mysql·postgresql
Csvn1 小时前
第 5 章 工具调用
人工智能·aigc·agent
clorinda1 小时前
OpenCV实战学习记录:图像拼接与答题卡识别
人工智能·opencv·学习
腾视科技-AIoT1 小时前
腾视科技AIBOX双版本重磅发布!本地安全与全球适配,解锁视频智能新可能
大数据·人工智能·科技·ai·物理ai·ainas·腾视科技
zcmodeltech1 小时前
煤化工沙盘模型控制系统设计与实现:多工段协同联动方案
网络·人工智能·stm32·嵌入式硬件·制造·多分类
CTA终结者1 小时前
2026年程序员量化开发学习:用示例、拆解和练习入门
人工智能·python
小酒星小杜1 小时前
如何简单地创建你的第一部漫画?从一个“可见变化”开始
人工智能·python·产品
掘金酱1 小时前
TRAE Work 实战帮征文 | 获奖名单公示
前端·人工智能·后端