pgsql-16 pgvector向量数据库
1. 概述
在大模型(LLM)时代,向量检索成为 AI 应用的核心能力:把文本、图片、音频通过 Embedding 模型编码成高维向量(如 1536 维),然后通过"向量相似度"找到语义最接近的内容。这类需求催生了一批专用向量数据库(Milvus、Pinecone、Qdrant、Weaviate)。
而 pgvector 是 PostgreSQL 的一个开源扩展,它让 PostgreSQL 直接具备"存储向量 + 相似度检索 + 建立向量索引"的能力。这意味着你无需引入一套独立的向量数据库,就能在熟悉的关系型数据库里同时管理业务数据和向量数据。
1.1 典型应用场景
- RAG(检索增强生成):把知识库文档切片、向量化存入 PG,用户提问时检索最相关的片段喂给 LLM。
- 语义搜索:不再是关键词匹配,而是"意思相近"就能搜到。
- 推荐系统:用户向量 × 物品向量,找最相似的物品。
- 图片/音频检索:以图搜图、听歌识曲。
- 去重 / 异常检测:相似度过高判定为重复,过低判定为异常。
1.2 为什么用 pgvector 而不是专用向量库?
| 维度 | pgvector (PostgreSQL) | 专用向量库 (Milvus/Pinecone) |
|---|---|---|
| 数据统一 | 向量与业务数据同库,可 JOIN、可事务 | 向量单独存储,需两套系统同步 |
| 事务/一致性 | 完整 ACID | 大多最终一致 |
| 过滤查询 | 天然支持 SQL 条件过滤 + 向量检索 | 需专门的 metadata filtering |
| 运维成本 | 复用现有 PG 运维 | 额外一套系统 |
| 超大规模(亿级+) | 索引构建/内存有压力 | 专门优化,更强 |
| 极致检索性能 | 好,但略逊 | 更快(GPU、分布式) |
结论:中小规模(百万~千万级向量)、需要与业务数据强关联、希望少维护一套系统 → 首选 pgvector;亿级以上、追求极致 QPS/延迟 → 考虑专用向量库。
对比 MySQL:MySQL 8.x 长期不支持向量类型,直到 MySQL 9.0(2024)才引入实验性的
VECTOR类型和DISTANCE()函数,但缺乏成熟的向量索引(HNSW/IVFFlat)生态,检索只能全表扫描。PostgreSQL + pgvector 在这方面领先 MySQL 数年,这也是 AI 应用大量选型 PG 的原因之一。
2. 安装 pgvector
pgvector 不是 PostgreSQL 内置扩展,需要单独编译安装(或用云厂商已集成的版本,如 AWS RDS、阿里云 RDS、Supabase 均已内置)。
# 源码编译安装(Linux)
git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # 需要 pg_config 在 PATH 中
# macOS (Homebrew 安装的 PG)
brew install pgvector
-- 在目标数据库中启用扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 查看版本
SELECT extversion FROM pg_extension WHERE extname = 'vector';
3. 向量类型与基础操作
3.1 三种向量类型
pgvector 提供多种类型,权衡精度与存储:
| 类型 | 说明 | 每维字节 | 适用 |
|---|---|---|---|
vector(n) |
单精度浮点,最常用 | 4 字节 | 通用(OpenAI/BGE 等 Embedding) |
halfvec(n) |
半精度浮点(0.7.0+) | 2 字节 | 省一半空间,精度损失小 |
bit(n) |
二值向量 | 1 bit | 汉明距离场景 |
sparsevec(n) |
稀疏向量(0.7.0+) | 仅存非零 | 高维稀疏(如 SPLADE) |
-- 创建带向量列的表(1536 维,对应 OpenAI text-embedding-3-small)
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT, -- 原始文本
category VARCHAR(50), -- 业务字段,可用于过滤
embedding vector(1536) -- 向量列
);
-- 插入向量(用字符串字面量,方括号包裹)
INSERT INTO documents (content, category, embedding) VALUES
('PostgreSQL 是强大的开源数据库', 'db', '[0.1, 0.2, 0.3, ...]'), -- 实际是1536个数
('向量检索是 RAG 的核心', 'ai', '[0.2, 0.1, 0.4, ...]');
3.2 距离运算符(核心)
pgvector 用特殊运算符计算两个向量的"距离/相似度",运算符决定了检索语义,也决定了索引怎么建:
| 运算符 | 距离类型 | 含义 | 常用场景 |
|---|---|---|---|
<-> |
L2(欧氏)距离 | 空间直线距离,越小越近 | 图像特征 |
<#> |
负内积 | 返回 -(a·b),越小越相似 |
需要归一化向量 |
<=> |
余弦距离 | 1 - cos(θ),越小越相似 |
文本 Embedding 最常用 |
<+> |
L1(曼哈顿)距离 | 各维差绝对值之和 | 0.7.0+ |
-- 计算两个向量的余弦距离
SELECT '[1,2,3]'::vector <=> '[4,5,6]'::vector AS cosine_distance;
-- 找出与给定向量最相似的 5 条(KNN 检索)
SELECT id, content, embedding <=> '[0.1,0.2,...]'::vector AS distance
FROM documents
ORDER BY embedding <=> '[0.1,0.2,...]'::vector -- 按距离升序 = 最相似在前
LIMIT 5;
关键点:
ORDER BY ... LIMIT k这种"取最近的 k 个"就是 KNN(K 近邻)检索,也是向量索引唯一能加速的查询形态。索引类型必须与运算符匹配(余弦距离 →vector_cosine_ops)。
4. 向量索引:IVFFlat vs HNSW
向量列上如果没有索引,KNN 检索会全表扫描 + 逐个算距离(暴力检索),百万级数据就会很慢。pgvector 支持两种 ANN(近似最近邻)索引,用少量精度换取巨大的速度提升。
4.1 IVFFlat(倒排文件 + 平面)
原理:先用 K-means 把所有向量聚成 lists 个簇(每簇一个中心点)。检索时只在最近的 probes 个簇里找,而不是全表。
-- 建 IVFFlat 索引(必须指定运算符类,且 lists 需调优)
-- 经验值:lists = rows / 1000(百万级),或 sqrt(rows)
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1000);
-- 检索时设置探测簇数(越大越准越慢),仅当前会话生效
SET ivfflat.probes = 10;
特点:
- 构建快、内存占用小。
- ⚠️ 必须在有数据后再建索引(需要数据来聚类),空表建索引效果差。
- 数据大量变化后需要重建以保持召回率。
4.2 HNSW(分层可导航小世界图)
原理:构建一个多层图结构,上层稀疏(快速跳转),下层稠密(精细搜索),像"高速公路 + 乡间小路"逐层逼近目标。是目前综合最优的向量索引。
-- 建 HNSW 索引
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 检索时设置搜索候选数(越大越准越慢)
SET hnsw.ef_search = 40;
参数含义:
m:每个节点的最大连接数(默认 16),越大图越密、召回越高、内存越大。ef_construction:构建时的候选队列大小(默认 64),越大索引质量越高、构建越慢。ef_search:查询时候选队列,控制精度/速度权衡。
4.3 两者对比(面试高频)
| 维度 | IVFFlat | HNSW |
|---|---|---|
| 查询速度 | 快 | 更快 |
| 召回率(准确度) | 中 | 高 |
| 索引构建速度 | 快 | 慢 |
| 内存占用 | 低 | 高 |
| 是否需要预先有数据 | 是(要聚类) | 否(可空表建) |
| 增量插入友好度 | 差(易退化) | 好 |
| 推荐场景 | 内存紧张、数据静态 | 绝大多数生产场景首选 |
4.4 关键坑:过滤 + 向量检索
业务中常见"先按条件过滤再向量检索",比如"只在 category=‘ai’ 的文档里找最相似":
SELECT id, content
FROM documents
WHERE category = 'ai' -- 条件过滤
ORDER BY embedding <=> '[...]'::vector
LIMIT 5;
问题:向量索引和普通索引无法同时高效使用。若过滤后剩余行很少,向量索引可能被放弃(退化为暴力检索);若过滤条件命中很多行才用向量索引,可能因过滤丢掉近邻导致召回不足。
解决方案:
- 分区表:按 category 分区,让向量索引在分区内生效。
- 调大
hnsw.ef_search/ivfflat.probes:补偿过滤带来的召回损失。 - pgvector 0.8.0+ 引入 迭代扫描(iterative scan),能在过滤场景下持续从索引取候选直到凑够
LIMIT:
SET hnsw.iterative_scan = strict_order; -- 或 relaxed_order
5. RAG 实战:从文本到检索
下面演示一个完整的 RAG 数据层:文档切片 → 向量化 → 存储 → 检索。向量化通常由应用层(Python/Go 调 Embedding 模型)完成,这里聚焦 PG 侧。
5.1 表设计
CREATE TABLE knowledge_chunks (
id BIGSERIAL PRIMARY KEY,
doc_id BIGINT, -- 所属文档
chunk_text TEXT NOT NULL, -- 切片文本
token_count INT,
metadata JSONB, -- 来源、页码等元信息
embedding vector(1536),
created_at TIMESTAMPTZ DEFAULT now()
);
-- HNSW 索引(余弦距离)
CREATE INDEX idx_chunks_embedding ON knowledge_chunks
USING hnsw (embedding vector_cosine_ops);
-- 元信息用 GIN 索引,支持过滤
CREATE INDEX idx_chunks_metadata ON knowledge_chunks USING GIN (metadata);
5.2 检索查询(相似度 + 阈值 + 过滤)
-- 返回相似度(1 - 余弦距离),过滤低相关结果,并限定数据来源
SELECT
id,
chunk_text,
1 - (embedding <=> :query_vec) AS similarity -- 转为"相似度"更直观
FROM knowledge_chunks
WHERE metadata->>'source' = 'handbook' -- 只搜手册
AND 1 - (embedding <=> :query_vec) > 0.75 -- 相似度阈值
ORDER BY embedding <=> :query_vec
LIMIT 5;
5.3 混合检索(Hybrid Search,进阶)
纯向量检索对"专有名词/精确关键词"不敏感,工业界常把**全文搜索(关键词)与向量检索(语义)**结果融合(RRF,倒数排名融合)。这正是 pgvector 相对专用向量库的独特优势——同库即可实现:
-- 关键词检索结果 与 向量检索结果 用 RRF 融合排序
WITH semantic AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> :query_vec) AS rank
FROM knowledge_chunks ORDER BY embedding <=> :query_vec LIMIT 20
),
keyword AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(to_tsvector('simple', chunk_text),
plainto_tsquery(:q)) DESC) AS rank
FROM knowledge_chunks
WHERE to_tsvector('simple', chunk_text) @@ plainto_tsquery(:q) LIMIT 20
)
SELECT COALESCE(s.id, k.id) AS id,
COALESCE(1.0/(60+s.rank), 0) + COALESCE(1.0/(60+k.rank), 0) AS rrf_score
FROM semantic s
FULL OUTER JOIN keyword k USING (id)
ORDER BY rrf_score DESC
LIMIT 5;
全文搜索部分详见 全文搜索。
6. 性能与调优
- 优先选 HNSW:生产环境绝大多数场景默认用 HNSW。
- 归一化向量 + 内积:若向量已归一化,用
<#>(内积)比<=>(余弦)更快,结果等价。 - 控制维度:维度越高越慢越占空间。可用
text-embedding-3-small(1536)而非large(3072),或用halfvec半精度减半存储。 maintenance_work_mem:建 HNSW 索引很吃内存,临时调大(如SET maintenance_work_mem = '2GB')能显著加速构建。- 并行构建:
SET max_parallel_maintenance_workers = 4;加速索引构建。 - 监控召回率:ANN 是近似检索,用一批已知答案对比暴力检索结果,评估召回率,据此调
ef_search/probes。
-- 用 EXPLAIN ANALYZE 确认是否走了向量索引
EXPLAIN ANALYZE
SELECT id FROM knowledge_chunks
ORDER BY embedding <=> '[...]'::vector LIMIT 5;
-- 看到 "Index Scan using idx_chunks_embedding" 即命中索引
-- 若是 "Seq Scan" 则是暴力检索,需检查运算符类是否匹配
7. 面试高频问答
Q1:为什么可以用关系型数据库做向量检索?
向量本质是浮点数组,pgvector 提供了 vector 类型、距离运算符和 ANN 索引(IVFFlat/HNSW),把"最近邻搜索"变成可被索引加速的 ORDER BY 距离 LIMIT k 查询。
Q2:IVFFlat 和 HNSW 怎么选? HNSW 召回率高、查询快、支持增量插入、可空表建索引,是默认首选;IVFFlat 构建快、省内存,适合内存紧张或数据静态的场景。(见 4.3 对比表)
Q3:什么是 ANN?为什么不用精确检索? ANN 是近似最近邻。精确 KNN 需要和全部向量算距离(O(n)),亿级数据不可行。ANN 用图/聚类结构把复杂度降到近似对数级,牺牲极小召回率换取数量级的速度提升。
Q4:余弦距离、内积、L2 距离有什么区别? L2 是几何直线距离;内积同时受夹角和模长影响;余弦只看夹角(方向),对文本 Embedding 最常用。若向量已归一化,三者可相互转化。
Q5:pgvector 和专用向量库(Milvus/Pinecone)的取舍? pgvector 数据统一、支持事务与 SQL 过滤、运维简单,适合中小规模且需与业务数据关联;专用库在亿级规模、极致 QPS、分布式与 GPU 加速上更强。(见 1.2)
Q6:向量检索 + 条件过滤为什么会有坑? 向量索引与普通索引难以同时高效使用,过滤可能导致召回不足或索引失效。解决:分区、调大候选参数、或用 0.8.0+ 的迭代扫描。
8. 下一步
学习 扩展插件总览,系统了解 PostgreSQL 强大的插件生态。
xingliuhua