混合检索的极简主义:PostgreSQL + pgvector 生产级方案
混合检索的极简主义PostgreSQL pgvector 生产级方案一、什么是混合检索在知识问答系统中单一的检索方式总有局限关键词检索BM25擅长匹配精确术语但无法理解语义。用户问“怎么解决登录失败”它只认识“登录”“失败”这几个字不理解“认证”“鉴权”“Session过期”等表达方式。语义检索向量理解含义能泛化召回但对低频术语、缩写词、代码片段等缺乏敏感性。混合检索将两者结合用 BM25 保证精确匹配用向量保证语义泛化再用 RRF倒数排名融合把两路结果合并取长补短。这套组合已成为 RAG 系统的标准检索模式在 Elasticsearch 8.x、Solr 等主流搜索引擎中均有官方实现。二、一个被忽视的事实很多架构师接到检索需求后的第一反应是画出这样一张架构图业务库MySQL→ 发件箱模式 → Kafka → 消费组 → ElasticsearchBM25 Milvus向量→ 融合服务 → 返回结果这张图很漂亮但它隐含了一个前提你的数据量足够大值得用分布式架构去拆解。现实中有大量场景并不需要这么复杂的方案企业内部知识库几百到几千份文档个人或小团队的 RAG 应用几千万字的中文资料垂直领域的问答助手几万到十几万条切块这些场景的共同特点是数据量不大但对稳定性要求不低对运维投入要求极低。这时候PostgreSQL pgvector 提供了另一种选择不用拆一个库扛所有。三、PostgreSQL 如何“一人三角”PostgreSQL 通过扩展机制在同一个内核上叠加多种数据模型关系数据库存文档元数据、切块映射、状态字段——这是它的原生能力全文检索引擎通过tsvector和 GIN 索引构建倒排表支持 BM25 风格的全文检索向量检索引擎通过pgvector扩展支持 HNSW 或 IVFFlat 索引做向量的近似最近邻搜索这三个角色共享同一套基础设施统一内存池热数据、倒排词典、HNSW 图结构全部在 Shared Buffers 中查询无需跨网络延迟极低。ACID 事务写入一条记录要么三个索引同时更新要么一起回滚。不存在“数据已入业务库但索引没更新”的中间状态也不需要发件箱、消息队列、最终一致性这些分布式补偿机制。单一进程BM25 检索和向量检索在同一个数据库进程内完成没有序列化开销没有网络往返没有连接池争用。关键理解这三个索引是独立的物理文件互不干扰。查询优化器会根据统计信息选择走 GIN 索引、HNSW 索引还是两者都走再做 Bitmap 合并。四、索引选型HNSW 还是 IVFFlatpgvector 提供两种向量索引选型依据是数据规模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 写入流程新文档进入系统后经历以下步骤文档解析提取纯文本按策略切块512 tokens10% 重叠优先在标点处切割插入 chunks 表全文索引同步更新GIN 索引在事务提交时即可见发送异步任务调用 Embedding 模型生成向量向量回填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-large768维、OpenAI text-embedding-3-small1536维都在安全范围内。当数据量突破 100 万、或 QPS 超过 200、或需要多租户隔离等复杂权限控制时就是考虑迁移到 ES Milvus 分布式架构的信号。但在此之前单库方案足够支撑 3-5 年的增长。八、总结PostgreSQL pgvector 方案的核心理念是用 ACID 事务解决数据一致性问题而不是靠发件箱 MQ 补偿任务用共享内存池解决数据访问延迟问题而不是靠分布式缓存用单一技术栈降低运维复杂度而不是靠多套系统的组合来换取理论上无限的水平扩展这套方案不够“酷”——它没有 Kafka 的吞吐没有 Milvus 的专用优化没有微服务的灵活部署。但它足够稳、足够简单适合绝大多数中小规模 RAG 场景。好的架构不是技术越多越好而是恰到好处地匹配场景。对于百万级 Chunks 以内的知识问答系统PostgreSQL pgvector 就是那个“恰到好处”的选择。