混合检索的极简主义:PostgreSQL + pgvector 生产级方案

混合检索的极简主义:PostgreSQL + pgvector 生产级方案

一、什么是混合检索?

在知识问答系统中,单一的检索方式总有局限:

  • 关键词检索(BM25):擅长匹配精确术语,但无法理解语义。用户问"怎么解决登录失败",它只认识"登录""失败"这几个字,不理解"认证""鉴权""Session过期"等表达方式。
  • 语义检索(向量):理解含义,能泛化召回,但对低频术语、缩写词、代码片段等缺乏敏感性。

混合检索将两者结合:用 BM25 保证精确匹配,用向量保证语义泛化,再用 RRF(倒数排名融合)把两路结果合并,取长补短。这套组合已成为 RAG 系统的标准检索模式,在 Elasticsearch 8.x、Solr 等主流搜索引擎中均有官方实现。

二、一个被忽视的事实

很多架构师接到检索需求后的第一反应,是画出这样一张架构图:

业务库(MySQL)→ 发件箱模式 → Kafka → 消费组 → Elasticsearch(BM25)+ Milvus(向量)→ 融合服务 → 返回结果

这张图很漂亮,但它隐含了一个前提:你的数据量足够大,值得用分布式架构去拆解。

现实中有大量场景并不需要这么复杂的方案:

  • 企业内部知识库:几百到几千份文档
  • 个人或小团队的 RAG 应用:几千万字的中文资料
  • 垂直领域的问答助手:几万到十几万条切块

这些场景的共同特点是:数据量不大,但对稳定性要求不低,对运维投入要求极低。

这时候,PostgreSQL + pgvector 提供了另一种选择:不用拆,一个库扛所有。

三、PostgreSQL 如何"一人三角"?

PostgreSQL 通过扩展机制,在同一个内核上叠加多种数据模型:

  • 关系数据库:存文档元数据、切块映射、状态字段------这是它的原生能力
  • 全文检索引擎 :通过 tsvector 和 GIN 索引,构建倒排表,支持 BM25 风格的全文检索
  • 向量检索引擎 :通过 pgvector 扩展,支持 HNSW 或 IVFFlat 索引,做向量的近似最近邻搜索

这三个角色共享同一套基础设施:

统一内存池:热数据、倒排词典、HNSW 图结构全部在 Shared Buffers 中,查询无需跨网络,延迟极低。

ACID 事务:写入一条记录,要么三个索引同时更新,要么一起回滚。不存在"数据已入业务库但索引没更新"的中间状态,也不需要发件箱、消息队列、最终一致性这些分布式补偿机制。

单一进程:BM25 检索和向量检索在同一个数据库进程内完成,没有序列化开销,没有网络往返,没有连接池争用。

关键理解:这三个索引是独立的物理文件,互不干扰。查询优化器会根据统计信息选择走 GIN 索引、HNSW 索引,还是两者都走再做 Bitmap 合并。

四、索引选型:HNSW 还是 IVFFlat?

pgvector 提供两种向量索引,选型依据是数据规模:

IVFFlat:先对向量做聚类,查询时只搜索最近的几个聚类中心。构建快,内存小,但需要预先训练聚类中心,且数据分布变化后需要重训。适合百万级以下、数据相对稳定的场景。

HNSW:构建一张分层图结构,查询时从顶层向下贪婪搜索。无需训练,支持动态插入,召回率极高(0.95-0.99),查询速度是 IVFFlat 的数倍到数十倍。代价是构建稍慢,内存占用略高。

对于持续增长的场景,HNSW 是更合适的选择------新数据直接插入图结构,无需等待聚类中心重算。构建时间较长(对于十万级数据约 20 分钟),但这是一次性成本,远低于日常运维的复杂度。

两个核心参数直接影响性能与内存的平衡:

  • m(每个节点的双向链接数):值越大,索引质量越高、查询越快,但内存和构建时间也增加。一般取 12-48,默认 16。
  • ef_construction (构建时的动态列表大小):值越大,索引质量越高,构建越慢。建议至少为 2 * m

查询时还有第三个参数 ef_search,可在运行时动态调整:值越大召回率越高,但查询越慢。适合在负载低时提高召回率,负载高时牺牲少量召回率换取速度。

五、混合检索的实现思路

5.1 为什么不依赖数据库内置的混合查询?

pgvector 目前不提供 BM25 + 向量的原生混合检索能力,这是设计决策而非缺陷------它将融合逻辑留给应用层,保持了索引内核的简洁性。

在应用层做融合有三个好处:一是融合算法(如 RRF)可以独立迭代升级;二是可以灵活调整两路检索的权重和各自的超参数;三是不受数据库版本限制,任何支持向量和全文检索的数据库都可复用这套逻辑。

5.2 查询流程

用户输入一个问题后,应用层并行发起两路检索:

  • 全文检索:将问题转为 ts_query,走 GIN 索引,召回 Top K(通常取 30-50)
  • 向量检索:将问题转为 embedding,走 HNSW 索引,召回 Top K(同样取 30-50)

两路返回的是各自排序的文档 ID 列表,不涉及具体分数(分数尺度不同,无法直接比较)。

5.3 RRF 融合算法

RRF(倒数排名融合)是目前业界最稳定的多路召回融合算法:

RRF_score(d) = Σ 1 / (k + rank_i(d))

其中 k 为平滑常数,通常取 60。RRF 不依赖分数归一化,天然支持多路召回的公平融合------无论 BM25 打分是 0.1 还是 10,无论向量距离是 0.01 还是 1.0,最终只靠排名决定权重。Elasticsearch 8.x 和 Solr 均将 RRF 作为标准融合方式内置。

应用层计算出每个文档的 RRF 分数后,按分数降序排列,返回 Top N 给大模型作为上下文。

六、数据写入与索引更新

6.1 写入流程

新文档进入系统后,经历以下步骤:

  1. 文档解析,提取纯文本
  2. 按策略切块(512 tokens,10% 重叠,优先在标点处切割)
  3. 插入 chunks 表,全文索引同步更新(GIN 索引在事务提交时即可见)
  4. 发送异步任务,调用 Embedding 模型生成向量
  5. 向量回填,HNSW 索引自动插入新节点

关键点:全文索引是同步更新的------数据插入提交后,用户立即就能搜到新文档。向量索引是异步更新的------延迟取决于 Embedding 计算速度(毫秒到秒级)。这种设计兼顾了实时性与吞吐量。

6.2 为什么不需要消息队列?

分布式架构使用 MQ 是因为"写入业务库"和"写入 ES/Milvus"是多个独立的网络调用,必须用消息队列保证最终一致性。

但在单库方案中,全文索引和向量索引只是同一个数据库的不同索引结构。一个 INSERT 提交后,所有索引同时更新------不需要发件箱,不需要 MQ,不需要补偿任务。ACID 事务天然解决了数据一致性问题。

6.3 定期维护

虽然日常写入无需人工干预,但有两个维护任务值得关注:

全文索引的 IDF 更新 :随着新文档加入,词项的文档频率会变化,旧 IDF 会漂移。解决方式是定期重建全文索引(支持 CONCURRENTLY 在线重建,不阻塞查询),建议每周低峰期执行一次。

向量索引的长期健康:HNSW 索引在大量插入后,图结构可能产生局部退化,导致召回率缓慢下降。建议每月或每季度重建一次 HNSW 索引(同样支持在线重建),恢复最优结构。

对于十万级数据量,重建全文索引只需几十秒,重建 HNSW 索引也只需几分钟。这些操作可以在低峰期自动执行,对用户完全透明。

七、这个方案的边界在哪里?

没有任何方案是万能的。PostgreSQL + pgvector 的适用边界由以下因素决定:

数据量上限:业界公认的 pgvector 舒适区在 100 万条向量以内。超过这个量级,查询延迟和内存占用会显著上升。

并发上限:单机 PostgreSQL 的混合检索 QPS 通常在 100-200 之间(取决于数据量和索引参数)。内部系统通常远低于这个值。

向量维度限制:pgvector 对 HNSW 索引的支持上限为 2000 维。主流的 bge-large(768维)、OpenAI text-embedding-3-small(1536维)都在安全范围内。

当数据量突破 100 万、或 QPS 超过 200、或需要多租户隔离等复杂权限控制时,就是考虑迁移到 ES + Milvus 分布式架构的信号。但在此之前,单库方案足够支撑 3-5 年的增长。

八、总结

PostgreSQL + pgvector 方案的核心理念是:

  • 用 ACID 事务解决数据一致性问题,而不是靠发件箱 + MQ + 补偿任务
  • 用共享内存池解决数据访问延迟问题,而不是靠分布式缓存
  • 用单一技术栈降低运维复杂度,而不是靠多套系统的组合来换取理论上无限的水平扩展

这套方案不够"酷"------它没有 Kafka 的吞吐,没有 Milvus 的专用优化,没有微服务的灵活部署。但它足够稳、足够简单,适合绝大多数中小规模 RAG 场景。

好的架构不是技术越多越好,而是恰到好处地匹配场景。 对于百万级 Chunks 以内的知识问答系统,PostgreSQL + pgvector 就是那个"恰到好处"的选择。

相关推荐
snow@li17 小时前
MyBatis:动态 SQL 全景梳理
数据库·sql·mybatis
360智汇云18 小时前
KV-Probe:通用 KV 数据库测试套件
数据库
x8618 小时前
我与 IT 这三十年:2015,大数据平台的重与轻
数据库·it史
粗体鱼18 小时前
RAG/Agent 记忆混合检索多路召回:RRF 算法与Chunk RRF、Document RRF如何决策TopK
postgresql·milvus·es·rag·rff·mermory
z落落18 小时前
T-SQL 事务(Transaction)
java·数据库·sql
SelectDB19 小时前
当 PostgreSQL 面临性能瓶颈:80TB 电商业务迁移至 Apache Doris 的实践思考
postgresql
ClouGence20 小时前
MySQL迁移到达梦怎么做?信创数据库低停机迁移实战
数据库·sql·mysql
星空露珠20 小时前
迷你世界3.0API,事件监听
开发语言·数据结构·数据库·游戏·lua
数据库安全20 小时前
灾备演练双月报|美创 DRCC 筑牢红十字医院医疗系统安全底线
数据库·安全
IvorySQL20 小时前
PG 日报|PG20 正式计划移除 refint 模块,官方指引迁移原生外键
数据库·人工智能·postgresql·开源·区块链