TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

用 Postgres 和 pgvector 做语义检索

在 Postgres 上用 pgvector 做语义检索:装好扩展,用 halfvec 存向量,建 HNSW 索引,只重新向量化改动过的文本。讲清楚我们为问答功能搭的这套配置。

这篇适合谁,读完能做到什么

适合已经在用 Postgres、想给自己的内容加上语义检索(按意思而不是按原字搜索)的开发者。读完你应该能装好 pgvector,选出合适的向量类型和距离运算符,建一个 HNSW 索引,并看懂我们为 TellWorld 问答功能搭的这套配置,包括怎样避免对没有改动的文本重复向量化。

pgvector 是什么

pgvector 是一个扩展,给 Postgres 加上了向量类型和最近邻搜索。它提供 vector、halfvec、bit 和 sparsevec 几种类型。halfvec 存半精度元素,占用 2 * dimensions + 8 字节,vector 则是 4 * dimensions + 8 字节;两者都支持最多 16,000 维。[2]

默认情况下,pgvector 做的是精确的最近邻搜索,召回率是完美的。加上 HNSW 或 IVFFlat 这类近似索引,就是用一部分召回率换速度。[2]HNSW 索引会建立一张多层图。它的查询性能比 IVFFlat 好,代价是建索引更慢、占用内存更多。它不需要训练步骤,所以可以在没有数据时就创建。[2]我们用的是 HNSW。

距离运算符有好几种。<-> 是 L2 距离,<#> 是负内积,<=> 是余弦距离,<+> 是 L1 距离,<~> 是二值向量的汉明距离,<%> 是二值向量的 Jaccard 距离。README 说明,<#> 返回的是负内积,因为 Postgres 的索引扫描只支持 ASC 排序。[2]

准备

README 给出的是从源码编译安装的方式,版本是 v0.8.7。Linux 和 Mac 的源码安装支持 Postgres 13+。[2]CHANGELOG 列出的最新正式版是 0.8.7 (2026-10-01),下一个版本 0.9.0 尚未发布。[3]安装时要固定版本,升级之前先看一下 CHANGELOG。

用 Docker 的话,镜像 pgvector/pgvector:pg18-trixie 会在官方 Postgres 镜像上加入 pgvector。[2]用 Homebrew 装的 Postgres 则执行 brew install pgvector,它只会加到 postgresql@18 和 postgresql@17 两个 formula 上。[2]README 还列出了 PGXN 的 pgxn install vector、APT 的 sudo apt install postgresql-18-pgvector、Yum 的 sudo yum install pgvector_18、APK 的 apk add postgresql-pgvector,以及 conda-forge 的 conda install -c conda-forge pgvector。[2]

向量化我们用的是阿里云百炼(Alibaba Cloud Model Studio)。它的 text-embedding-v4 是一个多语言文本向量模型,由 Qwen3 训练而来,支持 64 到 2048 之间的自定义维度。[4]北京地域的限流是 RPM 1800、TPM 1,200,000。[4]该模型按输入 token 计费,输出不计费。[6]北京地域的输入单价是每百万 token 0.5 元,Batch 调用半价,另有 100 万 token 的免费额度,有效期 90 天。[5][6]

上手步骤

  1. 从源码安装 pgvector。README 给出的命令是:[2]
cd /tmp
git clone --branch v0.8.7 https://github.com/pgvector/pgvector.git
cd pgvector
make
make install # may need sudo
  1. 在每个要用到它的数据库里执行一次 CREATE EXTENSION vector; 启用扩展。[2]
  1. 建 HNSW 索引。README 的示例在一张叫 items 的表上,写法是 CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);。m 和 ef_construction 默认分别是 16 和 64,可以用 WITH (m = 16, ef_construction = 64) 设置。[2]HNSW 索引按距离函数分别创建。README 还列出了 vector_ip_ops、vector_cosine_ops、vector_l1_ops、bit_hamming_ops 和 bit_jaccard_ops,并说 halfvec 应使用 halfvec_l2_ops。[2]我们用的是 halfvec_cosine_ops,查询时对应的运算符是 <=>。[2]
  1. 查询时用距离运算符做升序的 ORDER BY,并加上 LIMIT。查询必须同时具备这两者,索引才会被使用。[2]搜索的候选列表由 hnsw.ef_search 控制,默认值是 40。用 SET LOCAL 可以只对当前事务生效:[2]
BEGIN;
SET LOCAL hnsw.ef_search = 100;
SELECT ...
COMMIT;
  1. 确定向量维度和批量大小。text-embedding-v4 默认 1024 维;北京地域的表格列出的可选维度有 2,048、1,536、1,024、768、512、256、128 和 64。[5]批次大小是 10,即一次请求最多向量化 10 段文本,每段不超过 8192 个 token。[5]北京地域单批次最多处理 33,000 个 token,新加坡地域则是 8,192。[5]
  1. 把客户端指向兼容 OpenAI 的端点。北京的 base_url 是 https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1,新加坡地域是 https://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1。密钥从环境变量 DASHSCOPE_API_KEY 读取。[5]

我们的做法

TellWorld 的每个条目都会被切成若干分块,存进 cms_chunk 表。每一行包含文本、token 数、内容哈希、模型名和向量。[1]向量列是 halfvec(1024),用 halfvec_cosine_ops 建 HNSW 索引。[1]1024 与 text-embedding-v4 的默认维度一致。[5]同一张表上还有一个基于文本的 trigram GIN 索引(gin_trgm_ops),用于关键词匹配。[1]

向量来自兼容 OpenAI 的 /embeddings 端点,默认模型是 text-embedding-v4。[1]我们只对内容有改动的分块重新向量化,能做到这一点,靠的就是内容哈希。2026 年 11 月 13 日的一次运行涉及 5,342 篇文档、34,228 个分块,其中只重新向量化了 94 个分块,用了 7,531 个 token。[1]网站的问答功能就是从这些分块中检索的。[1]

每一行都记录模型名,在更换模型时就能分辨每个向量是哪个模型生成的。[1]这是我们针对自己内容做的设计。分块数量和 token 消耗都来自我们自己的运行,别的站点看到的数字会不同。

限制和常见问题

  • 使用近似索引时,过滤条件是在索引扫描之后才应用的。如果某个条件只匹配 10% 的行,那么在 HNSW 和默认的 hnsw.ef_search 为 40 的情况下,平均只会有大约 4 行被匹配。[2]
  • 对于 HNSW,如果过滤条件去掉了太多候选,查询返回的行数可能少于你要求的数量。README 介绍了迭代索引扫描(0.8.0 引入),用 SET hnsw.iterative_scan = strict_order; 开启。[2]
  • README 建议在生产环境使用 CREATE INDEX CONCURRENTLY,以免阻塞写入。[2]
  • NULL 向量不会被索引;对余弦距离来说,零向量也不会被索引。[2]
  • HNSW 的限制是:vector 最多 2,000 维,halfvec 最多 4,000 维,bit 最多 64,000 维,sparsevec 最多 1,000 个非零元素。[2]
  • 另一种索引是 IVFFlat。应在表里已有数据之后再创建。lists 在 100 万行以内取 rows / 1000,超过 100 万行取 sqrt(rows);probes 从 sqrt(lists) 开始调。README 用 SET ivfflat.probes = 10; 设置 probes。[2]
  • 批量调用的向量化费用是实时调用的一半,但批次的限制依然适用。官方文档建议,对于大规模的非实时文本,用 text-embedding-v4 配合兼容 OpenAI 的 Batch 调用,可显著降低成本。[5]
  • 我们的 1024 维和 halfvec 的 4,000 维上限相比,都在 HNSW 的限制之内。[2]

下一步

先读 pgvector 的 README[2]和 CHANGELOG。[3]再读阿里云百炼的向量化文档[5]和计费页面。[6]正式上线前,先对一小批文档完整跑一遍重新向量化,把 token 数与计费页面对一下。

来源

  1. 1
  2. 2
    pgvector README (master)pgvector (official GitHub repository)
  3. 3
    pgvector CHANGELOG (master)pgvector (official GitHub repository)
  4. 4
    text-embedding-v4 模型信息阿里云百炼 (Alibaba Cloud Model Studio)
  5. 5
    向量化(Embedding)阿里云百炼 (Alibaba Cloud Model Studio)
  6. 6
    模型计费 (billing-for-model-studio)阿里云百炼 (Alibaba Cloud Model Studio)

引用本页: 用 Postgres 和 pgvector 做语义检索. TellWorld, https://www.tellworld.net/zh/practice/practice-pgvector-search. 更新于 2026-11-14

相关动态