88 lines
4.9 KiB
Markdown
88 lines
4.9 KiB
Markdown
---
|
||
id: ai.vector-db.selection-pgvector
|
||
title: 向量库选型和 pgvector 实战:Chroma / pgvector / Milvus / Elasticsearch
|
||
type: lesson
|
||
domain: ai
|
||
stack: [vector-db, postgresql]
|
||
tags: [向量库选型, pgvector, Chroma, Milvus, Elasticsearch, 元数据过滤]
|
||
level: intermediate
|
||
prerequisites: [ai.vector-db.ann-metrics]
|
||
est_hours: 3
|
||
related: [ai.vector-db.scenario.multi-tenant-index, backend.postgresql.index, backend.elasticsearch.index]
|
||
week: [5, 6]
|
||
order: 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](examples/vector-stores/pgvector_demo.py),已在 PostgreSQL 17 + pgvector 0.8.0 上运行):
|
||
|
||
```sql
|
||
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](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 的三种距离运算符分别是什么?索引怎么建才能生效?
|
||
- 向量和业务数据放一个库有什么好处和风险?
|