Node.js如何基于pgvector实现PostgreSQL向量检索?

来源:AI技术网作者:湖南程序员头衔:程序员
导读:本期聚焦于湖南程序员创作的《Node.js如何基于pgvector实现PostgreSQL向量检索?》,敬请观看详情。想把向量检索能力直接放进现有的PostgreSQL数据库,又不想额外维护独立的向量数据库,pgvector配合Node.js是一条务实的路线。pgvector让PostgreSQL支持向量类型、距离计算和近似最近邻索引,开发者可以用熟悉的pg驱动完成建表、插入和查询。本文会从扩展启用、表结构设计、向量写入到相似度查询与索引优化,完整演示Node.js端的实现步骤,并说明余弦距离、欧氏距离和内积三种度量方式的选择依据。文中给出的示例代码可以直接运行在本地开发环境,帮助你快速落地语义搜索或推荐系统。

pgvector是PostgreSQL的扩展,为关系型数据库增加了向量数据类型和相似度检索能力。借助pg库,Node.js应用可以复用现有的PostgreSQL连接和事务机制,不必引入独立的向量数据库。本文从扩展启用、表结构、数据写入、相似度查询到索引优化,给出可直接落地的实现方案。

Node.js如何基于pgvector实现PostgreSQL向量检索?

一、启用pgvector扩展并配置Node.js连接

首先需要在PostgreSQL实例中安装pgvector扩展。如果使用的是本地开发库,可以通过源码编译或者直接使用已经打包好的版本。安装完成后,在目标数据库中执行创建扩展语句:

CREATE EXTENSION IF NOT EXISTS vector;

执行成功后,数据库就具备了vector数据类型以及对应的距离运算符。可以使用以下SQL确认扩展是否就绪:

SELECT extname, extversion FROM pg_extension WHERE extname = 'vector';

Node.js端推荐使用pg库,它是最常用的PostgreSQL客户端,支持连接池、参数化查询和事务。安装命令如下:

npm install pg

接着创建一个数据库连接池。连接池能够复用连接,避免每次查询都重新建立TCP连接,适合向量查询这种可能频繁调用的场景。

const { Pool } = require('pg');

const pool = new Pool({
  user: 'postgres',
  host: '127.0.0.1',
  database: 'vectordb',
  password: 'your_password',
  port: 5432,
  max: 20,
  idleTimeoutMillis: 30000,
});

module.exports = pool;

上面的配置使用了127.0.0.1作为本地地址,用户名和密码需要替换成实际环境的值。连接池的最大连接数设置为20,空闲超时30秒,适合中等规模的应用。

二、创建向量表并写入数据

pgvector允许在表中使用vector(n)类型,其中n表示向量维度。维度一旦确定,插入的向量必须保持一致,否则会报错。常见的嵌入模型输出维度有384、768、1536等。例如用text-embedding-3-small模型产生的向量是1536维,可以这样建表:

CREATE TABLE documents (
  id serial PRIMARY KEY,
  content text NOT NULL,
  embedding vector(1536) NOT NULL
);

写入向量时,pgvector接受用方括号包裹的浮点数组字符串,比如:[0.12,0.34,0.56]。Node.js中需要把数组转换成这种格式。下面是一个插入单条记录的函数:

async function insertDocument(content, embeddingArray) {
  const embeddingString = '[' + embeddingArray.join(',') + ']';
  const query = 'INSERT INTO documents (content, embedding) VALUES ($1, $2) RETURNING id';
  const values = [content, embeddingString];
  const result = await pool.query(query, values);
  return result.rows[0].id;
}

如果向量维度较高,手动拼接字符串容易出错。可以封装一个工具函数,把数组统一转为pgvector需要的字符串,同时检查维度是否符合表定义。批量插入时建议使用事务或者pg库的批量操作,减少网络往返。以下是一个批量插入示例:

async function insertBatch(documents) {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    for (const doc of documents) {
      const embeddingString = '[' + doc.embedding.join(',') + ']';
      await client.query(
        'INSERT INTO documents (content, embedding) VALUES ($1, $2)',
        [doc.content, embeddingString]
      );
    }
    await client.query('COMMIT');
  } catch (e) {
    await client.query('ROLLBACK');
    throw e;
  } finally {
    client.release();
  }
}

在生产环境中,上千条向量的写入建议使用PostgreSQL的COPY命令或者批量INSERT语法,避免逐条提交带来的性能损耗。pg库本身支持使用流式查询和COPY FROM,但需要先把数据格式化成适合的文本。

三、执行相似度查询与距离度量选择

pgvector提供了三种距离度量方式:余弦距离、欧氏距离和负内积。余弦距离衡量方向差异,适合文本向量;欧氏距离衡量绝对距离,适合低维坐标;负内积常用于最大化相似度的场景,比如某些推荐模型。对应的运算符分别是<=>、<->和<#>。

查询与某个向量最相似的记录,可以使用如下SQL:

SELECT id, content, embedding <=> $1 AS distance
FROM documents
ORDER BY embedding <=> $1
LIMIT 10;

这里的$1是查询向量的字符串,例如[0.1,0.2,...,0.9]。Node.js中封装的查询函数可以接收一个数组,把它转换成向量字符串,然后执行查询:

async function searchSimilar(queryEmbedding, limit = 10) {
  const embeddingString = '[' + queryEmbedding.join(',') + ']';
  const query = `
    SELECT id, content, embedding <=> $1 AS distance
    FROM documents
    ORDER BY embedding <=> $1
    LIMIT $2
  `;
  const result = await pool.query(query, [embeddingString, limit]);
  return result.rows;
}

注意距离值越小表示越相似。余弦距离的范围是0到2,0表示方向完全一致。如果需要转换成相似度分数,可以用1减去距离。另外,如果要按照负内积排序,运算符写作<#>,此时返回的值越大表示越相似。

不同距离函数对向量的模长敏感程度不同。如果向量已经做了归一化处理,余弦距离和欧氏距离的排序结果基本一致;如果没有归一化,欧氏距离会偏向模长大的向量。实际项目中应根据嵌入模型输出是否归一化来选择合适的度量。

四、创建向量索引提升查询性能

当数据量达到几万条后,全表扫描的相似度查询会变得非常慢。pgvector内置了IVFFlat和HNSW两种近似最近邻索引。IVFFlat基于倒排文件,先对向量空间进行聚类,查询时只搜索最接近的一部分聚类;HNSW基于分层可导航小世界图,召回率通常更高,但构建时间更长、内存占用更大。

给documents表创建HNSW索引可以使用以下SQL:

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);

这里vector_cosine_ops表示使用余弦距离作为索引的度量。如果要使用欧氏距离,应换成vector_l2_ops;使用内积则用vector_ip_ops。索引度量需要与查询时使用的运算符保持一致,否则索引不会被利用。

IVFFlat索引的创建相对简单,但需要在创建前先有足够的数据量,并且要指定聚类的数量。示例:

CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);

创建索引后,可以使用EXPLAIN ANALYZE查看查询是否走了索引。如果执行计划中出现Index Scan using documents_embedding_idx,说明索引生效。另外,HNSW索引会占用较多内存,建议根据服务器内存调整maintenance_work_mem参数,并定期使用VACUUM清理死元组。

在实际应用中,向量查询常常与结构化过滤结合,例如只搜索某一分类下的文档。pgvector索引可以和其他B-tree索引配合使用,PostgreSQL会自动选择合适的执行计划。不过要注意,如果过滤条件选出的行数很少,全表扫描可能比索引更快,需要根据实际数据分布测试。

五、生产环境中的注意事项

将pgvector用于生产之前,需要考虑几个关键点。首先是向量维度与存储空间:1536维的向量大约占用6KB存储,百万级数据会达到数GB,需要规划好磁盘和内存。其次是查询的响应时间,近似索引虽然快,但召回率不是100%,可以通过增大HNSW的ef_search参数来平衡速度与精度。

另一个常见问题是连接池的配置。向量查询可能耗时较长,如果连接池中的连接都被慢查询占用,后续请求会排队。可以为慢查询设置单独的连接池,或者使用pg库的query_timeout选项,避免单个请求阻塞整个服务。

此外,pgvector扩展升级时需要留意版本兼容性。PostgreSQL的小版本升级通常不影响扩展,但大版本升级可能需要重新编译扩展。建议在测试环境先验证升级流程,并备份向量数据。

最后,如果团队已经深度使用PostgreSQL,pgvector是一个自然的过渡方案,能减少技术栈的复杂度。但如果是超大规模的向量检索,比如十亿级数据,还是需要评估专用向量数据库的性能优势。

Node.jspgvector向量检索修改时间:2026-10-06 05:11:58

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1006/66306.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。