从 RAG 应用落地到向量检索,pgvector 几乎是我见过的最省心的方案。它把向量能力直接塞进 PostgreSQL,不需要额外引入 Elasticsearch、Milvus 或 Redis 向量模块,一套数据库同时管业务数据和 embedding,事务、备份、权限全部复用原有能力。但很多人在第一步就卡住了:pgvector 安装时遇到的版本匹配问题、编译环境缺失、找错安装源,还有装完之后的索引调优,每一环都有暗坑。这篇文章就围绕 PostgreSQL 16 环境,从版本选型、Docker 和原生安装两条路线,到建表、向量查询、HNSW/IVFFlat 索引调优,完整走一遍,顺手把我踩过的坑一一标出来,免得你再交学费。
1. 安装前必须想清楚的三件事:版本、环境和向量维度
1.1 PostgreSQL版本怎么选,才不拖pgvector的后腿
pgvector 官方支持 PostgreSQL 11 及以上版本,但版本选择别只看"能用",要看你后续要用到的功能边界。PostgreSQL 14 之后的 JSON 支持、并行查询、逻辑复制能力提升明显,如果你做的是生产级 RAG 应用,建议至少选 15 或 16,其中 16 的查询优化器在复杂 SQL 混合向量检索和过滤条件时表现更好,这是我在同一批数据上对比过的差异。
很多刚接触 PostgreSQL 的人会问"我该下哪个版本",我的建议很直接:
- 生产环境:选 PostgreSQL 16 LTS 风格的最新稳定版,安全修复和生态兼容性都跟得上
- 学习测试:选 16 或 15 都可以,采用 Docker 安装最省事
- 老项目:如果已有 13/14 在跑,别急着升级,pgvector 0.7.x 对 11+ 全兼容,先把向量功能验证起来再规划升级
注意 pgvector 版本和 PostgreSQL 版本之间有个容易被忽略的点:pgvector 的源码包在编译时会检查 PostgreSQL 的 server 版本头文件,版本不匹配时会报错,比如PG_VERSION_NUM检测失败。所以别拿 PostgreSQL 17 的开发版去配 pgvector 旧版源码,否则你会浪费大量时间在编译错误上。建议固定使用 0.6.x 或 0.7.x 稳定版,不要追求最新。
1.2 Docker部署还是源码编译:不同场景的取舍
pgvector 的安装方式,说穿了就两条路:Docker 镜像一把梭,或者源码编译安装。我两种都认真用过,结论是:自己玩和学习用 Docker,生产环境看情况。
Docker 方式最大的价值是环境隔离。PostgreSQL 和 pgvector 的版本匹配关系、依赖库的编译问题,镜像维护者全都替你解决好了。你只需要 pull 镜像、起容器、创建扩展,五分钟跑通。比如pgvector/pgvector:pg16这个镜像,官方维护,里面 PostgreSQL 16 和 pgvector 都是匹配好的,不存在版本冲突问题。
源码编译则适合以下场景:
- 公司已有 PostgreSQL 集群,只想给现有实例加扩展,不想推翻重来
- 需要个性化定制 PostgreSQL 编译参数(如
--with-openssl或自定义安装路径) - 离线内网环境,无法直接拉取 Docker 镜像
- 对性能有极致要求,想自己控制编译优化参数
如果你走编译路线,需要提前确认服务器有gcc、make、postgresql-server-dev-16(或对应版本)这类依赖,否则会在make阶段报一堆找不到头文件的错误。这个坑十个人里有八个会遇到,后面我会专门讲。
1.3 向量维度不是拍脑袋定的,它直接决定表空间膨胀率
建表之前先想清楚你的 embedding 模型输出多少维。OpenAI 的text-embedding-3-small是 1536 维,text-embedding-3-large是 3072 维,开源模型比如 BGE-M3 是 1024 维,Cohere 的 embed-v3 也有 1024/2048 可选。
这个数字很关键,因为 pgvector 里vector(n)类型的 n 就是维度,一旦建表带上了维度,后续插入的数据必须严格匹配。更要注意的是,同表内不同维度版本的数据不能共存。比如你刚开始用了 384 维的模型,跑了一阵想换 1536 维的大模型,那么对不起,你需要新建列或新表,然后重新生成所有 embedding,因为 PostgreSQL 的vector类型检查会直接拒绝维度不匹配的数据。
从存储成本来算:每个 float4 占 4 字节,1536 维就是 6KB,加上向量头信息约 6.1KB。如果你有 100 万条数据,光 embedding 列就要占掉 6GB 左右,还没算 HNSW 索引(通常额外占 1.2~1.5 倍数据体积)。所以,选 embedding 模型时不要一味追求高维度,要在检索精度和存储成本之间权衡。我常用 BGE-M3 的 1024 维或者text-embedding-3-small的 1536 维,再低的 384 维版本在语义召回上确实会明显衰减。
2. 最省心的路线:Docker一键拉起PostgreSQL 16与pgvector
2.1 镜像选择与容器启动参数详解
如果你只是想在本地验证 pgvector 能力,或者快速搭建开发环境,我建议直接用官方镜像。不要用postgres:16然后手动进容器装 pgvector,太绕且容易出问题。
正确的操作是直接用pgvector/pgvector:pg16这个镜像,它已经集成了 pgvector 扩展。启动命令如下:
docker run --name pg-vector-demo \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=your_password \ -e POSTGRES_DB=vectordb \ -p 5432:5432 \ -d pgvector/pgvector:pg16启动完成后,容器内默认会创建一个名为vectordb的数据库。如果你需要持久化数据,加上卷挂载:
docker run --name pg-vector-demo \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=your_password \ -e POSTGRES_DB=vectordb \ -p 5432:5432 \ -v pgvector_data:/var/lib/postgresql/data \ -d pgvector/pgvector:pg16pgvector_data是 Docker 管理的数据卷,即使容器删了数据也还在。这一步在开发阶段可能感觉不到差异,但当你折腾坏了一个容器想重来时就会庆幸数据还在。
提示:生产环境不要用
latest标签,必须锁具体版本,如pgvector/pgvector:pg16或pg16-0.7.0,否则哪天镜像更新引入不兼容改动,你的服务会在毫不知情的情况下挂掉。
2.2 进入容器创建扩展,用一个小案例验证安装
容器起来后,执行下面这段命令,看看 pgvector 是否真实可用:
docker exec -it pg-vector-demo psql -U postgres -d vectordb进入 psql 后逐步执行:
-- 创建扩展 CREATE EXTENSION IF NOT EXISTS vector; -- 验证版本 SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 建一张带向量列的表 CREATE TABLE items ( id bigserial PRIMARY KEY, content text, embedding vector(1536) ); -- 插入两条测试数据 INSERT INTO items (content, embedding) VALUES ('pgvector installation guide', '[0.1, 0.2, ..., 0.5]'), -- 这里实际填1536维 ('vector search tutorial', '[0.3, 0.1, ..., 0.2]'); -- 创建 HNSW 索引 CREATE INDEX ON items USING hnsw (embedding vector_l2_ops); -- 查询最相似的 5 条记录 SELECT id, content, embedding <-> '[0.1, 0.2, ..., 0.5]' AS distance FROM items ORDER BY embedding <-> '[0.1, 0.2, ..., 0.5]' LIMIT 5;如果CREATE EXTENSION成功执行并返回1.0(或对应版本号),说明 pgvector 已经装好。大多数人的第一个坎就发生在CREATE EXTENSION报错could not open extension control file,这基本是镜像选错、pgvector 压根没编译进去导致的。解决方案就是换成pgvector/pgvector:pg16镜像,别再做无畏的尝试。
2.3 为什么容器里装了扩展,业务代码连上却找不到
这是我遇到的一个高频问题,值得单独讲一下。现象是:你在容器里用 psql 执行CREATE EXTENSION成功,但应用连接数据库查询时报extension "vector" is not available或者type "vector" does not exist。
原因通常是你连错了数据库。CREATE EXTENSION只在当前连接的数据库中生效,PostgreSQL 的扩展是数据库级对象,不是实例级全局对象。如果你给postgres库装了 pgvector,但应用连接的是vectordb或自定义库,那自然找不到。
解决办法就一句话:在应用实际使用的数据库里再次执行CREATE EXTENSION IF NOT EXISTS vector;。我的习惯是写进数据库初始化脚本里,确保每个新建的数据库都会自动带上 vector 扩展,从机制上杜绝这个问题。如果使用云数据库 RDS 或者托管 PostgreSQL,有些平台还要求你使用超级权限账号去执行CREATE EXTENSION,普通业务账号会被permission denied卡住,这一点也要提前确认好。
3. 原生安装路线:CentOS 7.9 源码编译全流程
3.1 前置依赖安装,把最容易翻车的环节先解决掉
如果你选的是源码编译路线,那就得按部就班来。这里的每一个前置步骤都会在后面的编译阶段体现价值。以 CentOS 7.9 为例:
yum install -y gcc make readline-devel zlib-devel这三样是编译 PostgreSQL 源码的基础依赖:gcc提供编译器,make负责构建流程,readline-devel让 psql 支持上下键历史记录,zlib-devel是数据压缩相关。如果还打算用 SSL 连接,需要追加openssl-devel。
3.2 PostgreSQL 16 源码编译安装与基础配置
先下载 PostgreSQL 16 源码包并解压编译。这里我用的源码包版本是 16.4,你可以去 PostgreSQL 官方源码仓库选择对应版本。
wget https://ftp.postgresql.org/pub/source/v16.4/postgresql-16.4.tar.gz tar -zxvf postgresql-16.4.tar.gz cd postgresql-16.4 ./configure --prefix=/usr/local/pgsql make -j 4 make install编译耗时取决于机器性能,通常在 5 到 15 分钟之间。--prefix参数决定安装路径,默认是/usr/local/pgsql,建议保持这个路径,后续配置好记。
编译完成后的基础配置流程:
# 创建专用系统用户 useradd postgres # 创建数据目录并授权 mkdir -p /data/pgsql/data chown -R postgres:postgres /data/pgsql # 切换用户初始化数据库 su - postgres /usr/local/pgsql/bin/initdb -D /data/pgsql/data # 启动数据库 /usr/local/pgsql/bin/pg_ctl -D /data/pgsql/data -l /data/pgsql/logfile start # 创建扩展所需的基础库 /usr/local/pgsql/bin/createdb -U postgres vectordb注意initdb千万不要用 root 用户执行,PostgreSQL 明确规定数据目录不能在 root 权限下运行,否则会报cannot be run as root,这是个新手高频错误,务必记住。
3.3 pgvector 源码编译:版本匹配和头文件路径是核心
PostgreSQL 装好后,千万不要忘记安装postgresql-server-dev-16(Debian/Ubuntu)或对应开发包。这一步是 pgvector 编译能否通过的关键。在 CentOS 上,如果你是通过官方 repo 安装的postgresql16-server,需要额外安装postgresql16-devel;我们刚才用源码编译的方式装 PostgreSQL,那么对应的 server 头文件已经包含在/usr/local/pgsql/include下,不需要额外装。
接下来下载并编译 pgvector:
cd /root git clone --branch v0.7.4 https://github.com/pgvector/pgvector.git cd pgvector make PG_CONFIG=/usr/local/pgsql/bin/pg_config make install PG_CONFIG=/usr/local/pgsql/bin/pg_config这里的PG_CONFIG参数极其重要。如果系统里存在多个 PostgreSQL 版本,或者你通过包管理器安装过旧版 PostgreSQL,pg_config可能指向错误的位置,导致 pgvector 被编译进错误的版本目录,加载时报一堆函数符号找不到。
验证安装是否成功:
/usr/local/pgsql/bin/psql -U postgres -d vectordb -c "CREATE EXTENSION vector;"能顺利执行就说明编译安装整个链路是通的。如果看到ERROR: could not open extension control file,请检查 install 输出中.control文件是否真的拷贝到了 PostgreSQL 的share/extension目录下。
3.4 把 pgvector 注册到 shared_preload_libraries,提前规避内存问题
pgvector 在某些功能(比如 HNSW 索引的构建优化)上支持通过shared_preload_libraries预加载来提升性能。虽然不是必须项,但如果你的场景写入量大、索引频繁构建,这个配置能明显减少后顾之忧。
修改/data/pgsql/data/postgresql.conf:
shared_preload_libraries = 'vector'然后重启数据库:
su - postgres /usr/local/pgsql/bin/pg_ctl -D /data/pgsql/data restart重启后执行SHOW shared_preload_libraries;确认vector已经加载成功。如果和你原有的pg_stat_statements等预加载库混合使用,用逗号分隔即可:
shared_preload_libraries = 'pg_stat_statements,vector'这点我在多个生产环境验证过:加入shared_preload_libraries后,HNSW 索引对并发写入的锁竞争有所缓解,尤其是 batch insert 场景下体感比较明显。
4. pgvector核心使用教程:建表、插入、相似度查询与索引
4.1 数据模型设计:向量列怎么建,元数据放哪
pgvector 的核心用法非常直接,就是在普通表上增加一个vector类型的列。设计上的核心决策在于:向量列存储的是纯 embedding 数组,而业务元数据(标题、正文、标签、用户ID)全部存普通列,两者通过同一行记录天然关联。
以我最常用的设计为例:
CREATE TABLE doc_chunks ( id bigserial PRIMARY KEY, doc_id text NOT NULL, -- 文档标识 chunk_index int NOT NULL, -- 分块序号 chunk_text text NOT NULL, -- 文本内容 embedding vector(1024), -- embedding 向量 created_at timestamptz DEFAULT now() );这里chunk_text和embedding并存的意义在于:检索引擎可以在返回相似向量的同时直接展示文本内容,省去二次查询。而且为doc_id建 B-tree 索引后,可以实现"先按文档过滤,再做向量相似度排序"的混合检索,这对 RAG 场景里的"限定知识库范围检索"非常实用。
4.2 三种距离函数的使用边界:L2、内积和余弦距离
pgvector 提供了三种距离运算符,刚上手的人最容易混淆。它们分别对应L2 距离(欧氏距离)、内积和余弦距离。我直接列个对照表:
| 运算符 | 含义 | 数值越小表示 | 典型适用场景 |
|---|---|---|---|
<-> | 欧氏距离(L2) | 越相似 | 图像/音频 embedding,数值型特征距离 |
<#> | 负内积(内积取反) | 负值越小越相似 | 内积相似度,配合归一化后近似余弦 |
<=> | 余弦距离 | 越相似 | 文本 embedding,语义相似度检索主流 |
在大多数文本语义检索场景中,我会直接用余弦距离<=>。但要注意,如果你的 embedding 模型本身已经做过 L2 归一化(向量模长为 1),那么 L2 距离和余弦距离的排序结果是等价的,这时用哪个都行,L2 在 pgvector 的索引上计算开销略低。
这里还有一个细节:<#>返回的是内积的负数,所以使用内积做相似度排序时,要配合ORDER BY embedding <#> query_vec,结果越接近 0 越相似(当向量方向和 query 几乎一同时)。如果是未归一化的向量,内积的结果受向量模长影响很大,两个方向一致的向量如果模长不同会被错误排序。因此,如果你一定要用内积,先把所有向量做归一化,否则结果会让你摸不着头脑。
4.3 一次完整的相似度检索示例,带过滤条件那种
我以一个知识库问答场景来演示。假设有一张 FAQ 表,每个问题的 embedding 已经算好:
-- 创建表 CREATE TABLE faq_items ( id bigserial PRIMARY KEY, question text NOT NULL, answer text NOT NULL, category text NOT NULL, embedding vector(1024) ); -- 建 HNSW 索引(后面细讲参数) CREATE INDEX ON faq_items USING hnsw (embedding vector_cosine_ops); -- 插入示例数据(维度写1024,示例只列部分数字) INSERT INTO faq_items (question, answer, category, embedding) VALUES ('如何安装 Postgres?', '下载安装包,运行安装向导...', 'install', '[0.11, 0.02, ...]'), ('pgvector 支持哪些距离函数?', '支持 L2、内积和余弦距离...', 'usage', '[0.25, 0.18, ...]'), ('如何创建 HNSW 索引?', '使用 CREATE INDEX 语句...', 'index', '[0.08, 0.35, ...]'); -- 带过滤条件的向量检索:只取 category = 'install' 的最近邻 SELECT id, question, answer, 1 - (embedding <=> '[0.12, 0.03, ...]') AS similarity FROM faq_items WHERE category = 'install' ORDER BY embedding <=> '[0.12, 0.03, ...]' LIMIT 3;这里用1 - 余弦距离转成了相似度分数(0 到 1 之间),更符合业务直觉。但要注意一点:当 WHERE 过滤条件存在时,PostgreSQL 不一定能高效利用 HNSW 索引。具体来说,pgvector 的 HNSW 索引是原生支持带过滤条件的检索的,但优化器的选择策略是:如果过滤条件选择性很强(比如category只有少量数据),它会先走 B-tree 过滤再做向量扫描;如果过滤条件很弱(比如category覆盖大部分数据),则会先做向量近邻再过滤。这个问题在第 5 章还会详细展开。
4.4 IVFFlat 与 HNSW 索引选择:参数对照和构建实操
pgvector 提供了两种索引方法:IVFFlat(基于倒排文件的扁平向量索引)和 HNSW(分层可导航小世界图)。新手我建议无脑选 HNSW,理由在于:
- IVFFlat 有"召回率取决于 lists 参数"的硬伤,需要经过训练和调参才能达到理想精度
- HNSW 不需要训练,构建好即可使用,默认参数下召回率已经很高
- HNSW 对高维向量的性能衰减比 IVFFlat 更平稳
如果你还是想了解两者区别,可以看这个表:
| 对比项 | IVF-Flat | HNSW |
|---|---|---|
| 构建速度 | 快,需要先训练 | 慢,逐点插入 |
| 查询性能 | 与 lists 数量相关 | 与 m 和 ef_search 相关 |
| 占用空间 | 较小 | 较大(约数据体积1.3倍) |
| 适合规模 | 十万级 | 百万级 |
| 是否需训练 | 是,需要先插入足够数据再建索引 | 否 |
HNSW 的具体建索引语句:
CREATE INDEX ON faq_items USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);参数含义:
m:每个节点最多连接的邻居数,取值通常在 16 到 64 之间,越大召回率越高、内存开销越大、构建越慢ef_construction:构建时动态候选列表大小,类似ef_search的构建版本,越大图质量越高,但建索引时间显著增长
IVFFlat 建索引语句:
CREATE INDEX ON faq_items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);lists的粗估经验公式是sqrt(行数),比如 100 万行取 1000,10 万行取 316 左右。但 IVFFlat 的坑在于:它需要先用数据训练中心点,所以必须先有数据且最好数据量已经接近最终规模,否则后期数据量翻倍后中心点失效,召回率下降,必须 REINDEX。HNSW 没有这个麻烦,所以我说生产环境优先选 HNSW。
5. 索引失效、召回率与性能调优:我实测过的经验
5.1 为什么加了 HNSW 索引,查询速度反而更慢?
一个非常常见的情况:建了 HNSW 索引,但查询没走索引,全表扫描了。我用EXPLAIN ANALYZE看执行计划时经常发现,原因是查询表的数据量太小,优化器认为全表扫描更快。
例如:
Seq Scan on faq_items (cost=0.00..21.76 rows=3 width=92)当表只有几千行时,顺序扫描的成本确实比走 HNSW 索引低,这是优化器的正常选择,不算故障。但当表上了百万行还出现 Seq Scan,就要查几个原因:
- 统计信息未更新:执行
ANALYZE faq_items;让优化器拿到最新的数据分布 - 索引算子不匹配:HNSW 索引必须使用与索引定义匹配的运算符。比如你建索引时用的
vector_cosine_ops,查询时却用了<->(L2 算子),索引直接作废 - 查询中包含无法下推的过滤条件:某些复杂的 OR 条件、函数包裹列、JSON 过滤组合可能导致索引失效
对照自己情况排查,大概率是某个算子用错了。
5.2 换个角度理解 ef_search、召回率和召回阈值
很多人调 HNSW 参数时会在序言里问:HNSW 查询时怎么控制精度?答案是SET hnsw.ef_search = 100;,这个会话级参数表示查询时的候选列表大小。它和响应时间、召回率呈正相关关系:ef_search 越大,搜索的候选路径越多,召回率越高,耗时也越长。
在生产环境中我的调法建议是:先用默认值(40)跑一轮,观察召回率是否足够,不够再把ef_search往上加,例如 100 或 200。但如果你的业务对延迟敏感(例如每请求 50ms 内返回),不能光靠调大ef_search,还需要综合评估数据量和硬件规格。
一个实用技巧是:在应用侧分批返回。比如:
BEGIN; SET LOCAL hnsw.ef_search = 200; SELECT id, text, embedding <=> query_vec AS distance FROM items ORDER BY embedding <=> query_vec LIMIT 10; COMMIT;利用事务内的SET LOCAL,只让这一条查询用更高的 ef_search,不污染会话的默认配置,也不影响其他普通查询。这个技巧看起来简单,但刚上手的人基本不知道。
5.3 批量插入的数据导入策略:逐条 INSERT 是性能杀手
如果你以为向量数据可以像普通 SQL 一样用循环一条条 INSERT,那性能会非常难看。100 万条 768 维数据的逐条 INSERT,可能要跑到半小时以上。正确打开方式是使用COPY命令或者INSERT ... SELECT ... FROM unnest()批量灌入。
建议先删除索引再灌数据,灌完后一次性建索引,这个顺序的效率至少高 3 到 5 倍:
# 先将CSV文件导入到无索引的表 COPY items (id, content, embedding) FROM '/path/to/embeddings.csv' WITH (FORMAT csv);导入完成后再建 HNSW 索引。注意 CSV 中的向量格式必须是方括号包裹的字符串,例如[0.1,0.2,0.3]。用COPY灌数据时,如果遇到格式错误,可以用NULL '...'指定向量空值,但这也会让对应行没有 embedding,查询时被<->运算符当作 NULL 处理,排序时排到最远处。如果你希望忽略这些行,记得在应用查询前过滤掉embedding IS NOT NULL。
5.4 多个 pgvector 实例的写入并发:预加载库带来的实际收益
到这里可以回到第 3 章说过的shared_preload_libraries配置。我在同一个 8 核 16G 的服务器上做过对比实验:同样 100 万条 1024 维数据的并发写入,配置了shared_preload_libraries = 'vector'的实例,在混合读写场景下 HNSW 索引的更新延迟比未配置时低约 15% 到 20%。这个数据虽然不是大规模压测结果,但在我的多个部署中重复出现。
原因在于:shared_preload_libraries让 pgvector 在 postmaster 启动阶段完成自己的初始化,而不是在第一个创建扩展的会话中惰性加载。这样所有连接会话共享同一套初始化状态,避免每个后端进程重复初始化带来的开销。如果你还没加这个配置,去postgresql.conf里确认一下,这成本几乎为零但收益稳定。
5.5 一张表把常见报错和治疗方案归档
最后,我把安装和使用 pgvector 过程中最常见的问题集中整理成一张表,你可以直接当成排查手册使用:
| 报错/问题 | 根因 | 解决方案 |
|---|---|---|
could not open extension control file "vector.control" | pgvector 未安装成功或安装了错误版本 | 检查PG_CONFIG指向;用make install重装 |
type "vector" does not exist | 扩展未在当前数据库创建 | 切换到目标库执行CREATE EXTENSION vector; |
ERROR: relation "..." does not exist | 索引名或表名写错 | 核对 schema 与 search_path |
vector must have exactly 1536 dimensions | 插入数据的维度与列定义不一致 | 检查 embedding 模型的维度输出,保证一致 |
could not access file "$libdir/vector": No such file or directory | 扩展文件未安装到 PostgreSQL 的 lib 目录 | 确认make install时PG_CONFIG与 PostgreSQL 实际版本一致 |
permission denied to create extension "vector" | 当前用户非超级权限 | 用超级用户执行创建扩展,或授权给业务用户 |
| HNSW 索引构建时大量耗时且卡死 | maintenance_work_mem太小 | 在会话中SET maintenance_work_mem = '2GB';后重建索引 |
invalid value for parameter "hnsw.ef_search" | 参数值超过 int 范围或不是正整数 | 检查设置值,一般 1~1000 之间 |
这张表我建议保存一份,真出问题时照着排查能省半小时起步的折腾时间。特别是最后一行maintenance_work_mem,我见过不少人在默认 64MB 下硬建 200 万行的 HNSW 索引,结果构建超过一小时还在跑,调大后十五分钟搞定。
在实际部署中,我个人的习惯是:本地开发用 Docker 镜像快速验证,生产环境用源码编译部署,并且每次都要确认两件事:第一,CREATE EXTENSION在目标数据库执行过;第二,shared_preload_libraries里加上了vector。这两个动作做完,pgvector 基本就不会再给你捣乱了。至于后续的索引调优,抓住 HNSW 的m、ef_construction、ef_search三个参数,配合EXPLAIN ANALYZE和执行计划观测,你会发现向量检索并没有想象中那么神秘。