Files
zhuangang/topics/ai/vector-db/02-selection-pgvector.md

4.9 KiB
Raw Permalink Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week, order
id title type domain stack tags level prerequisites est_hours related week order
ai.vector-db.selection-pgvector 向量库选型和 pgvector 实战:Chroma / pgvector / Milvus / Elasticsearch lesson ai
vector-db
postgresql
向量库选型
pgvector
Chroma
Milvus
Elasticsearch
元数据过滤
intermediate
ai.vector-db.ann-metrics
3
ai.vector-db.scenario.multi-tenant-index
backend.postgresql.index
backend.elasticsearch.index
5
6
2

向量库选型和 pgvector 实战:Chroma / pgvector / Milvus / Elasticsearch

解释说明

选型对比

方案 适合 优点 注意
Chroma 原型、学习、小规模 安装即用,Python 内嵌模式 大规模、高可用要评估;版本间 API 有变化
pgvector(PostgreSQL 扩展) 已有 PostgreSQL、百万级以内、需要和业务数据联查 一套数据库;SQL 过滤、事务、备份都复用 超大规模和高 QPS 时不如专用向量库;向量索引的内存占用要规划
Milvus 千万到亿级、需要分布式 多种索引、分区、标量过滤、水平扩展 组件多,运维成本高(也有托管版)
Elasticsearch / OpenSearch 已经用 ES 做全文检索,想要关键词 + 向量混合检索 一个引擎做 BM25 + kNN 向量功能的版本差异较大,以官方文档为准
FAISS 嵌入到程序里的向量检索库 快,索引类型全 是库不是数据库:没有持久化服务、过滤、权限,需要自己做

本计划的选择:学习阶段用 Chroma 或 numpy 快速理解;项目使用 pgvector,因为第 3~4 周已经学了 PostgreSQL,业务数据、权限和向量可以放在同一个库里联查;面试时能讲清楚“为什么不用 Milvus”和“什么规模需要换”。

pgvector 要点(以官方文档为准:https://github.com/pgvector/pgvector)

  • 类型:vector(n);还有 halfvec(半精度,省一半空间)、sparsevec。
  • 运算符:<-> L2 距离,<#> 负内积,<=> 余弦距离(都是越小越近)。
  • 索引:USING hnsw (embedding vector_cosine_ops),运算符类必须和查询用的运算符一致(vector_l2_ops / vector_ip_ops / vector_cosine_ops),否则不走索引。
  • 查询参数:SET hnsw.ef_search = 100;(默认 40);IVFFlat 用 ivfflat.probes。
  • 过滤:WHERE tenant_id = $1 ORDER BY embedding <=> $2 LIMIT 10。过滤条件很严格时,0.8 版本可以开启 hnsw.iterative_scan 让索引继续扫描直到凑够结果;或者按租户建部分索引 / 分区。

案例

一个 SaaS 客服系统,50 个租户共 200 万个知识块,平均每个租户 4 万,最大租户 60 万。业务表已经在 PostgreSQL 中。选择 pgvector:块表带 tenant_id,HNSW 索引 + tenant_id 普通索引;大租户检索结果不足时开启迭代扫描。评估结论写进文档:当总量超过数千万或 QPS 超过单机能力时,再评估 Milvus。

代码示例

pgvector 的表结构和查询(完整可运行版本 examples/vector-stores/pgvector_demo.py,已在 PostgreSQL 17 + pgvector 0.8.0 上运行):

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE chunks (
    id         BIGSERIAL PRIMARY KEY,
    tenant_id  TEXT NOT NULL,
    doc_id     TEXT NOT NULL,
    version    INT  NOT NULL,
    content    TEXT NOT NULL,
    embedding  vector(1024) NOT NULL
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX ON chunks (tenant_id);
CREATE INDEX ON chunks (doc_id);

-- 查询:$1 租户,$2 查询向量
SET hnsw.ef_search = 100;
SELECT id, doc_id, content, 1 - (embedding <=> $2) AS similarity
FROM chunks
WHERE tenant_id = $1
ORDER BY embedding <=> $2
LIMIT 10;

Chroma 的写入、元数据过滤、更新删除见 examples/vector-stores/chroma_demo.py。

易错点

  • 建索引用了 vector_l2_ops,查询用 <=>:不走索引,变成全表扫描。
  • <#> 返回的是负内积,排序时直接 ORDER BY embedding <#> q 就是内积从大到小;自己再加负号就反了。
  • 小表的执行计划显示顺序扫描:数据少时规划器认为顺序扫描更便宜,这是正常的,不是索引失效。
  • HNSW 索引默认不支持太高的维度(vector 类型的索引上限 2000 维,halfvec 4000 维,以官方文档为准),高维模型要选 halfvec 或降维。
  • 大批量导入时边写边维护 HNSW 索引很慢:先导入再建索引,并适当调大 maintenance_work_mem。
  • Chroma 不同版本的客户端 / 持久化格式有变化,升级前看迁移说明。

面试常问

  • 为什么选 pgvector 而不是 Milvus?什么时候要换?
  • pgvector 的三种距离运算符分别是什么?索引怎么建才能生效?
  • 向量和业务数据放一个库有什么好处和风险?