pgvector 扩展
问题
PostgreSQL 的 pgvector 扩展是什么?如何用它实现向量搜索?
答案
一、pgvector 简介
pgvector 是 PostgreSQL 的开源向量搜索扩展,让 PostgreSQL 原生支持向量存储和相似度搜索。它的最大优势是不需要引入额外的向量数据库,直接复用现有的 PostgreSQL 基础设施。
-- 安装扩展
CREATE EXTENSION vector;
二、为什么选择 pgvector
| 优势 | 说明 |
|---|---|
| 零额外运维 | 复用现有 PostgreSQL,不引入新组件 |
| SQL 原生 | 用标准 SQL 进行向量搜索 |
| 混合查询 | 向量搜索 + 标量过滤在同一条 SQL 中完成 |
| 事务支持 | ACID 事务保证数据一致性 |
| JOIN 能力 | 向量搜索结果可以 JOIN 其他表 |
适合中小规模(百万级以内向量)、已有 PostgreSQL 基础设施、需要混合查询的场景。
三、基本使用
创建表
-- 创建带向量字段的表
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT,
category VARCHAR(64),
-- vector(1536) 表示 1536 维向量(OpenAI text-embedding-3-small 的维度)
embedding vector(1536),
created_at TIMESTAMP DEFAULT NOW()
);
插入数据
-- 插入向量数据
INSERT INTO documents (title, content, category, embedding)
VALUES (
'PostgreSQL 入门',
'本文介绍 PostgreSQL 的基本使用...',
'database',
'[0.23, -0.15, 0.87, ..., 0.42]' -- 1536 维向量
);
相似度搜索
-- 余弦距离搜索(<=> 操作符)
-- 返回与查询向量最相似的 10 条记录
SELECT id, title, 1 - (embedding <=> '[0.1, 0.2, ...]') AS similarity
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 10;
-- L2 距离搜索(<-> 操作符)
SELECT id, title
FROM documents
ORDER BY embedding <-> '[0.1, 0.2, ...]'
LIMIT 10;
-- 内积搜索(<#> 操作符,取负值)
SELECT id, title
FROM documents
ORDER BY embedding <#> '[0.1, 0.2, ...]'
LIMIT 10;
距离操作符
| 操作符 | 含义 | 排序方式 |
|---|---|---|
<-> | L2 距离 | 值越小越相似 |
<=> | 余弦距离 | 值越小越相似(1 - 余弦相似度) |
<#> | 负内积 | 值越小越相似 |
混合查询(向量 + 标量过滤)
-- 在特定分类下搜索最相似的文档
SELECT id, title, 1 - (embedding <=> $1) AS similarity
FROM documents
WHERE category = 'database'
AND created_at > '2024-01-01'
ORDER BY embedding <=> $1
LIMIT 10;
这是 pgvector 相比专用向量数据库的核心优势——向量搜索和标量过滤在一条 SQL 中完成,无需应用层二次过滤。
四、索引
pgvector 支持两种向量索引:
IVFFlat 索引
-- 创建 IVFFlat 索引(需要先有数据)
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100); -- 聚类数量,推荐 sqrt(行数)
查询时设置探查数:
-- 搜索时探查的聚类数(越大越精确,越慢)
SET ivfflat.probes = 10;
HNSW 索引(推荐)
-- 创建 HNSW 索引(0.5.0+ 版本支持。可在空表上创建)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
查询时设置搜索宽度:
SET hnsw.ef_search = 40;
索引对比
| 对比 | IVFFlat | HNSW |
|---|---|---|
| 建索引要求 | 需要先有数据 | 可在空表上创建 |
| 查询速度 | 较快 | 更快 |
| 召回率 | 90~99% | 95~99% |
| 建索引时间 | 快 | 慢 |
| 内存占用 | 低 | 高 |
| 推荐场景 | 数据量大、内存有限 | 通用场景(推荐) |
五、性能优化
1. 维度优化
如果业务不需要高维向量,可以降维减少存储和计算开销:
-- 使用 halfvec 半精度(float16,节省一半存储)
CREATE TABLE items (
id BIGSERIAL PRIMARY KEY,
embedding halfvec(768) -- 半精度向量
);
2. 分区表
-- 按分类分区,缩小搜索范围
CREATE TABLE documents (
id BIGSERIAL,
category VARCHAR(64),
embedding vector(1536)
) PARTITION BY LIST (category);
CREATE TABLE documents_tech PARTITION OF documents FOR VALUES IN ('tech');
CREATE TABLE documents_finance PARTITION OF documents FOR VALUES IN ('finance');
每个分区独立建向量索引,查询时自动裁剪分区。
3. 并行查询
-- 调大并行度
SET max_parallel_workers_per_gather = 4;
六、与 ORM 集成
Node.js(Prisma + pgvector)
// Prisma schema 中使用 Unsupported 类型
// 通过 raw SQL 进行向量操作
const results = await prisma.$queryRaw`
SELECT id, title, 1 - (embedding <=> ${embedding}::vector) as similarity
FROM documents
WHERE category = ${category}
ORDER BY embedding <=> ${embedding}::vector
LIMIT 10
`;
Python(SQLAlchemy + pgvector)
from pgvector.sqlalchemy import Vector
from sqlalchemy import Column, Integer, String
class Document(Base):
__tablename__ = "documents"
id = Column(Integer, primary_key=True)
title = Column(String)
embedding = Column(Vector(1536)) # pgvector 向量类型
# 搜索
from sqlalchemy import select
stmt = select(Document).order_by(
Document.embedding.cosine_distance(query_vector)
).limit(10)
results = session.execute(stmt).scalars().all()
七、pgvector vs 专用向量数据库
| 对比 | pgvector | Milvus/Qdrant |
|---|---|---|
| 运维成本 | 低(复用 PG) | 高(独立集群) |
| 数据规模 | 百万级 | 十亿级 |
| 混合查询 | 原生 SQL | 有限标量过滤 |
| 事务支持 | 完整 ACID | 不支持 |
| 向量搜索性能 | 中等 | 极高 |
| 索引类型 | HNSW、IVFFlat | HNSW、IVF、PQ 等 |
| 适合场景 | 中小规模、已有 PG | 大规模向量搜索 |
选型建议
- 数据量 < 100 万,已有 PostgreSQL → pgvector
- 数据量 > 100 万,向量搜索是核心功能 → Milvus/Qdrant
- 需要全文搜索 + 向量搜索 → Elasticsearch 或 PostgreSQL(全文 + pgvector)
常见面试问题
Q1: pgvector 的优势和劣势是什么?
答案:
优势:
- 零额外运维,复用 PostgreSQL
- SQL 原生,支持向量搜索 + 标量过滤 + JOIN
- 完整的 ACID 事务支持
- 社区活跃,更新频繁
劣势:
- 大规模向量搜索性能不如专用向量数据库
- 索引类型有限(HNSW、IVFFlat)
- 分布式能力依赖 PostgreSQL 本身(Citus 等)
- 不适合十亿级别的超大规模场景
Q2: pgvector 的 HNSW 索引参数如何调优?
答案:
两个关键参数:
| 参数 | 作用 | 推荐值 | 增大的影响 |
|---|---|---|---|
m | 每个节点的连接数 | 16~32 | 召回率↑ 内存↑ 建索引慢↑ |
ef_construction | 建图搜索宽度 | 64~200 | 索引质量↑ 建索引慢↑ |
查询时的 hnsw.ef_search(默认 40):增大提高召回率但增加延迟。
调参流程:从默认值开始,用基准测试数据集评估 Recall 和 QPS,逐步调整。
Q3: pgvector 搜索结果如何获取相似度分数?
答案:
-- 余弦距离转相似度:1 - 距离
SELECT id, title,
1 - (embedding <=> $1) AS cosine_similarity -- 0~1,越大越相似
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;
-- L2 距离直接返回(越小越相似)
SELECT id, title,
embedding <-> $1 AS l2_distance
FROM documents
ORDER BY embedding <-> $1
LIMIT 10;