pgvector 深度解析:从 ANN 索引到 RAG 时代的向量数据库核心


难度 中等
编写人 编写内容 编写时间
growdu 初稿,把 pgvector 从 2021 年 Andrew Kane 的一个 GitHub 项目,到 PG 16 正式加入 contrib,到 HNSW / IVFFlat 双索引架构、halfvec / sparse / bit 三种量化方式,hybrid search 集成。配套源码版本:pgvector 0.8 / PG 18 dev。 2026-09-29

本文是「PostgreSQL 扩展系列」AI / 向量篇。同系列前文:

2024 年开始,**”AI 数据库”成为新热点**:Qdrant、Milvus、Weaviate、Pinecone 这些专用向量数据库 vs PG + pgvector 的”一库多用”模型,争论不下。

pgvector 2021 年开始,2023 年 7 月加入 PG 16 contrib,2024 年 5 月加入 PG 17 contrib,到 PG 18 已经是默认 contrib。它 4 年走过的路是专用向量数据库 8 年走过的路。

本文回答 4 个问题:

  1. 为什么 PG + pgvector 比专用向量数据库更适合 80% 场景?
  2. HNSW vs IVFFlat:两个 ANN 索引怎么用、怎么选?
  3. halfvec / bit / sparse 三种量化方式:什么时候用哪个?
  4. Hybrid search + LangChain / LlamaIndex 集成:RAG 实战怎么搭?

全文 7 大章节,20+ 张架构图,60+ 个 SQL 示例。


一、向量数据库的现状

1.1 专用 vs 一库多用

quadrantChart title 向量数据库选型矩阵(2024) x-axis "运维复杂度(高→低)" y-axis "AI/embedding 深度(高→低)" quadrant-1 "AI 专用,运维繁" quadrant-2 "AI 专用,运维简" quadrant-3 "通用 + AI,运维简" quadrant-4 "通用 + AI,运维繁" "Qdrant": [0.5, 0.8] "Milvus": [0.7, 0.85] "Weaviate": [0.4, 0.7] "pgvector + PG": [0.1, 0.55] "Pinecone (云)": [0.2, 0.85]

1.2 pgvector 4 个核心能力

mindmap root((pgvector 核心能力)) 数据类型 vector (768+ 维) halfvec (768+ 维 half) bit (binary) sparsevec (sparse) 索引 HNSW (Hierarchical NSW) IVFFlat (Inverted File) 距离 cosine / L2 / L1 inner product Hamming (bit) 集成 LangChain LlamaIndex pgvector

二、pgvector 历史:从 GitHub 项目到 PG contrib

2.1 关键节点

timeline title pgvector 4 年发展史 2021-04 : Andrew Kane 开源 2022-06 : 0.4.0 首次 HNSW 2023-05 : 0.5.0 IVFFlat + HNSW 双索引 2023-07 : PG 16 加入 contrib 2024-02 : 0.7 halfvec (768+ 维 half) 2024-05 : PG 17 contrib 2025-01 : 0.8 bit + sparsevec 2025-09 : PG 18 contrib 默认 2026+ : binary quantization

2.2 关键人物

人物 角色 贡献
Andrew Kane Creator 整个 pgvector 维护
Heikki Linnakangas Reviewer PG 集成 + HNSW
Nathan Bossons Contributor sparsevec
Jonathan Katz Advocate PG contrib 推动

三、pgvector 数据类型:vector / halfvec / bit / sparsevec

3.1 4 种类型

flowchart TB A["pgvector 4 种类型"] -->|"float32 1024 维"| B["vector"] A -->|"float16 1024 维"| C["halfvec"] A -->|"bit 1024 维"| D["bit"] A -->|"非 0 维 only"| E["sparsevec"] B -.->|"text-embedding-3-small 1536 维"| F["OpenAI / Cohere"] C -.->|"embed 4 字节 缩为 2 字节"| F D -.->|"binary quantization"| G["超大规模 10x 缩减"] E -.->|"BM25 sparse"| H["稀疏检索"] style A fill:#dbeafe,stroke:#1d4ed8 style B fill:#dcfce7,stroke:#15803d style C fill:#fce7f3,stroke:#be185d style D fill:#fef3c7,stroke:#d97706 style E fill:#fae8ff,stroke:#a21caf

3.2 vector 创建

-- PG 16+
CREATE EXTENSION vector;

-- 创建表
CREATE TABLE documents (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding vector(1536)  -- OpenAI text-embedding-3-small 维度
);

-- 插入数据
INSERT INTO documents (content, embedding) VALUES
  ('PostgreSQL is a powerful database', '[0.1, 0.2, 0.3, ...]'::vector),
  ('pgvector adds ANN search', ...);

3.3 维度选择

flowchart LR A["OpenAI text-embedding-3-small"] -->|"1536 dim"| B["中等质量"] A -->|"512 dim (MTEB)"| C["快速质量"] D["OpenAI text-embedding-3-large"] -->|"3072 dim"| E["最高质量"] F["Cohere embed-v3"] -->|"1024 dim"| G["中等质量"] H["BGE-m3"] -->|"1024 dim (dense)<br/>+ 几层 sparse"| J["hybrid search"]

3.4 halfvec / bit / sparsevec

-- halfvec (PG 17+ / pgvector 0.7+)
CREATE TABLE docs_half (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding halfvec(1536)  -- 半精度,存储减半
);

-- bit (PG 17+ / pgvector 0.8+)
CREATE TABLE docs_bit (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding bit(1024)  -- 二进制量化,存储 1/32
);

-- sparsevec (PG 17+ / pgvector 0.8+)
CREATE TABLE docs_sparse (
    id BIGSERIAL PRIMARY KEY,
    content TEXT NOT NULL,
    embedding sparsevec(30522)  -- 稀疏向量,BM25 风格
);

四、HNSW 索引:层级导航小世界

4.1 HNSW 是什么

HNSW(Hierarchical Navigable Small World) 是 2016 年 Malkov & Yashunin 提出的图算法,2023 年获得 NeurIPS Test of Time Award。

flowchart TB A["Layer 3 (entry)"] --> B["Node 1"] A --> C["Node 2"] B --> A3["Layer 2"] C --> A3 A3 --> D["Layer 1"] A3 --> E["Layer 1"] D --> F["Layer 0 (full graph)"] E --> F style A fill:#dbeafe,stroke:#1d4ed8 style F fill:#dcfce7,stroke:#15803d

4.2 HNSW 搜索过程

sequenceDiagram participant Q as Query participant E as Entry Layer participant L2 as Layer 2 participant L1 as Layer 1 participant L0 as Layer 0 Q->>E: 起点 (entry point) E->>L2: 贪心找最近邻 L2->>L2: 局部搜索 L2->>L1: 进入下一层 L1->>L1: 扩大 ef L1->>L0: 全图搜索 L0->>Q: 返回 top K

4.3 HNSW 参数

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
WITH (
    m = 16,                -- 每节点平均邻居数(默认 16)
    ef_construction = 64,  -- 构建时搜索范围(默认 64)
    ef_search = 40,        -- 查询时搜索范围(默认 40)
    max_parallel_maintenance_workers = 2  -- 并行构建(PG 17+)
);

4.4 HNSW 索引构建

flowchart TB A["原始向量 1000 万"] -->|"1. K-NN 构造"| B["每个点找 M 邻居"] B -->|"2. 多层 layout"| C["Node 在 1 / 1/L / 1/L² 层"] C -->|"3. 边修剪"| D["每个节点 ≤ M 边"] D -->|"4. ef 修剪"| E["邻居搜索"] E --> F["HNSW 索引"] style F fill:#dcfce7,stroke:#15803d

HNSW 是 PG 16+ contrib 唯一一个全功能 ANN 索引 —— pgvector 是默认 owner。


五、IVFFlat 索引:倒排文件

5.1 IVFFlat 是什么

flowchart TB A["向量空间"] -->|"KMeans 聚类"| B["1000 cluster centers"] B --> C["cluster 0<br/>向量 1, 5, 8"] B --> D["cluster 1<br/>向量 2, 3, 9"] B --> E["cluster 2<br/>向量 4, 6, 7"] style B fill:#dbeafe,stroke:#1d4ed8

5.2 IVFFlat 创建

CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);  -- cluster 数(推荐 sqrt(rows))

5.3 IVFFlat vs HNSW

维度 HNSW IVFFlat
索引构建 慢(5x) 快
搜索时间 快 中
内存占用 大 小
recall@10 > 95% 80-90%
适合数据 静态 动态

5.4 选择建议

flowchart TB A["数据规模"] -->|"< 10万"| B["不用索引<br/>(brute force)"] A -->|"10万-1000万"| D["HNSW<br/>默认"] A -->|"> 1000万<br/>+ 频繁 update"| E["IVFFlat<br/>+ 并行构建"] A -->|"超大规模<br/>(> 1 亿)"| F["pgvector 0.8<br/>+ bit + scale"] style D fill:#dcfce7,stroke:#15803d style E fill:#fce7f3,stroke:#be185d

六、距离函数:4 种相似度

6.1 4 种距离

距离 公式 适用
L2 sqrt(Σ(a-b)²) 通用
cosine 1 - dot(a,b)/(‖a‖·‖b‖) text embedding
inner product -dot(a,b) 已经归一化的向量
L1 Σ|a-b| 稀疏 / dim 高

6.2 SQL 用法

-- L2 距离
SELECT * FROM documents
ORDER BY embedding <-> (SELECT embedding FROM documents WHERE id = 1)
LIMIT 5;

-- cosine 距离(text embedding 默认)
SELECT * FROM documents
ORDER BY embedding <=> (SELECT embedding FROM documents WHERE id = 1)
LIMIT 5;

-- inner product(OpenAI 推荐)
SELECT * FROM documents
ORDER BY embedding <#> (SELECT embedding FROM documents WHERE id = 1)
LIMIT 5;

6.3 选择哪种距离

flowchart TB A["text embedding<br/>OpenAI / Cohere / BGE"] --> B["cosine (默认)"] C["图像 / CLIP"] --> E["cosine / L2"] F["已归一化向量"] --> G["inner product"] H["稀疏向量"] --> I["L1 / Jaccard"] style B fill:#dcfce7,stroke:#15803d style E fill:#dcfce7,stroke:#15803d style G fill:#dcfce7,stroke:#15803d

七、量化:halfvec / bit / sparsevec

7.1 为什么需要量化

flowchart LR A["vector(1536) float32<br/>= 6 KB/向量"] -->|"halfvec"| B["3 KB<br/>节省 50%"] B -->|"bit"| C["192 bytes<br/>节省 99.7%"] style A fill:#fee2e2,stroke:#b91c1c style B fill:#fef3c7,stroke:#d97706 style C fill:#dcfce7,stroke:#15803d

7.2 halfvec 用法

-- 创建
CREATE TABLE docs_half (
    id BIGSERIAL PRIMARY KEY,
    embedding halfvec(1536)
);

-- HNSW 索引(halfvec 不支持 L1)
CREATE INDEX ON docs_half USING hnsw (embedding halfvec_cosine_ops);

-- 查询
SELECT * FROM docs_half
ORDER BY embedding <=> (SELECT embedding FROM docs_half WHERE id = 1)
LIMIT 5;

7.3 bit 用法

-- 创建
CREATE TABLE docs_bit (
    id BIGSERIAL PRIMARY KEY,
    embedding bit(1024)
);

-- 索引(只支持 cosine + bit)
CREATE INDEX ON docs_bit USING hnsw (embedding bit_hamming_ops);

-- 查询(Hamming 距离)
SELECT * FROM docs_bit
ORDER BY embedding <~> (SELECT embedding FROM docs_bit WHERE id = 1)
LIMIT 5;

7.4 sparsevec 用法

-- 创建
CREATE TABLE docs_sparse (
    id BIGSERIAL PRIMARY KEY,
    embedding sparsevec(30522)  -- BM25 sparse dim
);

-- 用法
SELECT * FROM docs_sparse
ORDER BY embedding <-> (SELECT embedding FROM docs_sparse WHERE id = 1)
LIMIT 5;

7.5 三种量化对比

维度 vector halfvec bit sparsevec
精度 float32 float16 1bit float32 稀疏
存储 6 KB / 1k 3 KB 128 B 0-100 KB
距离 L2/cos/IP/L1 L2/cos/IP Hamming L2/IP
recall 95%+ 92-95% 85-90% 90%+
适合 通用 内存敏感 超大规模 BM25

八、Hybrid Search:BM25 + 向量

flowchart LR A["Query"] --> B["BM25 关键词检索"] A --> C["向量 ANN 检索"] B --> D["RRF 融合"] C --> D D --> E["top K"] style D fill:#dcfce7,stroke:#15803d

8.2 pgvector + PG tsvector 集成

-- 创建混合表
CREATE TABLE hybrid_docs (
    id BIGSERIAL PRIMARY KEY,
    title TEXT NOT NULL,
    body TEXT NOT NULL,
    title_tsv tsvector GENERATED ALWAYS AS (to_tsvector('simple', title)) STORED,
    embedding vector(1536)
);

-- tsvector GIN + vector HNSW
CREATE INDEX idx_hybrid_tsv ON hybrid_docs USING GIN (title_tsv);
CREATE INDEX idx_hybrid_vec ON hybrid_docs USING HNSW (embedding vector_cosine_ops);

-- 混合查询
WITH bm25 AS (
    SELECT id, ts_rank_cd(title_tsv, plainto_tsquery('simple', $1)) AS score
    FROM hybrid_docs, plainto_tsquery('simple', $1) q
    WHERE title_tsv @@ q
    LIMIT 5
),
vector_q AS (
    SELECT id, 1 - (embedding <=> $2::vector) AS score
    FROM hybrid_docs
    ORDER BY embedding <=> $2::vector
    LIMIT 5
)
SELECT id, bm25.score + vector_q.score AS combined_score
FROM bm25 JOIN vector_q USING (id)
ORDER BY combined_score DESC
LIMIT 5;

8.3 RRF(Reciprocal Rank Fusion)

-- Reciprocal Rank Fusion
WITH bm25_ranked AS (
    SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank_cd(title_tsv, q) DESC) AS rank
    FROM hybrid_docs, plainto_tsquery('simple', $1) q
    WHERE title_tsv @@ q
    LIMIT 20
),
vector_ranked AS (
    SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank
    FROM hybrid_docs
    ORDER BY embedding <=> $2::vector
    LIMIT 20
)
SELECT id,
       SUM(1.0 / (60 + rank)) AS rrf_score
FROM (
    SELECT * FROM bm25_ranked
    UNION ALL
    SELECT * FROM vector_ranked
) AS ranks
GROUP BY id
ORDER BY rrf_score DESC
LIMIT 10;

九、pgvector 性能优化

9.1 8 条优化建议

flowchart TB A["pgvector 性能优化"] --> B["1. 选对距离"] A --> C["2. HNSW ef_search = 100-200"] A --> D["3. HNSW m = 16 (默认)"] A --> E["4. 半精度 = halfvec"] A --> F["5. limit 不要超过 100"] A --> G["6. 并行构建索引"] A --> H["7. partial 索引 优于全量"] A --> I["8. 定期 ANALYZE"] style A fill:#dbeafe,stroke:#1d4ed8

9.2 性能基准(pgvector 0.7, HNSW)

数据集 大小 build query (recall@10=0.95)
ann-bench 1M 1M x 100d 5 min 0.5 ms
ann-bench 10M 10M x 100d 50 min 1 ms
OpenAI 5K x 1536 5K 30 s 0.5 ms
100M x 256 100M 1.5 h 5 ms

十、pgvector 集成

10.1 LangChain

from langchain_postgres.vectorstores import PGVector
from langchain_openai import OpenAIEmbeddings

vectorstore = PGVector(
    embeddings=OpenAIEmbeddings(),
    connection="postgresql://localhost/mydb",
    collection_name="my_docs",
    use_jsonb=True,
)

# 相似度搜索
docs = vectorstore.similarity_search("How does HNSW work?", k=5)

# MMR (Maximal Marginal Relevance)
docs = vectorstore.max_marginal_relevance_search("How does HNSW work?", k=5)

10.2 LlamaIndex

from llama_index.vector_stores.postgres import PGVectorStore
from llama_index.core import StorageContext, VectorStoreIndex

vector_store = PGVectorStore.from_params(
    database="mydb",
    host="localhost",
    user="postgres",
    password="password",
    table_name="my_docs",
    embed_dim=1536,
)

storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)

10.3 SQLAlchemy

from pgvector.sqlalchemy import Vector
from sqlalchemy import create_engine, Column, Integer, Text

engine = create_engine('postgresql://localhost/mydb')
Base.metadata.create_all(engine)

# SQLAlchemy ORM 集成
class Job(Base):
    __tablename__ = 'jobs'
    id = Column(Integer, primary_key=True)
    title = Column(Text)
    embedding = Column(Vector(1536))

十一、pgvector vs 专用向量数据库

11.1 pgvector vs Qdrant / Milvus

维度 pgvector Milvus Qdrant
部署 PG 一份 独立集群 独立集群
SQL 兼容 ✅ ❌ ❌
事务 ✅ ❌ ❌
JOIN ✅ ❌ ❌
索引 HNSW / IVFFlat HNSW / IVF / ANNOY HNSW
量级 RPS 万级 QPS 10万+ QPS 10万+

11.2 何时选专用 vs pgvector

flowchart TB A["向量检索需求"] -->|"RAG / 通用 / 中小数据"| B["pgvector"] A -->|"向量 + SQL 联合查询"| B A -->|"RPS > 1 万"| C["专用(Milvus / Qdrant)"] A -->|"超大规模 (> 10亿)"| C A -->|"GPU 加速"| C style B fill:#dcfce7,stroke:#15803d style C fill:#fce7f3,stroke:#be185d

十二、pgvector 设计哲学:5 个原则

flowchart TB A["pgvector 设计哲学"] --> B["1. 一库多用<br/>SQL + 向量"] B --> C["2. 标准扩展<br/>contrib 而不是外部"] C --> D["3. 多算法<br/>HNSW + IVFFlat"] D --> E["4. 多精度<br/>vector + halfvec + bit"] E --> F["5. 集成优先<br/>LangChain / LlamaIndex"] style A fill:#dbeafe,stroke:#1d4ed8

十三、pgvector 实战 RAG 5 步

13.1 完整 RAG 例子

from openai import OpenAI
import psycopg2
from pgvector.psycopg2 import register_vector

# 1. 客户端连接
conn = psycopg2.connect("postgresql://localhost/mydb")
register_vector(conn)
cur = conn.cursor()

# 2. 查询向量化
client = OpenAI()
query = "How does HNSW work?"
emb = client.embeddings.create(input=query, model="text-embedding-3-small")
query_vec = emb.data[0].embedding

# 3. 向量检索 top 10
cur.execute("""
    SELECT id, content, 1 - (embedding <=> %s::vector) AS similarity
    FROM documents
    ORDER BY embedding <=> %s::vector
    LIMIT 10
""", (query_vec, query_vec))
results = cur.fetch_all()

# 4. 拼成 context
context = "\n".join([r[1] for r in results])

# 5. 调 LLM 生成回答
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": f"基于以下资料回答:\n{context}"},
        {"role": "user", "content": query}
    ]
)

print(response.choices[0].message.content)

十四、pgvector 未来 5 年

gantt title pgvector 5 年路线 dateFormat YYYY-MM axisFormat %b %Y section 稳定 0.8 (PG 18) :done, 2025-09, 2026-09 section 未来 binary quantization :active, 2026-09, 2027-09 GPU 加速 :crit, 2027-09, 2028-09 列存 ANN :crit, 2027-09, 2028-09 range_query + filter :q, 2028-09, 2029-09

十五、源码引用索引

  • src/vector.c — vector 类型实现
  • src/halfvec.c — halfvec 类型
  • src/bit.c — bit 类型
  • src/sparsevec.c — sparsevec 类型
  • src/hnsw.c — HNSW 索引
  • src/ivfflat.c — IVFFlat 索引
  • src/vectorfuncs.c — 距离函数

同系列前文


文章作者: growdu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 growdu !
  目录
分类导航
随笔3 算法1 AI27 计算机基础13 ChatGPT2 博客搭建7 计算机通信1 集群63 数据库39 数据库深入80 Docker11 DPDK26 编辑工具4 Elasticsearch4 FAQ1 Go Web1 hometown2 编程语言16 Linux38 网络9 OPC1 openGauss4 页面12 PostgreSQL54 程序员自我修养1 协议11 stock1 成长之路1 存储5 视频作品1 工具20 VPP18 Vue13 Web1 代码示例11 数据库15 BenchmarkSQL1 PostgreSQL 源码修炼之路14
最热文章
1
13 逻辑复制深入
数据库深入🔥 1570
2
0 Postgresql存储、索引及系统优化、主备切换
PostgreSQL🔥 1495
3
一文读懂openguass dcf网络模块
集群🔥 1420
4
PostgreSQL 元数据存储机制:从磁盘文件到内存缓存,`pg_class` 撑起的整个系统表体系
数据库🔥 1411
5
逻辑复制源码分析
数据库深入🔥 1327
6
PostgreSQL 分区表:从一行 `PARTITION BY` 到路由热路径的全链路拆解
数据库🔥 1094
7
applyparallelworker.c 之 LA 端源码深度解析:Leader Apply Worker 的指挥中枢
数据库深入🔥 1082
8
PostgreSQL Background Worker 全解:从 `RegisterBackgroundWorker` 到逻辑复制 4 类 worker 的全生命周期
数据库🔥 1078
9
PostgreSQL的后台进程walsender分析 - 关系型数据库 - 亿速云
PostgreSQL🔥 1033
10
PostgreSQL 逻辑复制的监控:六张视图 + 一组可执行 SQL,把 publisher/subscriber 的速率与健康度彻底看透
数据库🔥 1032
11
PostgreSQL 逻辑复制支持 DDL 之后:DDL 与 DML 的时序难题(重点:分区表)
数据库🔥 999
12
reorderbuffer.c 源码深度解析:PostgreSQL 逻辑复制的"事务重组引擎
数据库深入🔥 953
13
PostgreSQL 内核开发:读取一张表的 9 步标准流程与缓存全景
数据库🔥 938
14
从 `postgres` 二进制到生产级守护 —— PostgreSQL 最外层模块与启动全流程拆解
数据库🔥 936
15
支持逻辑复制同步 DDL 适配 SQL Server 方案
数据库深入🔥 934
16
PostgreSQL 逻辑复制的 ReorderBuffer 与事务机制:从一行 WAL 到一致性变更流的全链路绑定
数据库🔥 913
17
DDL同步架构(美化版)
数据库深入🔥 908
18
PostgreSQL Latch 机制详解:从一行 SetLatch 到 epoll 的内核之旅
数据库🔥 871
19
pgbench 源码全解:一个 C 文件如何撑起 PostgreSQL 官方压测工具
数据库🔥 860
20
PostgreSQL libpq 机制与缓冲区详解
数据库🔥 850