pgvector 实战:把向量检索"塞"进关系数据库,一条 SQL 搞定联合查询

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):

  1. halfvec 半精度向量。 halfvec(1024) 内存占用约为 vector 的一半,召回损失很小;且支持最高 4000 维向量的索引(普通 vector 上限 2000 维)。
  2. 二值量化。 bit 向量 + 二值量化可索引最高 64000 维,适合作为极快粗筛层。
  3. sparsevec 稀疏向量。 适合 BM25/TF-IDF 风格场景,为混合检索提供数据类型基础。
  4. 过滤与索引增强。 查询计划器在带 WHERE/JOIN 条件时更倾向正确选择向量索引;HNSW 构建和搜索性能持续改进;SQL 内 hybrid search(向量 + 全文)成为现实。
  5. 配套 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 压缩、二值量化粗筛,或迁移到独立向量库。

相关推荐
IT_陈寒1 小时前
JavaScript的this指向问题又让我加了个班
前端·人工智能·后端
美好世界1 小时前
Codex 源码导读:第一部分——工程分层
后端
行百里er1 小时前
Redis 核心数据结构(四)——Set 与 Sorted Set,去重与排名神器
redis·后端
lizhongxuan1 小时前
Firecracker 与 KVM
后端
PC2005_cloud1 小时前
Nginx 学习笔记:Server 块配置详解,域名路由与多站点部署实战
前端·后端
YIAN1 小时前
LangChain.js 对话记忆体系(一):内存存储与文件持久化,让 AI 拥有对话记忆
前端·后端·langchain
福兮说1 小时前
errgroup 的六个坑:Wait 之后 ctx 已取消、SetLimit 嵌套死锁,以及另外四个
后端·go
IT_陈寒1 小时前
React状态管理这个坑,我是怎么翻车的
前端·人工智能·后端
mldong1 小时前
聚合边界:为什么 ProcessTask 没有自己的 Repository
后端·架构