公司动态
13.pgvector入门用PostgreSQL直接实现向量检
pgvector 入门用 PostgreSQL 直接实现向量检索码海寻道 · 大模型、智能体与 RAG 工程组件系列第 13 篇如果项目已经使用 PostgreSQL是否一定要再部署 Milvus不一定。pgvector 是 PostgreSQL 的向量相似度搜索扩展可以把向量直接存进 PostgreSQL 表并使用 SQL 完成近邻查询。对于中小型知识库、原型项目和希望减少基础设施数量的团队它是一条很有吸引力的路线。本文从安装、建表、写入、查询和索引开始搭建一个最小的文本向量检索示例并说明它适合什么场景、有哪些边界。一、pgvector 解决什么问题PostgreSQL 原生并不是向量数据库。安装 pgvector 扩展后可以获得vector等向量数据类型L2、内积、余弦等距离运算符精确近邻搜索HNSW 和 IVFFlat 等近似索引向量与普通 SQL 条件、JOIN 和事务结合查询。pgvector 官方项目将其定位为 PostgreSQL 的开源向量相似度搜索扩展并强调可以把向量与其他业务数据一起存储。pgvector 是 PostgreSQL 的扩展不是一个独立的向量服务。它的查询、索引构建、WAL、备份、连接数和资源消耗都发生在 PostgreSQL 体系内。这是它简化架构的原因也是它需要和业务 SQL 争抢资源的原因。二、安装和启用扩展具体安装方式取决于操作系统、PostgreSQL 版本和发行版。可以使用系统包、Docker、源码或托管服务提供的扩展。安装完成后在目标数据库中启用CREATEEXTENSIONIFNOTEXISTSvector;扩展是按数据库启用的。即使服务器上已经安装了 pgvector如果当前数据库没有执行CREATE EXTENSION仍然不能使用vector类型。可以检查扩展版本SELECTextname,extversionFROMpg_extensionWHEREextnamevector;生产环境中要记录 PostgreSQL 和 pgvector 版本升级前先阅读兼容性说明并准备回滚和备份方案。三、创建一个向量表向量字段需要指定维度。假设 Embedding 模型输出 1536 维CREATETABLEdocument_chunks(id BIGSERIALPRIMARYKEY,document_id UUIDNOTNULL,tenant_id UUIDNOTNULL,contentTEXTNOTNULL,metadata JSONBNOTNULLDEFAULT{}::jsonb,embedding VECTOR(1536),created_at TIMESTAMPTZNOTNULLDEFAULTnow());这里的VECTOR(1536)必须与 Embedding 模型实际输出维度一致。模型换成 768 维时不能直接把 768 维向量写进vector(1536)字段。在 AI 应用中建议同时保存原始文本片段文档 ID租户 ID页码、标题和版本等元数据Embedding 模型名称和版本。不要只保存一个向量否则检索到结果后无法追溯来源和执行权限过滤。四、写入向量向量可以使用字符串形式插入INSERTINTOdocument_chunks(document_id,tenant_id,content,embedding)VALUES(00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000010,员工年休假申请需要通过内部系统提交。,[0.12, -0.03, 0.44, ...]);示例中的省略号只是说明格式实际写入时必须提供完整的 1536 个数值。批量建库时不建议逐条发送 INSERT。可以使用驱动的批量写入或COPY并在初始数据加载完成后再创建索引以减少建库时间。生产批量导入建议记录批次 ID、文档版本、Embedding 模型版本和成功/失败条数。初始大批量导入可以先写入数据、执行ANALYZE、再创建索引日常增量写入则需要观察索引维护、WAL、锁和查询尾延迟。五、用 SQL 执行最近邻查询pgvector 使用不同运算符表示不同距离运算符含义-L2 距离#负内积余弦距离L1 距离使用余弦距离查询SELECTid,document_id,content,1-(embedding[0.12, -0.03, 0.44, ...])AScosine_similarityFROMdocument_chunksWHEREtenant_id00000000-0000-0000-0000-000000000010ANDembeddingISNOTNULLORDERBYembedding[0.12, -0.03, 0.44, ...]LIMIT5;注意两个细节返回的是余弦距离距离越小越相似如果要展示余弦相似度可以使用1 - distance。pgvector 的#返回负内积这是为了配合 PostgreSQL 只能使用升序索引扫描的行为。不要直接把#的负值当成正向内积解释。六、把普通 SQL 条件和向量检索结合起来这是 pgvector 对业务应用很有吸引力的地方可以在同一条 SQL 中完成租户、状态、版本和时间过滤。SELECTid,content,metadata,1-(embedding$1::vector)ASsimilarityFROMdocument_chunksWHEREtenant_id$2ANDmetadata-statuspublishedANDmetadata-languagezh-CNORDERBYembedding$1::vectorLIMIT$3;这类查询非常适合小型企业知识库。权限和业务过滤可以和向量检索放在同一个关系查询中减少应用层拼接多个系统结果的复杂度。但要通过EXPLAIN (ANALYZE, BUFFERS)检查实际执行计划。过滤条件和近似索引之间的组合可能影响召回数量和执行效率。如果过滤条件很严格近似索引可能先访问有限候选导致最终有效结果不足。pgvector 新版本提供了迭代扫描等能力但是否适用取决于 PostgreSQL 和 pgvector 版本不能把某个版本的参数直接复制到所有环境。遇到空结果或召回不足时应同时比较精确搜索基线、近似搜索计划和过滤后候选数量。七、精确搜索与近似搜索默认情况下pgvector 可以执行精确近邻搜索对候选向量计算距离再返回最准确的结果。优点召回结果准确便于建立评测基线小数据量下实现简单。缺点数据量增大后查询成本上升高并发下 CPU 压力增大。当数据规模和查询压力上升可以建立近似索引。近似搜索通常以少量召回率损失换取更低延迟。八、HNSW 索引使用余弦距离时可以创建CREATEINDEXdocument_chunks_embedding_hnswONdocument_chunksUSINGhnsw(embedding vector_cosine_ops);HNSW 通过多层图结构寻找近邻通常具有较好的速度—召回率平衡。可以调整构建参数CREATEINDEXdocument_chunks_embedding_hnsw_tunedONdocument_chunksUSINGhnsw(embedding vector_cosine_ops)WITH(m16,ef_construction64);参数越激进可能提高召回率但会增加建索引时间、内存和写入成本。具体参数需要压测不建议直接复制到生产环境。如果使用 L2 或内积需要使用对应的 operator class-- L2CREATEINDEXONdocument_chunksUSINGhnsw(embedding vector_l2_ops);-- Inner ProductCREATEINDEXONdocument_chunksUSINGhnsw(embedding vector_ip_ops);同一个查询指标和索引 operator class 必须匹配。九、IVFFlat 索引IVFFlat 先把向量划分到若干列表再只搜索部分列表CREATEINDEXdocument_chunks_embedding_ivfflatONdocument_chunksUSINGivfflat(embedding vector_cosine_ops)WITH(lists100);查询时可以调整探测列表数量BEGIN;SETLOCALivfflat.probes10;SELECTid,contentFROMdocument_chunksORDERBYembedding$1::vectorLIMIT5;COMMIT;probes越大通常召回率越高但查询成本也会上升。IVFFlat 的列表数和数据量有关。数据量很小时过早创建过大的 IVFFlat 索引可能导致效果不理想。可以先用精确搜索建立基线再决定是否需要近似索引。十、批量导入时的建议顺序创建表 ↓ 批量导入文本和向量 ↓ 检查行数、维度和空值 ↓ 创建向量索引 ↓ 执行 ANALYZE ↓ 用真实问题压测示意SELECTCOUNT(*)FROMdocument_chunks;SELECTCOUNT(*)FROMdocument_chunksWHEREembeddingISNULL;ANALYZEdocument_chunks;初始数据加载完成后再建索引通常比边写入边构建更高效。生产环境创建大索引时还要考虑是否使用并发创建以减少对写入的影响。十一、pgvector 的 Python 查询示例下面使用伪代码表示应用层流程真实项目需要根据使用的 PostgreSQL 驱动选择参数绑定方式query_vectorembedding_model.embed_query(user_query)sql SELECT id, document_id, content, 1 - (embedding %s::vector) AS similarity FROM document_chunks WHERE tenant_id %s ORDER BY embedding %s::vector LIMIT %s rowsconnection.execute(sql,(query_vector,tenant_id,query_vector,top_k),).fetchall()连接层同样重要使用连接池限制在线查询和批量导入的并发设置 statement timeout避免一个慢向量查询长期占用连接。大索引创建和重建应安排在可控时段并评估是否需要CREATE INDEX CONCURRENTLY。应用层要确保查询向量和文档向量使用同一个 Embedding 体系参数使用驱动绑定避免 SQL 注入tenant_id和权限过滤不会被遗漏空向量、维度错误和数据库超时有明确处理结果保留文档来源和版本信息。十二、pgvector 的优点1. 架构简单业务数据和向量可以在同一个 PostgreSQL 集群中管理减少服务数量。2. 事务和 JOIN 方便文档状态、权限和向量记录可以使用关系查询组织起来。3. 运维体系成熟可以复用 PostgreSQL 的备份、复制、监控和权限体系。4. 适合快速验证不必一开始部署独立向量数据库就可以完成语义检索和 RAG 原型。十三、pgvector 的边界数据规模和并发向量数量、查询并发和延迟要求达到一定规模后需要评估 PostgreSQL 是否会被向量检索拖累。资源竞争业务 SQL、事务写入、全文检索和向量搜索共享数据库资源可能互相影响。向量索引调优HNSW、IVFFlat、过滤条件和 PostgreSQL 查询计划需要一起调优。扩展方式如果系统需要独立扩展向量查询节点和业务数据库拆分成 PostgreSQL Milvus 可能更合适。备份与恢复pgvector 的向量和索引属于 PostgreSQL 数据库的一部分不能只备份业务表结构而忽略扩展版本、向量数据和索引重建时间。恢复演练至少要验证恢复 PostgreSQL 实例 ↓ 确认 vector 扩展和版本 ↓ 检查向量行数、维度和 NULL 比例 ↓ 确认索引可用或执行重建 ↓ 用固定问题集验证 RecallK 和延迟下一篇会专门比较两种架构什么时候继续使用 pgvector什么时候单独部署 Milvus。十四、上线前检查清单已在目标数据库启用vector扩展向量维度与 Embedding 模型一致查询指标和 operator class 匹配知道是余弦距离而不是相似度精确搜索和近似搜索有对比基线HNSW/IVFFlat 参数经过真实数据压测过滤条件包含租户和权限边界批量导入和建索引顺序合理使用EXPLAIN (ANALYZE, BUFFERS)分析慢查询已规划备份、升级和索引重建。使用连接池、查询超时和并发限制已验证过滤条件下的精确搜索与近似搜索差异已进行包含 pgvector 扩展和索引恢复的演练结语pgvector 是 PostgreSQL 的自然延伸但不是所有规模的终点pgvector 让 PostgreSQL 可以直接保存向量并执行相似度搜索。对于中小型 RAG、原型和关系查询占主导的应用它能以较低的系统复杂度完成完整闭环。但当向量规模、并发、隔离和扩展需求不断增长时仍然要用真实数据评估它的边界。下一篇《什么时候用 pgvector什么时候单独部署 Milvus》参考资料pgvector 官方项目PostgreSQL DocumentationIndexesPostgreSQL DocumentationConcurrency ControlPostgreSQL DocumentationEXPLAIN本文为“码海寻道”原创技术文章。pgvector 的扩展版本、支持维度和索引参数会变化生产环境请以实际版本文档和压测结果为准。