AlloyDB BM25:关键词回主库
后端约 6 分钟阅读

AlloyDB BM25:关键词回主库

AlloyDB PostgreSQL 2026-08 上线 BM25 全文索引 Preview。讨论为何混搜仍要关键词通道,以及融合、ACL、索引失效的后端落地清单。

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 以官方文档为准,心智可以先定成:

  1. 实例大版本落到 17/18
  2. 启用文档所述的 pg_textsearch(或当时正式扩展名)
  3. 在要搜的文本列建 BM25 索引
  4. 查询走扩展提供的排序接口,而不是自己拼 ts_rank 玄学

和自建 Postgres + 外部 Elasticsearch 比,收益是少一条同步管道:事务提交和索引可见性都在数据库边界内。代价也清楚:Preview 的功能开关、配额、和超级大索引的运维细节还要跟 release note 盯着。

若你不在 GCP,思路仍可迁移:Postgres 生态里有多种全文方案;关键是主存储与关键词索引的一致性模型,不是 logo。自建 PG 用 tsvector、外部搜用 ES/OpenSearch、云厂商托管 BM25,都是同一类决策:接受哪里的延迟、哪里的运维、哪里的账单。

AlloyDB 同期还在讲向量规模和列存加速。对要做「库内 RAG」的人,吸引力是事务数据、全文、向量待在一个信任边界里。这不等于所有搜索都该塞进 OLTP 实例——分析型、日志型流量照样该分流。

混合检索的后端形状

图:关键词通道与向量通道在服务端融合

我更愿意在服务里固定三步,而不是让业务 SQL 里散落魔法:

text
query
  → (A) BM25 top-k1   // 关键词、ID、术语
  → (B) vector top-k2 // 语义扩充
  → merge + 去重
  → (可选) 交叉编码器 / 业务规则重排
  → 截断返回

伪代码:

python
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 维度必须进缓存键。曾经见过「高级用户结果被匿名用户命中缓存」的事故,搜相关,根因是权限。

落地检查清单

  1. 列 20 条真实用户查询,标哪些必须关键词命中
  2. 单独测 BM25 / 单独向量 / 融合,看失败 case 类型
  3. ACL 与租户隔离写进查询计划,不进「后处理 TODO」
  4. Preview 功能:预留关掉扩展、回退 tsvector 或外部搜的开关
  5. 文档更新用 outbox 或触发器,避免「库已改、索引还在睡」
  6. 准备可解释性:至少能回答「这条为什么排第一」(命中了哪些词 / 哪路召回)
  7. 压测写入:批量导入和 Agent 狂写时的索引延迟曲线

带走一句: AlloyDB 的 BM25 Preview 提醒我们,关键词排序没有退休,它只是从「默认外挂」变回「可与主库共生的选项」。混搜的胜负手在融合与权限,不在又换一个 embedding 模型。

相关文章