
Google Cloud 在 2026 年 8 月 4 日给 AlloyDB for PostgreSQL 加了 BM25 全文索引(Preview):pg_textsearch 扩展,面向 PostgreSQL 17/18 实例。同一时期文档和 Next'26 材料还在推向量检索(ScaNN 等)和混合检索叙事。对后端来说,信号很具体——关键词相关性又回到数据库内核旁边,而不是永远外包给专用搜索集群。
来源:AlloyDB release notes、Create BM25 index。
为什么 BM25 还没过时
向量检索擅长「意思差不多」。产品里大量查询却是:
- 订单号、SKU、错误码、精确短语
- 运营配置的关键词、合规必须命中的术语
- 「只要含有 X 且不要 Y」这种布尔味很重的条件
纯向量会把 INV-2026 和一堆发票叙述搅在一起;纯 ILIKE '%foo%' 在大表上又丑又慢。BM25 是信息检索里熬出来的词频–逆文档频率排序,对关键词场景仍然诚实。
RAG 和站内搜若只接 embedding,常见翻车是:用户输入专有名词,召回一篇「语义相关」但关键字段全错的文档。混合检索(BM25 + vector,再轻量重排)把这类坑填上了一半。
客服知识库是个好例子。用户贴错误码 E-4412,你要的是命中该码的 runbook,不是一篇语气相似的「常见网络问题」。embedding 可以在第二段补「相关排查」,第一段召回应交给关键词。
AlloyDB 这条能力和你怎么用
Preview 阶段 API 以官方文档为准,心智可以先定成:
- 实例大版本落到 17/18
- 启用文档所述的
pg_textsearch(或当时正式扩展名) - 在要搜的文本列建 BM25 索引
- 查询走扩展提供的排序接口,而不是自己拼
ts_rank玄学
和自建 Postgres + 外部 Elasticsearch 比,收益是少一条同步管道:事务提交和索引可见性都在数据库边界内。代价也清楚:Preview 的功能开关、配额、和超级大索引的运维细节还要跟 release note 盯着。
若你不在 GCP,思路仍可迁移:Postgres 生态里有多种全文方案;关键是主存储与关键词索引的一致性模型,不是 logo。自建 PG 用 tsvector、外部搜用 ES/OpenSearch、云厂商托管 BM25,都是同一类决策:接受哪里的延迟、哪里的运维、哪里的账单。
AlloyDB 同期还在讲向量规模和列存加速。对要做「库内 RAG」的人,吸引力是事务数据、全文、向量待在一个信任边界里。这不等于所有搜索都该塞进 OLTP 实例——分析型、日志型流量照样该分流。
混合检索的后端形状
图:关键词通道与向量通道在服务端融合

我更愿意在服务里固定三步,而不是让业务 SQL 里散落魔法:
query
→ (A) BM25 top-k1 // 关键词、ID、术语
→ (B) vector top-k2 // 语义扩充
→ merge + 去重
→ (可选) 交叉编码器 / 业务规则重排
→ 截断返回
伪代码:
def search(q: str, k: int = 20):
kw = db.bm25_search(q, limit=50) # id, bm25_score
sem = db.vector_search(embed(q), 50) # id, cos_score
merged = rrf_fuse(kw, sem) # 或加权分
merged = apply_acl(merged, user) # 权限必须在融合后、返回前
return merged[:k]
权限不能只做向量侧过滤。 图好看的召回如果在应用层才做 ACL,容易在日志和缓存里留下不该存在的片段。能在 SQL 里用 RLS / 租户键收紧,就别拖到最后。
融合算法别一上来上深度学习重排。RRF(倒数排名融合)或简单加权,配上业务规则(置顶官方文档、过滤过期)往往就够。重排模型留给头部流量和难 query;它贵,也难解释。
查询分类可以很糙但有效:含长数字/错误码 → 提高 BM25 权重;短自然语言问句 → 提高向量权重;空结果时互换通道再试一次。这类启发式写在配置里,比写死在 SQL 好调。
索引与写入路径
BM25 对更新敏感:标题改一个词,排序特征就变。设计时提前定:
- 同步更新:事务内维护,简单,写入延迟上去
- 近实时:outbox → worker 重建文档,接受秒级可见延迟
Agent 和自动化写入变多以后,文档表的更新频率往往高于你的设计假设。监控要看:索引构建延迟、查询 P99、以及「搜得到旧标题」的投诉。
大字段(整篇 Markdown、HTML)不要无脑进同一索引。摘要列 + 正文列分级,或正文只做存储、摘要/标题/标签进 BM25,能少很多噪音。HTML 要剥标签;否则 class 和 div 会污染词表,这事我看人踩过不止一次。
分词与语言:中文查询若走不适配的分析器,BM25 分数会离谱。上线前用真实中文 query 集打一遍,别只用英文 demo 过关。
和向量、图检索的分工
| 手段 | 更适合 |
|---|---|
| BM25 | 术语、编号、必须命中的词 |
| Vector | 同义改写、模糊意图 |
| 代码结构图 | 符号与调用关系(见今日 AI 文) |
| 日志系统 | 事件流、高基数维度 |
后端的职责是路由:这类 query 走哪条路,而不是逼一个索引干所有活。AlloyDB 把 BM25 和向量放近,是在减少「为了混搜再买一套集群」的默认冲动;真到十亿级日志,还是专用系统。
还有缓存:热 query 的融合结果可以短 TTL 缓存,但 ACL 维度必须进缓存键。曾经见过「高级用户结果被匿名用户命中缓存」的事故,搜相关,根因是权限。
落地检查清单
- 列 20 条真实用户查询,标哪些必须关键词命中
- 单独测 BM25 / 单独向量 / 融合,看失败 case 类型
- ACL 与租户隔离写进查询计划,不进「后处理 TODO」
- Preview 功能:预留关掉扩展、回退
tsvector或外部搜的开关 - 文档更新用 outbox 或触发器,避免「库已改、索引还在睡」
- 准备可解释性:至少能回答「这条为什么排第一」(命中了哪些词 / 哪路召回)
- 压测写入:批量导入和 Agent 狂写时的索引延迟曲线
带走一句: AlloyDB 的 BM25 Preview 提醒我们,关键词排序没有退休,它只是从「默认外挂」变回「可与主库共生的选项」。混搜的胜负手在融合与权限,不在又换一个 embedding 模型。


