最近在Windows上搭一个本地知识库的小项目,核心需求很简单:把文档切成片段后用embedding模型转成向量,存起来,再按语义做相似度检索。一开始想用现成的向量数据库,后来考虑到项目里还有一堆用户表、文档表、问答记录,干脆直接用PostgreSQL,再给它装上pgvector这个扩展,一套数据库同时管业务数据和向量数据。整个过程在Windows上不是“下一步下一步”就能完成的,中间踩了不少坑,今天把完整过程和经验整理出来,给想在Windows环境里实现向量存储的朋友做参考。
说明:本文操作基于Windows 11 x64、PostgreSQL 16系列和pgvector 0.8.x,其他版本流程一致,只需要注意路径和版本号差异。
1. 为什么选pgvector做向量存储
1.1 pgvector到底解决什么问题
要讲清楚这个,得先明白什么是“向量存储”。现在的AI应用,比如对话机器人、本地知识库问答、商品推荐系统,普遍的做法是先把文本或图片丢给embedding模型,转成几百上千维的浮点数组,这个数组就叫向量。向量有一个特性:语义越接近的内容,在向量空间里的距离就越近。比如“如何重置密码”和“密码忘了怎么办”这两句话,虽然字面不一样,但向量距离会非常小。
pgvector就是PostgreSQL生态里专门干这件事的扩展。它给数据库增加了一种vector类型,可以存固定维度的浮点数组,提供欧氏距离、余弦距离、内积距离三种相似度算法,还实现了IVFFlat和HNSW两种索引,让几十万条数据里的最近邻搜索不至于慢到全表扫描。说人话就是:PostgreSQL装上pgvector以后,向量就变成了表里的普通字段,可以用SQL做“找到跟这个向量最相似的前10条记录”这种操作。
在Windows上装pgvector,本质上是给PostgreSQL装一个原生动态链接库。这一点和Linux下用包管理器一行命令搞定不太一样,Windows下的安装路径曲折不少,但完全可行。这篇文章的核心就是把这个安装过程讲透,再把向量存储的建表、插入、查询实际操作一遍。
1.2 为什么不直接用专用向量数据库
不少做AI应用的朋友一上来就想上Milvus、Weaviate、Qdrant这类专用向量数据库,或者用FAISS直接做内存索引。这些方案在超大规模数据、高并发检索场景下确实很强,但是对一个刚开始做原型、数据量几十万条的本地项目来说,有点杀鸡用牛刀。
用pgvector最大的好处是能复用PostgreSQL全家桶的能力。我的项目里有用户表、文档表、问答记录表,这些业务数据天然要关联,如果分两套数据库,就得自己维护同步、处理事务一致性问题。而pgvector让向量数据跟普通字段一样参与SQL查询、JOIN、事务、备份恢复,开发心智负担小很多,团队里的人上手也快。
当然,缺点也要说清楚:与专用向量数据库相比,千万级以上数据的检索性能有差距,高级特性比如分片、混合检索里的稀疏向量支持也没有那么顺手。选型本质上是匹配场景,中小规模、偏传统业务的项目用pgvector是性价比很高的组合,这也是我在Windows上折腾它的直接动机。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Windows环境准备:版本选型和工具链
2.1 PostgreSQL版本选择和安装要点
pgvector官方支持PostgreSQL 13到17,我在实际安装时推荐直接用16或17,社区资料最多,遇到问题容易搜到答案。下载时认准Windows x86-64版,安装过程中有几个关键点必须注意:
- 安装目录默认是
C:\Program Files\PostgreSQL\16,这个路径后面频繁使用,最好记下来; - 设置超级用户postgres的密码,一定要记牢,后面连数据库全靠它;
- 端口保持默认5432即可,如果本机被其他服务占用再改;
- 字符集和地区设置我建议保持默认,只要确定库里的数据以中文和英文为主,默认UTF8编码就能满足需求;
- 安装向导最后一步会问是否启动Stack Builder,那个取消就行,不需要。
装完之后,先在命令行执行psql --version确认能否直接调用。如果提示找不到命令,说明PostgreSQL的bin目录没加到PATH里,需要手动把C:\Program Files\PostgreSQL\16\bin加入系统环境变量。还有一个我习惯做的动作:打开pgAdmin连一次数据库,确认服务正常,顺便看一眼postgresql.conf和pg_hba.conf的路径,后面排查问题时经常要用到这两个文件。
2.2 安装pgvector前需要准备的Windows工具链
很多人在Windows上第一次搜pgvector安装教程时,会看到Linux用户直接一条apt install postgresql-16-pgvector就完事,但在Windows上没这么容易。原因在于PostgreSQL扩展本质上是C语言写的动态库,每个PostgreSQL版本编译出来的二进制接口都不一样,官方只提供源码,把源码编译成Windows能用的DLL,这个步骤需要自己完成。
做这件事最主流的工具链是Visual Studio Build Tools。不需要装完整的Visual Studio IDE,装Build Tools就够。安装时在“工作负载”里勾选“使用C++的桌面开发”,它会自动带上MSVC编译器和Windows SDK。装完后,开始菜单里会出现一个叫x64 Native Tools Command Prompt for VS 2022的快捷方式,编译pgvector必须用它打开命令行,因为只有这个环境里才有cl.exe和nmake.exe。
另外还需要PostgreSQL的源码目录,因为编译扩展时会引用PostgreSQL的头文件和库文件。这里有个常见误区:有人只装了pgAdmin,没装完整PostgreSQL server,导致pg_config不存在,编译第一步就爆错。我的习惯是先确认C:\Program Files\PostgreSQL\16\bin\pg_config.exe能正常执行,再往下走。
注意:编译pgvector用到的
pg_config.exe,必须来自你要安装扩展的那个PostgreSQL实例。如果本机装了多个版本PostgreSQL,PATH里的pg_config很可能指向旧版,编译出来的扩展装到新库里就会加载失败。
3. 在Windows下安装pgvector的三种方式
3.1 方式一:使用预编译DLL(最快但有版本陷阱)
社区里有一些开发者会把自己编译好的pgvector Windows DLL发布到GitHub的Release页面,搜索“pgvector Windows dll”就能找到相关项目。如果你赶时间,或者暂时不想装Visual Studio,可以先下载对应PostgreSQL版本的预编译包来用。
基本操作步骤如下:
- 确认本机PostgreSQL的大版本,比如16.x就下载标注16的预编译包;
- 把下载得到的
vector.dll复制到C:\Program Files\PostgreSQL\16\lib目录; - 把
vector.control和所有vector--*.sql文件复制到C:\Program Files\PostgreSQL\16\share\extension目录; - 重启PostgreSQL服务,或者重启一次连接池,让数据库重新加载扩展文件。
这种方式坑也不少。首先是兼容性问题,第三方预编译DLL的编译环境、PostgreSQL小版本、编译开关都可能影响最终效果,很多时候复制完执行CREATE EXTENSION报错,根本原因就是版本对不上。其次,从不明来源下载可执行文件有安全风险,建议只有在你完全信任发布者时才用。另外,PostgreSQL的小版本之间二进制通常也不兼容,比如16.4的DLL放到16.6里可能直接加载失败,下载前务必看清说明。
3.2 方式二:源码编译(最稳妥,推荐动手)
这是官方README推荐的做法,也是我在Windows上最终稳定跑通的方式。下面按步骤写清楚。
第一步:准备源码
把PostgreSQL源码和pgvector源码都准备好。PostgreSQL源码从官方下载对应版本的源码包解压即可,pgvector用git克隆到本地:
bash复制git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git
这里要强调一下,PostgreSQL源码不需要整个编译安装,我们只是借它的src/include头文件和部分库文件,供pgvector编译时引用。
第二步:打开编译环境
从开始菜单打开x64 Native Tools Command Prompt for VS 2022,这个环境下cl.exe、nmake.exe等工具都自动加进PATH了。然后用cd进入pgvector源码目录。如果你不想每次手动切环境,也可以在普通cmd里执行vcvars64.bat来加载环境,但直接用快捷方式最省事,也最不容易出错。
第三步:设置PG_CONFIG并编译
在命令行里先设置环境变量:
bat复制set PG_CONFIG=C:\Program Files\PostgreSQL\16\bin\pg_config.exe
然后执行:
bat复制nmake /F Makefile.vc
如果一切正常,会在src目录下生成vector.dll,在根目录下生成vector.control和vector--0.8.0.sql等文件。我实际编译时遇到最多的问题是pg_config找不到,通常是PG_CONFIG没设置或者路径写错;其次是提示缺少某个头文件,这多半是PostgreSQL源码路径没对上,或者VS组件没装全。建议编译前先执行pg_config --includedir-server回显一下路径,确认它确实指向正确的PostgreSQL源码。
第四步:复制文件到PostgreSQL目录
编译成功后把产物复制进PostgreSQL安装目录:
bat复制copy src\vector.dll "C:\Program Files\PostgreSQL\16\lib"
copy vector.control "C:\Program Files\PostgreSQL\16\share\extension"
copy vector--*.sql "C:\Program Files\PostgreSQL\16\share\extension"
提示:复制前最好先重启PostgreSQL服务,或者至少在复制完成后执行
net stop postgresql-x64-16再net start postgresql-x64-16。否则DLL可能被进程占用,复制失败还是小事,数据库加载的还是旧文件才是大坑。
3.3 方式三:MSYS2环境下的备选方案
如果你平时习惯在Windows上用MinGW工具链,也可以考虑用MSYS2来安装pgvector。在MSYS2 shell里执行:
bash复制pacman -S mingw-w64-x86_64-pgvector
装完之后,在MSYS2的安装目录里找到编译好的扩展文件,复制到PostgreSQL的lib和share\extension目录即可。这个方式的优点是不用手动配置Visual Studio,缺点是需要额外装一套MSYS2环境,而且MSYS2软件源里的PostgreSQL工具链可能和你安装的PostgreSQL大版本不匹配。我把它定位为“懒人备选”,能跑通,但排查问题时不如源码编译直观。
4. 验证安装并实现向量存储
4.1 创建扩展和向量表
无论用上面哪种方式安装,最后都要在数据库里启用扩展。先用psql连接目标数据库:
bat复制psql -U postgres -d mydb
然后执行:
sql复制CREATE EXTENSION IF NOT EXISTS vector;
这一步没有报错,说明DLL和SQL文件都放对了位置。可以再执行下面这句做二次确认:
sql复制SELECT vector_dims('{1,2,3}'::vector);
返回3就代表扩展已经成功加载。接着建一张带向量字段的表:
sql复制CREATE TABLE documents (
id bigserial PRIMARY KEY,
title text NOT NULL,
content text,
embedding vector(768)
);
这里的vector(768)指定了向量维度,维度由你选用的embedding模型决定。比如OpenAI的text-embedding-3-small输出1536维,BGE-M3输出1024维,MiniLM-L6-v2输出384维,建表前一定要确认清楚。维度不匹配时,pgvector会在插入时报different vector dimensions错误,这类问题在项目迭代中特别容易出现,所以我建议把模型版本、向量维度这类元信息写进表注释或者项目配置里。
4.2 写入向量数据
pgvector接受字符串形式的向量,可以直接用文本数组的字面量插入:
sql复制INSERT INTO documents (title, content, embedding)
VALUES (
'如何重置密码',
'在登录页面点击忘记密码,按邮件提示操作。',
'[-0.012, 0.021, -0.005, 0.034]'::vector
);
实际项目里的向量通常来自Python调用embedding接口后的返回结果。可以用Python拼成字符串后直接插入:
python复制embeddings = get_embedding(text) # 返回 [0.012, -0.023, 0.001, ...]
cur.execute(
"INSERT INTO documents (title, content, embedding) VALUES (%s, %s, %s::vector)",
(title, content, str(embeddings))
)
这里有一个我一直强调的细节:每次插入的向量维度必须和建表时一致。如果embedding模型升级后输出维度变了,旧数据和新数据就彻底无法互相比较相似度。生产环境里建议把模型名称、版本、维度统一记录在配置中心,并在写入接口里做维度校验。想要批量导入大量数据时,先用COPY从文件加载会比逐条INSERT快很多,这里也顺便提一句。
4.3 相似度查询和索引优化
pgvector提供三个距离操作符:
<->欧氏距离,数值越小越相似;<=>余弦距离,常用于文本语义相似度;<#>内积距离,适合已经做过归一化的向量。
最常见的文本检索是按余弦距离排序取前N条:
sql复制SELECT id, title, embedding <=> '[-0.012, 0.021, -0.005, 0.034]' AS distance
FROM documents
ORDER BY embedding <=> '[-0.012, 0.021, -0.005, 0.034]'
LIMIT 5;
数据量小的时候,这样全表算距离也能接受,响应时间在几毫秒到几十毫秒之间。但数据量到了几十万条以后,必须建索引,否则查询会慢到不可接受。pgvector支持两种索引:
sql复制-- HNSW索引:适合查询性能优先、数据频繁增删的场景
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
-- IVFFlat索引:适合数据量大、一次性全量导入后再查询的场景
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
HNSW的m和ef_construction参数可以直接在索引定义里指定,比如WITH (m=16, ef_construction=64),默认值对大多数场景已经够用。IVFFlat的lists需要根据数据量调试,常见经验是取数据行数的千分之一,数据量不大时100左右即可。需要特别注意的是,IVFFlat索引如果建在数据还没导入完的表上,查询效果会非常差,推荐“先导入数据再建索引”;HNSW则没有这个限制。理解索引背后是“图搜索”还是“聚类扫描”这两种完全不同的机制,有助于你做出正确选择。
5. 常见问题与排查技巧实录
5.1 CREATE EXTENSION vector报警告或错误
这类问题几乎都是文件没放对位置,或者版本不匹配。常见的报错和解决办法整理如下:
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
could not open extension control file "vector.control" |
vector.control没复制到share\extension目录 | 检查文件路径,重新复制一次 |
could not load library ".../lib/vector.dll" |
DLL缺失或版本不匹配 | 确认DLL在lib目录且和PostgreSQL大版本匹配,安装VC++运行库 |
extension "vector" has no installation script |
SQL脚本缺失 | 把vector--*.sql全部复制过去 |
different vector dimensions |
插入的向量维度与建表不一致 | 检查embedding模型输出维度,统一维度再插入 |
我在3.1节里提过第三方DLL要慎用,最典型的问题就是版本不匹配导致could not load library。遇到这类报错,优先确认三件事:DLL是不是64位、是不是对应PostgreSQL大版本、系统有没有装Microsoft Visual C++ Redistributable。
5.2 编译阶段常见报错
编译pgvector时我踩过的坑主要有三个:
'nmake' 不是内部或外部命令:没有打开VS的开发者命令行,或者没执行vcvars64.bat;pg_config executable not found:没有设置PG_CONFIG环境变量,或者路径里的引号不对;cannot open include file 'postgres.h': No such file or directory:PostgreSQL源码路径不对,VS找不到头文件。
对第三个问题,可以在nmake之前手动指定头文件路径:
bat复制set PG_INCLUDE_DIR=C:\path\to\postgresql-src\src\include
set PG_LIB_DIR=C:\path\to\postgresql-src\src\interfaces\libpq
如果用的是pgvector 0.7.x之前的版本,源码里可能没有Makefile.vc,那就要先升级到新版本,或者切到0.8.x分支再编译。版本太老的话,别浪费时间在旧代码上。
5.3 查询性能不理想
有时候建了索引,查询还是慢,多半是索引没被用上。用EXPLAIN ANALYZE查看执行计划,如果看到Seq Scan而不是Index Scan,就说明索引没生效。这里有个特别容易被忽略的点:HNSW和IVFFlat的索引运算符类别必须和查询用的距离操作符匹配。比如查询里用的是<=>余弦距离,索引就必须建vector_cosine_ops;如果查询用<->欧氏距离,索引要建vector_l2_ops,否则PostgreSQL根本不会走索引。
还有一个小经验:LIMIT的值不要取太大,默认取10到50之间就够了,向量索引的构建成本和查询精度之间需要平衡。如果你需要更高的召回率,可以考虑牺牲一点性能把ef_search调大,在pgvector里可以通过SET hnsw.ef_search = 100;来做session级别的控制。
5.4 与AI应用配合时的几条建议
既然标题里带了“向量存储”,那说明你多半是在做AI相关的应用。我强烈建议把向量数据的写入和检索封装成独立接口,不要散落在业务代码里。因为embedding模型升级、向量维度变化、索引参数调优这些事迟早会发生,独立封装能让你只改一处,而不是全局找替换。
开发阶段想快速验证流程,也可以用Docker跑一个带pgvector的PostgreSQL容器,几条命令就能起来,适合做环境隔离和快速重置。本机原生安装则更适合长期稳定运行的场景,Windows服务管理、开机自启、备份恢复都会更顺。这两种方式不是非此即彼的关系,我通常是开发用Docker,正式跑用本机原生服务。
最后说几个小经验
这个流程完整跑通一次之后,我最大的感受是:Windows下编译PostgreSQL扩展,难的不是编译本身,而是环境匹配。DLL的位数、PostgreSQL小版本、VS工具链版本、PG_CONFIG指向,任何一个环节错位都会给你颜色看。所以第一次动手别嫌麻烦,按文章里的顺序一步步来,把每个验证命令都执行一遍。
再分享一个实用技巧:编译成功后,把vector.dll、vector.control、vector--*.sql这三个文件单独打包留存。下次换机器或者帮同事部署,只要PostgreSQL版本一致,直接复制到对应目录就能用,省去重新编译的一整轮折腾。如果以后想在Windows上继续装其他PostgreSQL扩展,这套“准备工具链、设置PG_CONFIG、nmake编译、复制产物”的流程完全可以复用,一次折腾,长期受益。
