
10 月 1 日,pgvector 发布 0.8.7。打开 CHANGELOG,只有两行,看起来就是个例行小版本。
但如果只读 CHANGELOG,你会漏掉两件事:这一版里藏着一个 CVSS 8.8 的高危漏洞(CVE-2026-103484),还有一条没写进 CHANGELOG、但对写入密集场景影响极大的 HNSW 竞态修复。10 月 5 日,PostgreSQL 官网专门为此发了新闻。
这不是一次可以排期的升级,是应该立即执行的补丁。
国庆期间我在一台 CentOS 7.9 上做了次完整实验:从源码编译最新的 IvorySQL(master 分支,PG 19beta1 内核),编译安装 pgvector 0.8.7,然后在 IvorySQL 里把这一版的每一处修复逐项验证了一遍。
这篇文章就是实验全记录。前半部分来看 0.8.7 的三处修复,后半部分是基于 IvorySQL 的具体实操过程,建议收藏。
01. 先说 CVE:两行 CHANGELOG 里藏着的 8.8 分
pgvector 0.8.7 于 2026 年 10 月 1 日发布(GitHub Release),PostgreSQL 官网 10 月 5 日跟进发布新闻。支持 PostgreSQL 13 及以上(0.8.x 系列从 0.8.0 起放弃了 PG 12)。索引磁盘格式没有变更,升级后不需要重建索引。
版本概览一张表先放这儿:
| 项目 | 说明 |
|---|---|
| 发布时间 | 2026-10-01(GitHub),2026-10-05(PostgreSQL 官网新闻) |
| 版本性质 | 安全修复版本:1 个高危 CVE + 2 项功能性修复 |
| 索引格式 | 无变更,升级后无需重建索引 |
| 升级建议 | 尽快,涉及可致任意代码执行的漏洞 |
这一版的三处修复,先给个全景:
| 修复 | 级别 | 是否写入 CHANGELOG | 一句话概括 |
|---|---|---|---|
| IVFFlat 构建缓冲区溢出(CVE-2026-103484) | CVSS 8.8 高危 | 已列出 | 有建索引权限即可越界写,可能导致任意代码执行 |
| avg 聚合空结果集报错(PR #1012) | 正确性 | 已列出 | 无匹配行时误报「vector must have at least 1 dimension」 |
| HNSW INSERT/VACUUM 竞态(PR #1010) | 并发、索引损坏 | 未列出 | 并发写入产生重复邻居,召回率悄悄劣化 |
重点说 CVE-2026-103484。NVD 已收录,PostgreSQL 作为 CNA 给出的 CVSS 3.1 评分 8.8(HIGH),攻击向量是 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H。翻译成白话:网络可达、利用难度低、普通数据库用户就够、无需交互,一旦利用成功,机密性、完整性、可用性全部打穿。关联 CWE-787(越界写)和 CWE-1284(输入数量校验不当),影响 0.8.6 及以下所有版本。
漏洞的本质不复杂:输入向量的维数和元素数量这类运行时数据,在 C 扩展层没有做严格校验。具备 CREATE INDEX 权限的用户构造一个特殊的 IVFFlat 索引构建请求,就能触发越界写。关键在于,pgvector 是跑在 PostgreSQL 后端进程内的 C 扩展,越界写一旦成功,攻击面直接覆盖数据库服务器进程本身。修复提交是 ee00d39ab30c2a7cf9510abf9288c624d91db156(v0.8.7 tag 的直接父提交),由 Compass Security 的 Emanuele Barbeno、Cyrill Bannwart、Urs Mueller 与 Lukasz D 报告。
更让人警惕的是趋势。把时间线拉出来看看 0.8.x 系列最近的节奏:
| 版本 | 日期 | 内容 |
|---|---|---|
| 0.8.2 | 2026-02-25 | 修复并行 HNSW 索引构建缓冲区溢出(CVE-2026-3172) |
| 0.8.6 | 2026-07-29 | IVFFlat 内存与转换修复 |
| 0.8.7 | 2026-10-01 | 修复 IVFFlat 索引构建缓冲区溢出(CVE-2026-103484) |
八个月内,两起索引构建类的内存安全问题,一起 HNSW、一起 IVFFlat。我的结论:pgvector 的版本号必须纳入日常补丁管理流程,不能再当「装一次管一年」的静态组件。

02. 另外两条修复:一条让你报错,一条让你"慢性中毒"
avg 空集报错(PR #1012)
现象很好复现:对向量列做 avg() 聚合,查询没有匹配行时(WHERE 过滤后为空,或者并行聚合的某个 worker 分片为空),会抛 vector must have at least 1 dimension,而不是按 SQL 语义返回 NULL。
根因在并行聚合的实现。PostgreSQL 的并行聚合用 INITCOND = '{0}' 当初始工作状态,表或扫描分片没行时,初始状态就是一个 0 维浮点数组。多个后台 worker 各自产生空分片后,vector_combine 合并函数会对这个初始化状态执行 CheckDim(0) 维数检查,于是误报。修复方式是修改 vector_combine_impl,仅当两个传入的并行状态都完全为空时条件性跳过维数检查,其余路径的校验原样保留,不会放松对畸形输入的防护。PR 还附带了一个强制并行执行限制的 TAP 测试用例,贡献者是 Vaibhav Jain。
对业务的影响很实际:如果你的报表里有 SELECT avg(embedding) FROM ... WHERE tag = ... 这类可能查空的查询,0.8.7 之前会间歇性失败。它不常出现,但一旦出现就是线上事故。
HNSW 竞态(PR #1010,CHANGELOG 漏列)
这条要单独敲黑板:它没有写进官方 CHANGELOG 的 0.8.7 条目,但经 Git 历史确认确实包含在本版本中(提交 3b72e5b 于 2026 年 8 月 4 日合并进 master,早于 v0.8.7 的 tag,且是 v0.8.7 的祖先提交)。只读 CHANGELOG 的运维同学会漏掉它,而对高并发写入场景,它的影响比 CVE 更隐蔽。
破坏机制值得细看一下,三步:
INSERT写入新元素的第一条桥接边(i=0)时执行盲写,不检查连接是否已存在,新元素由此在图遍历中变为全局可达;- 并发的
VACUUMPhase 2(RepairGraph)在修复相邻节点时发现了这个新元素,检查过连接后把它写进邻接表; INSERT恢复后更新其余候选连接(i=1),再次盲写,把同一元素第二次写进同一个邻接数组。
结果:重复邻居。浪费固定节点出度容量(m 或 2m)、阻塞有效导航路径、显著拉低 ANN 检索召回率。查询执行器虽然会用 visited 哈希集合对最终结果去重,但索引结构本身的物理损伤无法自愈。
修复方式是移除 checkExisting 标志,让 UpdateNeighborOnDisk 无条件执行 ConnectionExists 检查,与 VACUUM 的行为对齐,从算法上消除这个不对称竞态。性能开销可以忽略:目标邻居页在更新前已经被加载并加锁,不产生额外磁盘 I/O,唯一成本是有界邻接数组的 CPU 扫描(数组大小受 m 限制,通常 16 到 100)。贡献者是 Google 的 upenderbh。
如果你维护的是「高频写入 + 自动 VACUUM 并存」的 HNSW 索引(日志向量、增量样本库都是典型场景),0.8.7 之前存在索引被慢慢破坏、召回率数周内悄然劣化的隐患。注意,这个修复救不了已经损坏的索引。升级之后建议用 pageinspect 抽查邻居数组,或者干脆重建一次,消除隐患。
也顺手分享一个工作习惯:核对安全更新别只读 CHANGELOG,把 compare/v0.8.6...v0.8.7 的提交清单翻一遍才完整。这次漏列如果不是去对 Git 历史,估计不少团队升完级还以为只有两条修复。
03. 实验环境:从源码编译 IvorySQL
说回实验本身。选 IvorySQL 是因为它回答了一个很多 Oracle 存量客户在问的问题:迁到 PG 系之后,AI 业务要的向量能力怎么办?IvorySQL 的思路是双解析器(PG Parser + Oracle Parser)、双端口(5432 + 1521)、PL/iSQL 存储过程,Oracle 迁移平滑落地;同时又生自 PG 生态,pgvector 这类明星扩展理论上可以原封不动装进来。理论如此,但实测才知道。
实验环境:CentOS 7.9,gcc 4.8.5,8 核 / 16 GB 内存 / 50 GB 磁盘。拉取的 master 快照信息如下:
| 项目 | 值 |
|---|---|
| commit | 63fb0bfe6159fb91b03012eea09f3db7cd46db8c(2026-09-18) |
| PG 内核 | 19beta1 |
| IvorySQL 版本 | 5beta1 |
第一步,装编译依赖:
yum install -y readline-devel zlib-devel flex bison \ libicu-devel openssl-devel perl-ExtUtils-Embed libuuid-devel
两个坑提前说。CentOS 7 自带 OpenSSL 1.0.2,不满足 PG 新内核要求的 1.1.1 及以上,configure 时要加 --without-openssl(或者自己升级 OpenSSL)。另外 --with-uuid=e2fs 依赖 libuuid-devel,漏装会在 configure 阶段报 library 'uuid' is required。
第二步,拉源码。 GitHub 直连被重置的话,Gitee 镜像可用:
git clone --depth 1 https://gitee.com/IvorySQL/IvorySQL.git src
第三步,配置编译:
cd src
./configure --prefix=/usr/local/ivorysql \
--without-openssl --with-uuid=e2fs --with-oraport=1521
make -j8
make install
make -C contrib install
--with-oraport=1521 是 IvorySQL 特有选项,启用 Oracle 协议端口监听。核心安装用 make && make install 就够了。编译产物确认:postgres (PostgreSQL) 19beta1,psql 19beta1。
第四步,初始化并启动(Oracle 模式):
useradd ivorysql
mkdir -p /data/ivorysql/data /data/ivorysql/logs
chown ivorysql /data/ivorysql/data /data/ivorysql/logs
su ivorysql -c "/usr/local/ivorysql/bin/initdb -m oracle \
-D /data/ivorysql/data --locale=C --encoding=UTF8"
su ivorysql -c "/usr/local/ivorysql/bin/pg_ctl -D /data/ivorysql/data \
-l /data/ivorysql/logs/ivorysql.log -w start"
连接验证,同一实例、双协议端口,这就是 IvorySQL 的招牌:
$ psql -p 5432 -d postgres -c "select version();"
PostgreSQL 19beta1 (IvorySQL 5beta1) on x86_64-pc-linux-gnu ...
$ psql -p 5432 -d postgres -c "show ivorysql.port;"
ivorysql.port
---------------
1521
04. 编译 pgvector 0.8.7:一处内核 API 漂移
网络受限环境下,git clone 可能反复超时,codeload 直链下载 tar 包是最稳的路径:
curl -sL -o pgvector.tar.gz \
https://codeload.github.com/pgvector/pgvector/tar.gz/refs/tags/v0.8.7
tar xzf pgvector.tar.gz && mv pgvector-0.8.7 pgvector
编译前把 PG_CONFIG 指到 IvorySQL 的目录:
cd pgvector
export PG_CONFIG=/usr/local/ivorysql/bin/pg_config
make -j8
第一次编译,报错了:
src/ivfbuild.c:987:3: error: too few arguments to function 'plan_create_index_workers'
src/hnswbuild.c:1076:2: error: too few arguments to function 'plan_create_index_workers'
原因很清楚:IvorySQL 基于 PG 19beta1 内核,这个内核给 plan_create_index_workers() 新增了第三个参数 int override_workers(0 表示自动探测,大于 0 表示显式指定并行 worker 数),而 pgvector 0.8.7 的发布基线还是面向更早内核的调用方式。这是扩展追新内核时的典型接口漂移,不是 bug,属一行级适配。
补丁就是两个文件各加第三个参数,传 0,保持原有的自动探测语义:
// src/ivfbuild.c:987
parallel_workers = plan_create_index_workers(RelationGetRelid(buildstate->heap),
RelationGetRelid(buildstate->index), 0);
// src/hnswbuild.c:1076
parallel_workers = plan_create_index_workers(RelationGetRelid(heap),
RelationGetRelid(index), 0);
重新编译安装,vector.control、vector--0.8.7.sql、vector.so 全部就位。这一步也顺带印证了 0.8.7 的升级建议:索引磁盘格式无变更,升级不需要重建索引,因为改动集中在构建和并发逻辑,而不是存储布局。
05. 逐项验证:在 IvorySQL 上复现每一处修复
创建扩展并确认版本:
CREATE EXTENSION vector;
SELECT extname, extversion FROM pg_extension WHERE extname='vector';
-- vector | 0.8.7
注意看这个实例上同时装着什么:Oracle 兼容扩展 ivorysql_ora、GB18030-2022 字符集支持、plisql(PL/iSQL 过程语言),以及 vector 0.8.7。一套实例,Oracle 语法和向量检索都在上面了。
postgres=# \dx
List of installed extensions
Name | Version | Default version | Schema | Description
--------------+---------+-----------------+------------+------------------------------------------------------
gb18030_2022 | 1.0 | 1.0 | pg_catalog | support gb18030 2022 with extension
ivorysql_ora | 1.0 | 1.0 | sys | Oracle Compatible extension on Postgres Database
plisql | 1.0 | 1.0 | pg_catalog | PL/iSQL procedural language
plpgsql | 1.0 | 1.0 | pg_catalog | PL/pgSQL procedural language
uuid-ossp | 1.1 | 1.1 | sys | generate universally unique identifiers (UUIDs)
vector | 0.8.7 | 0.8.7 | public | vector data type and ivfflat and hnsw access methods
(6 rows)

5.1 基础能力:向量类型与距离检索
CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3));
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'), ('[1,1,1]');
SELECT id, embedding, embedding <-> '[1,1,1]' AS l2
FROM items ORDER BY embedding <-> '[1,1,1]';
id | embedding | l2
----+-----------+--------------------
3 | [1,1,1] | 0
1 | [1,2,3] | 2.23606797749979
2 | [4,5,6] | 7.0710678118654755
L2 距离升序,语义正确。
5.2 验证修复一:avg 空集语义(#1012)
SELECT avg(embedding) FROM items WHERE false;
avg
-----
-- 返回 NULL,旧版本此处直接报错
(1 row)
正常路径不受影响:
SELECT avg(embedding) FROM items;
avg
-------------------------
[2,2.6666667,3.3333333]
空集返回 NULL,标准 SQL 语义恢复。报表里那类「按标签过滤后可能为空」的聚合查询,从此少一类间歇性故障。
5.3 验证修复二:IVFFlat 构建路径(CVE-2026-103484)
最直接的验证就是索引能正常创建:
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 2);
-- CREATE INDEX
顺手看一眼 \d items,IVFFlat 和 HNSW 两个索引可以共存于同一张表,互不干扰:
Indexes:
"items_pkey" PRIMARY KEY, btree (id)
"items_embedding_idx" ivfflat (embedding) WITH (lists='2')
"items_embedding_idx1" hnsw (embedding vector_l2_ops)
再强调一遍漏洞的利用条件:仅需低权限用户加建索引权限。生产环境请把「能创建索引」当敏感权限管理,业务账号不应随手持有 DDL 权限。
5.4 验证修复三:HNSW 索引路径(#1010,CHANGELOG 未列)
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops);
-- CREATE INDEX
前面说过,这条修复对「写入密集 + 频繁 VACUUM」的场景比 CVE 更隐蔽。它不报错、不崩溃,只是让召回率在数周内悄悄劣化,而且磁盘上的结构损伤无法自愈。已经在旧版本上长期跑并发写入 HNSW 的同学,升级后重建一次索引。
5.5 附加能力抽查
SELECT '[1,2,3]'::halfvec(3) <-> '[4,5,6]'::halfvec(3);
-- 5.196152422706632
halfvec(半精度向量)正常。sparsevec 的输入语法是 {index:value,...}/dim 格式,比如 '{1:1,3:2}'::sparsevec(3),这个和 0.8.7 的修复点无关,知道格式就行。
06. 升级指南:十分钟动作,关掉一个高危窗口
综合前面的分析,升级理由按优先级排:
- CVE-2026-103484 没有讨价还价的空间。 网络可达、利用难度低、普通用户即可、无需交互,影响 0.8.6 及以下所有版本。这不是可以排期的改进项,是应该立即执行的补丁。
- avg 空集报错是生产里真实存在的间歇性故障。 并行聚合架构下任何一个 worker 分片为空都可能触发,不常出现,出现就是线上报表失败。
- HNSW 竞态让召回率慢性死亡。 不报错不崩溃,只悄悄劣化,最符合「温水煮青蛙」特征的缺陷。对 RAG、推荐这类以召回率为命脉的应用,这一条单独就值得升级。
- 升级成本几乎为零。 索引格式无变更,无需重建索引(HNSW 长期并发写入场景除外,建议主动重建一次);升级动作就是
make && make install加每库一条ALTER EXTENSION vector UPDATE;。
操作清单,可以直接收藏:
-- 1. 确认当前版本
SELECT extversion FROM pg_extension WHERE extname = 'vector';
-- 2. 升级二进制后,在每个使用向量扩展的数据库执行
ALTER EXTENSION vector UPDATE;
-- 3. 回归验证
SELECT extversion FROM pg_extension WHERE extname = 'vector'; -- 应为 0.8.7
SELECT avg(embedding) FROM your_table WHERE false; -- 应返回 NULL
不同安装方式的升级路径:
- 源码安装:
git pull或切到 v0.8.7 tag,make && make install,然后每个库执行ALTER EXTENSION vector UPDATE; - 发行版包:PGDG 或发行版仓库升级包,包脚本没自动执行的话手动跑
ALTER EXTENSION vector UPDATE; - 云托管:Neon、Supabase 这类托管服务一般会在维护窗口自动升级或提供版本选择,关注厂商公告
07. 三个判断
判断一:IvorySQL 对 PG 生态的继承是真实的。 这次实验里,pgvector 0.8.7 在 IvorySQL 上除了一行内核 API 适配外零障碍运行,CREATE EXTENSION、HNSW/IVFFlat 索引、聚合函数全部符合预期。Oracle 迁移项目不必为 AI 能力另起炉灶,一套实例同时服务 PL/iSQL 老代码和向量新业务。
判断二:在新内核加新扩展,要有适配意识。 追最新内核就意味着扩展可能踩到接口漂移,这次的 plan_create_index_workers 第三参数就是典型。生产环境我建议以 IvorySQL 稳定版为基线(比如 5.6,PG 18.6 内核),扩展生态的兼容性更成熟。生产环境千万不要上 beta 版本。
判断三:安全更新不分平台。 无论你跑在 PostgreSQL 还是 IvorySQL 上,只要装了 pgvector 0.8.6 及以下,CVE-2026-103484 就与你有关。升级十分钟的事,不要等 Agent 顺着安全漏洞爬进来就为时已晚了。
向量数据库的竞争进入深水区之后,胜负手不再是「有没有向量类型」,而是生态兼容性、修复响应速度和迁移友好度的综合较量。IvorySQL 用双端口承接存量 Oracle 业务、用 PG 生态承接 AI 新业务;pgvector 0.8.7 则示范了一个健康的开源扩展如何对待一个 8.8 分的 CVE:快速发布、明确披露、零迁移成本。
Have a nice day ~ ☕
公众号「 少安事务所 」,专注于数据 & AI 领域技术传播。
如果这篇文章为你带来了灵感或启发,请帮忙『点赞、转发、推荐』,感谢!ღ( ´・ᴗ・` )~




