随着向量数据库概念的火爆,pgvector作为PostgreSQL生态中最主流的向量扩展,已经被大量团队用于 embedding 的存储与相似度检索。但很多实践者很快会发现一个问题:单表千万级的向量数据配合HNSW索引,检索延迟可能还能接受,一旦数据量冲到亿级,或者写入吞吐持续走高,单机方案就会在内存、CPU和磁盘IO三个维度同时告急。这个时候,分片与并行查询就成了绕不开的话题。本文将从分片策略设计、跨节点查询归并、并行度调优三个层面,系统讲解pgvector在分布式场景下的落地方法。

一、向量分片前必须想清楚的两个问题
第一个问题是分片键的选择。与传统业务表不同,向量表的主键和向量列之间没有天然的关联关系,你无法像按用户ID分片那样"顺便"把相似的向量分到一起。pgvector的HNSW索引是基于图的近似最近邻结构,它天然是"全局"的,一旦切分到多个节点,每个节点只能构建局部的索引图,全局的近邻关系就被破坏了。这意味着分片检索的召回率一定会低于单机,这是架构上必须接受的代价。
第二个问题是路由模式。一种是按向量内容的哈希分片,比如对id取模,优点是分布均匀、写入简单,缺点是每次查询都要广播到所有分片;另一种是按业务维度分片,比如按用户所属的租户、按内容类目分片,这样查询可以只命中单个分片,延迟最优,但要求业务查询天然带有分片键。如果检索场景是"全库搜索相似内容",那就只能接受广播式查询,此时并行查询的性能就决定了整体体验。
二、基于Citus的分片表设计与索引策略
Citus是PostgreSQL生态中最成熟的分布式扩展,它把数据水平拆分为shard并分散到多个worker节点。下面是一个典型的向量分片表示例,采用均匀哈希分布,同时在每个分片上本地构建HNSW索引:
-- 先启用扩展(注意pgvector需在每个worker节点上都安装)
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS citus;
-- 创建向量表,1536维对应常见embedding模型输出
CREATE TABLE doc_vectors (
id bigserial PRIMARY KEY,
doc_id bigint NOT NULL,
content text,
embedding vector(1536),
created_at timestamptz DEFAULT now()
);
-- 通过Citus转为分布表,按doc_id哈希分到32个分片
SELECT create_distributed_table('doc_vectors', 'doc_id', shard_count := 32);
-- 分布索引会自动下推到各分片本地执行
CREATE INDEX ON doc_vectors
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
这个方案的关键点在于:create_distributed_table之后,Citus会自动把建索引语句下发到所有worker,每个分片持有自己局部的HNSW图。查询时,协调器把SQL广播到所有分片,每个分片返回自己的Top-N结果,协调器再做全局归并排序。由于只需要归并Top-N而非全量数据,网络传输量是可控的。
索引选型上,HNSW与IVFFlat在分片场景表现差异明显。HNSW查询快、召回率高,但构建慢、内存占用大;IVFFlat构建快、内存省,但召回率对lists参数非常敏感。如果分片数量很多,每个分片数据量被摊薄到几十万级别,此时IVFFlat配合合理的probes设置往往是性价比更高的选择,因为小数据集上HNSW的图构建收益会打折扣。
三、并行查询:单机并行与分布式并行的配合
PostgreSQL本身支持单机并行查询,对向量检索来说,即使不用Citus,合理配置并行参数也能显著提升全表扫描类查询的吞吐。核心参数有三个:max_parallel_workers_per_gather控制每个查询节点能启动的Worker数,parallel_setup_cost和parallel_tuple_cost影响优化器是否选择并行计划:
-- 查看当前并行相关配置 SHOW max_parallel_workers_per_gather; -- 允许每个Gather节点启用4个并行Worker SET max_parallel_workers_per_gather = 4; -- 降低并行启动成本,让小分片查询也倾向于并行 SET parallel_setup_cost = 500; SET parallel_tuple_cost = 0.05; -- 示例:向量相似度检索,按余弦距离排序取Top 20 SELECT doc_id, 1 - (embedding <=> '[0.11, 0.22, ...]'::vector) AS score FROM doc_vectors ORDER BY embedding <=> '[0.11, 0.22, ...]'::vector LIMIT 20;
需要特别注意的是,HNSW索引扫描本身是不能并行化的,PostgreSQL的并行Worker只作用于并行顺序扫描(Parallel Seq Scan)和并行索引扫描等计划节点。因此单机场景下,如果想利用并行加速向量检索,要么放弃索引走全表精确计算,要么依赖分片架构让多个节点"天然并行"。这也正是Citus方案的价值所在:分布式并行把并行度从单机CPU核数扩展到了整个集群的核数总和。
在Citus架构下,协调器对分片查询的并发调度默认是并行的,可以通过citus.max_adaptive_executor_pool_size调节单连接能同时驱动的分片连接数。一个实用技巧是缩小每个分片返回的结果集:在分片SQL里加上LIMIT 100,让每个分片只返回本地Top 100,协调器归并后取全局Top 20,召回率损失极小,但网络与排序开销大幅下降。
四、召回率与性能的平衡:实测调优建议
分片向量检索的本质是"局部Top-N再全局归并",召回率的损失主要来自边界向量。实测经验表明,8到32个分片配合每分片返回本地Top 100,召回率通常能保持在95%以上;如果业务要求召回率接近99%,一方面要调大hnsw.ef_search(默认40,可调到200以上),另一方面要增加每分片的返回量,这会直接推高延迟,需要在压测中找到平衡点。
另一个容易被忽视的坑是分片倾斜。如果业务写入存在热点key,比如大量文档集中在同一个doc_id哈希桶,会导致某个worker负载远高于其他节点。建议在分片键之外引入盐值字段,或者在业务层做写入打散。同时,务必监控每个分片的向量索引内存占用,HNSW索引需要常驻shared_buffers和操作系统页缓存,一旦某分片索引被频繁换出内存,延迟会出现数倍抖动。
总结来说,PostgreSQL做向量分片与并行查询是一条完全可行的路线:小规模用单机pgvector加并行参数,千万级以上引入Citus分片加局部HNSW索引,配合分片级LIMIT与归并优化,可以在不引入专用向量数据库的前提下,获得接近商业方案的检索性能。核心是接受近似检索的召回率折损,并通过分片数量、ef_search、返回量这三个参数的不断压测,找到属于自己业务的最优解。
PostgreSQLpgvector并行查询修改时间:2026-08-31 02:48:40