大模型应用
用 pgvector 在 PostgreSQL…
当需要在 PostgreSQL 中存储 embedding 并做语义/相似度/最近邻搜索(含 HNSW/IVFFlat 索引、量化、过滤搜索、批量导入与性能调优)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 pgvector 在 Postgre通用
解决思路
- 启用扩展:在每个数据库中执行 `CREATE EXTENSION IF NOT EXISTS vector;`(需 PostgreSQL 15+ 与 pgvector 0.8.0+)。
- 默认采用黄金配置:embedding 列用 `halfvec(N)`(N 为维度,示例用 1536),距离用余弦 `<=>`,索引用 HNSW(`m = 16`, `ef_construction = 64`),查询时 `SET hnsw.ef_search = 100`,查询模式为 `ORDER BY embedding <=> $1::halfvec(N) LIMIT k`。
- 默认使用 HNSW 索引:速度-召回权衡更好,可在空表上创建,无需训练步骤;仅在写密集或内存受限时才考虑 IVFFlat。
- 默认使用 `halfvec` 存储与索引,可减少约 50% 存储与索引体积且召回损失极小。
- 初始数据先批量导入,再建索引以获得最佳构建性能。
- 生产环境用 `CREATE INDEX CONCURRENTLY ...` 并发建索引。
- 默认使用余弦距离 `<=>`;对未归一化 embedding 用余弦,对单位归一化 embedding 余弦与内积排序一致,仍默认余弦。
- 查询算子必须与索引 ops 匹配:`halfvec_cosine_ops` 配 `<=>`,`halfvec_l2_ops` 配 `<->`,不匹配则不会走索引。
- 查询向量始终显式转型:`$1::halfvec(N)`,避免 prepared statement 中的隐式转换失败。
- 数据与查询必须使用同一个 embedding 模型。
- 类型规则:embedding 存为 `halfvec(N)`,查询向量转为 `halfvec(N)`,二值量化向量存为生成列 `bit(N)`;不要混用 `vector`/`halfvec`/`bit` 而不显式转型;不要在 `ORDER BY` 中对表列调用 `binary_quantize()`,应预先存储;维度必须一致。
- 标准建表与查询模式:`CREATE TABLE items (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, contents TEXT NOT NULL, embedding halfvec(1536) NOT NULL);` 然后 `CREATE INDEX ON items USING hnsw (embedding halfvec_cosine_ops);` 查询 `SELECT id, contents FROM items ORDER BY embedding <=> $1::halfvec(1536) LIMIT 10;`。
- HNSW 参数:`m` 默认 16(越大召回越好、内存越多),`ef_construction` 默认 64(越大图质量越好、构建越慢),`hnsw.ef_search` 默认 40(越大召回越好、查询越慢,且应 ≥ LIMIT)。
- ef_search 调优参考:40 约 1x 速度(较低召回),100 约 2x 慢(较高召回),200 约 4x 慢(很高召回),400 约 8x 慢(接近精确)。
- 设置 ef_search:会话级 `SET hnsw.ef_search = 100;`,单查询级用 `BEGIN; SET LOCAL hnsw.ef_search = 100; ... COMMIT;`。
- IVFFlat 仅在写密集/频繁重建/内存紧张/冷分区场景使用;需先有数据再建索引,召回取决于 `lists` 与 `ivfflat.probes`(probes 越高召回越好但越慢)。起始配置:`CREATE INDEX ON items USING ivfflat (embedding halfvec_cosine_ops) WITH (lists = 1000);` 与 `SET ivfflat.probes = 10;`。
- 量化是内存决策而非召回决策:默认用 `halfvec`;估算 HNSW 索引占用约每 1536 维 `halfvec`(m=16)4–6 KB,3072 维约 2 倍,m=32 约再翻倍。
- 容量参考(1536 维、m=16):16 GB 约 2–3M 向量,32 GB 约 4–6M,64 GB 约 8–12M,128 GB 约 16–25M;3072 维除以约 2,m=32 再除以约 2。
- 若 p95/p99 延迟升高而 CPU 空闲,说明 HNSW 索引可能已不在内存中;若 `halfvec` 放不下,改用二值量化 + 重排序。
- 二值量化(32× 内存缩减,需配合重排序):建表加生成列 `embedding_bq bit(1536) GENERATED ALWAYS AS (binary_quantize(embedding)::bit(1536)) STORED`,建索引 `CREATE INDEX ON items USING hnsw (embedding_bq bit_hamming_ops);`,查询时 `SET hnsw.ef_search = 800;` 先用 `ORDER BY i.embedding_bq <~> q.qb LIMIT 800` 取候选,再在外层 `ORDER BY candidates.embedding <=> $1::halfvec(1536) LIMIT 10` 重排序;80× 过采样(10 结果取 800 候选)是合理起点,召回不足则增大,重排序延迟过高则减小。
- 按数据规模调优:<100K 用默认(索引可选但改善尾延迟);100K–5M 用默认并监控 p95;5M+ 用 `ef_construction=100+` 且内存驻留关键;10M+ 用二值量化 + 重排序,优先加 RAM 或分区。
- 先调 `ef_search` 提升召回;仅当召回停滞且内存允许时才增大 `m`;并发下索引不驻留内存会导致尾延迟飙升;二值量化是兜底手段,优先加 RAM 或分区。
- 过滤搜索:当过滤条件显著缩小结果集时启用迭代扫描 `SET hnsw.iterative_scan = relaxed_order;`,结果仍稀疏时增大 `SET hnsw.max_scan_tuples = 50000;`(提高召回但可能显著增加延迟)。
- 迭代扫描不需要的场景:过滤匹配表中大部分行(低选择性)、已通过 B-tree 索引预过滤、查询单个分区或部分索引。
- 过滤策略选择:高选择性过滤(约 1 万行以下)在过滤列建 B-tree 索引预过滤;低基数过滤(少量不同值)按过滤值建部分 HNSW 索引(如 `WHERE category_id = 11`);多过滤值或大数据集按过滤键分区(如 `PARTITION BY LIST (category_id)`)。
- 过滤查询关键规则:匹配少量行的过滤需要预过滤、分区或迭代扫描;始终在真实负载下测量 p95/p99 延迟与访问元组数来验证。
- 大数据集标签过滤可考虑 pgvectorscale 的 StreamingDiskANN 索引,支持 `smallint[]` 列上的过滤索引,避免 HNSW 后过滤的精度损失。
- 批量导入:用 `COPY` 最快(文本格式 `'[0.1, 0.2, ...]'`,二进制格式需客户端支持);导入后再建索引,并设置 `SET maintenance_work_mem = '4GB';` 与 `SET max_parallel_maintenance_workers = 7;`。
- 维护:更新/删除后定期 `VACUUM`(陈旧条目可能残留);高变更后性能下降则 `REINDEX` 重建图;写密集且频繁删除可考虑 IVFFlat 或按时间用 hypertables 分区。
- 监控与调试:用 `pg_size_pretty(pg_relation_size('items_embedding_idx'))` 查索引大小;用 `EXPLAIN (ANALYZE, BUFFERS)` 调试查询;用 `pg_stat_progress_create_index` 监控建索引进度;用 `SET LOCAL enable_indexscan = off;` 对比近似与精确召回;用 `SET LOCAL enable_seqscan = off;` 强制走索引调试。
- 常见问题修复:查询不走 ANN 索引 → 补 `ORDER BY` + `LIMIT`、算子匹配 ops class、显式转型查询向量;过滤查询结果偏少 → 启用迭代扫描、增大 `hnsw.max_scan_tuples`、或预过滤/部分索引/分区;非过滤查询结果偏少 → 增大 `hnsw.ef_search`;高延迟低 CPU → 用 `halfvec`、减小 `m`/`ef_construction`、加 RAM、分区或二值量化;建索引慢 → 增大 `maintenance_work_mem` 与 `max_parallel_maintenance_workers`,批量导入后再建;OOM → 用 `halfvec`、减小索引参数或改二值量化 + 重排序;零结果/缺结果 → 避免 NULL embedding,余弦距离下不要用零向量。
适用边界 · 注意事项
- 需要选择 embedding 模型或做文本分块时(本技能不覆盖,这两者显著影响搜索质量)。
- 没有 PostgreSQL 15+ 或无法安装 pgvector 0.8.0+ 扩展时。
- 需要最高召回且低延迟、最小调优、或“一劳永逸”的索引时,不要用 IVFFlat。