土法炼钢兴趣小组的算法知识备份

【全文检索引擎】查询路径:协调节点上的 query + fetch

文章导航

分类入口
databasedistributed
标签入口
#elasticsearch#search#coordinating-node#query-phase#fetch-phase#reduce#dfs#scatter-gather#8.x

目录

一条 GET /products/_search 返回的不仅是倒排相交结果,还经历 分布式调度:某个节点充当 协调节点(coordinating node),把查询扇出到目标索引的 每个分片副本组,收集局部命中,再 归并(reduce) 成全局 Top-K,最后 二次扇出 fetch 拉取 _source 或 stored fields。若只读过 第 9 篇 的 Lucene BooleanScorer,容易以为瓶颈在 postings 相交;生产尾延迟里,协调节点归并跨分片 fetch聚合桶合并 常常与单分片 CPU 同级显眼。

本文锚定 Elasticsearch 8.x ReferenceSearchSearch your dataHow searches work),说明默认 query_then_fetch 两阶段路径、reduce 在排序与聚合上的职责,以及可选 dfs_query_then_fetch 如何为 BM25 提供 全局词项统计(与 第 6 篇 衔接)。不写伪造的集群 QPS;机制与 architecture/42 查询路径节 一致,本篇补充 分片选择、阶段边界与 DFS 代价

本文是「全文检索引擎」系列第 13 篇(共 18 篇)。→ 系列目录

版本锚定:Elasticsearch 8.x;底层 Lucene 9.x。DFS 在 8.x 中通过 search_type / 等价参数触发,行为以 Reference 为准。


一、协调节点:入口、路由与扇出

任何节点都可接收 REST 请求。若该节点 不持有 目标分片数据,它自动成为 协调节点:解析索引表达式(含别名、通配、_routing 裁剪),从 cluster state第 11 篇)解析 分片 → 节点 映射,构建 shard iterator(每个主分片副本组选一个活跃副本,默认轮询)。

flowchart TB
  Client["Client"]
  Coord["Coordinating node<br/>parse · route · reduce"]
  S0["Shard 0<br/>query / fetch"]
  S1["Shard 1<br/>query / fetch"]
  S2["Shard 2<br/>query / fetch"]
  Client --> Coord
  Coord -->|"query phase"| S0
  Coord -->|"query phase"| S1
  Coord -->|"query phase"| S2
  S0 --> Coord
  S1 --> Coord
  S2 --> Coord
  Coord -->|"fetch phase"| S0
  Coord -->|"fetch phase"| S2

扇出度 近似等于 参与查询的主分片数 × 阶段数。索引过碎(\(N_{\text{primary}}\) 过大)时,协调开销与 architecture/42 所述 分片膨胀 叠加,表现为 固定协调延迟 上升——即使每个分片数据很少。


二、Query phase:局部打分、局部 Top-K

Query phase 在每个分片本地执行 Lucene 查询(第 9 篇):

  1. 当前 Searcher 上的段(受 refresh 影响)做倒排遍历 / 跳跃。
  2. Similarity(默认 BM25)计算得分;排序字段走 DocValues第 10 篇)。
  3. 分片本地维护 优先队列,仅保留 from + size(或 track_total_hits 相关)所需的 轻量命中:通常是 _shard_doc_id_score、排序键、routing—— 默认拉取完整 _source

分片返回给协调节点的结构体可理解为:「我在本分片上的局部有序截断」。协调节点 不能 假设分片返回了全局最优,必须在 reduce 阶段 重新归并

2.1 与单机 Lucene 的差异

单机 Lucene 上 IndexSearcher.search 一次完成遍历 + TopDocs。分布式层把 TopDocs 截断前移到分片,是为 减少网络:若每分片返回全部命中,协调节点会 O(全命中) 爆炸。代价是:分片局部 Top-K 可能漏掉 全局边界附近的文档——因此需要 足够大的分片级队列size 很大或深度分页时问题恶化(官方推荐 search_after / PIT,Reference Paginate search results)。


三、Reduce:全局排序、聚合与 suggest

协调节点收到各分片 query 结果后执行 reduce

结果类型 Reduce 操作 注意点
相关性排序 多路归并 _score 分片局部 IDF 不一致时 BM25 有偏(见第四节 DFS)
sort 字段 多路归并排序键 字段需 DocValuesmissing 策略全局一致
aggregations 合并桶 / 子聚合 第 14 篇
track_total_hits 求和或截断计数 精确总数贵;默认阈值以 8.x 为准
sequenceDiagram
  participant C as Coordinating node
  participant A as Shard A
  participant B as Shard B
  C->>A: query phase
  C->>B: query phase
  A-->>C: local top (id, score)
  B-->>C: local top (id, score)
  Note over C: reduce → global top doc ids
  C->>A: fetch phase (ids on A)
  C->>B: fetch phase (ids on B)
  A-->>C: stored / _source
  B-->>C: stored / _source
  C-->>C: merge hits for response

Reduce 完成后,协调节点持有 全局 Top 文档的逻辑坐标(索引、分片、_id、routing),进入 fetch phase。


四、Fetch phase:按 doc id 取正文

Fetch phase 不再做全文打分,而是对 reduce 产出的 doc id 列表,向 持有该文档的分片 发起 multi-get 风格 的字段读取:

因此搜索延迟 ≈ query 扇出 + reduce + fetch 扇出"_source": falsestored_fields 精简可降低 fetch 成本——RAG 场景若只要 id 做二段召回,应显式关闭 _source(与 llm-infra sparse 召回 接口设计相关)。

副本一致性:fetch 可能命中与 query 不同副本(若两阶段间发生 relocation,极少见);依赖 Lucene 段复制 与 refresh 传播。写后读应配合 第 12 篇wait_for


五、可选 DFS:dfs_query_then_fetch 与全局统计

默认 query_then_fetch 下,BM25 的 文档频率 df平均字段长度 avgdl 等统计来自 分片本地 Searcher。词项在分片间分布不均时,同一查询在不同分片上的 IDF 不一致,全局排序可能偏离「把所有文档当作一个大索引」时的理想 BM25(第 6 篇 公式中的 collection 统计)。

search_type=dfs_query_then_fetch(或 8.x 中等价设置)增加 前置 DFS phase

  1. 协调节点向各分片收集 词项级全局统计(df、collection size 等)。
  2. 再执行常规 query phase,分片用 统一统计 打分。
模式 往返次数 打分一致性 适用
query_then_fetch(默认) 2 分片局部统计 大多数生产查询
dfs_query_then_fetch 3+ 全局统计近似单机 小索引、教学、对 IDF 敏感实验

代价:多一轮扇出与聚合统计,协调延迟上升;不能 免费修正跨分片 文档长度归一 的所有偏差。学习排序(LTR)与向量混合(第 15 篇)进一步把「全局特征」推到模型侧,DFS 仍是 稀疏 BM25 时代的工程补丁


六、查询裁剪:routing、别名与 preference

减少扇出度的手段(与 第 11 篇 联动):

GET /orders/_search?routing=user_42
{
  "query": { "term": { "user_id": "user_42" } }
}

_search并发 msearch 把多条查询批到一次 HTTP,但 每条子查询仍独立扇出——不能当作减少分片数的银弹。

6.1 深度分页与 search_after

from + size 深度分页时,协调节点需让每个分片返回 足够深的局部队列,内存与网络随 from 线性恶化。ES 8.x 推荐 search_after(排序游标)或 PIT(point in time)+ search_after 做稳定翻页(Reference Paginate search results):

POST /products/_search
{
  "size": 10,
  "sort": [
    { "created_at": "desc" },
    { "_id": "desc" }
  ],
  "search_after": [1710000000000, "doc_9981"]
}

机制要点:游标建立在 reduce 后的全局排序键上,下一页请求仍触发 全分片 query,但避免协调层为巨大 from 保留中间结果;PIT 固定 索引时间点,减轻 refresh 导致的 翻页重复/漏文档。这与 第 12 篇 的 NRT 语义直接相关。

6.2 profile 与慢查询判读

单分片热点还是协调归并瓶颈,可用 _profile: true 区分(Reference Profile API):

POST /products/_search
{
  "profile": true,
  "query": { "match": { "title": "lucene" } }
}

响应中 分片级 query 时间collect/fetch 时间 分开列出;若各分片均匀偏慢,查 postings 与 merge(第 5、8 篇);若分片快而总延迟高,查 扇出度、协调 heap、聚合 reduce第 14 篇)。本篇不粘贴未在固定环境复现的 profile 树,读者应在目标集群上对慢查询取样。


七、学术谱系、争论与开放问题

谱系:分布式 IR 的 collection merging(Zobel & Moffat, 2006)→ 各引擎 分片局部 Top-K + 协调归并 → ES 显式 query/fetch 分裂 以省带宽 → DFS 作为可选全局统计轮。

争论

  1. 两阶段 vs 单阶段 query_and_fetch:后者合并阶段但传 大 payload,官方默认弃用路径;与 MapReduce 式 map 输出Combiner 的权衡同构。
  2. 精确 total hits vs 性能track_total_hits: true 迫使分片报告全量计数,与 早停 Top-K 优化冲突——产品默认在「够用的近似」与「审计级精确」间折中。

开放问题


八、常见误解

误解 事实
「协调节点只做转发」 负责 解析、扇出、reduce、fetch 编排
「query 阶段返回完整文档」 默认只返 id + score + sort keys;正文在 fetch
「分片局部 Top-K = 全局 Top-K」 仅在得分 可分片独立 或分布极均匀时近似成立
「加副本减少 query 扇出」 扇出按 主分片数;副本只 负载均衡到组内不同物理分片
「DFS 是默认且免费」 默认 关闭;多一轮网络与统计合并
size: 10000 只是内存问题」 分片级队列与 fetch 都随 size 线性恶化

九、小结

三句话小结

  1. 默认搜索是 query_then_fetch:协调节点 query 扇出reduce 全局 Top doc idfetch 扇出_source/stored。
  2. Query phase 在分片本地用 Lucene 执行并 截断;reduce 归并得分、排序与聚合;fetch 才拉正文,两阶段设计为 省网络
  3. dfs_query_then_fetch 用额外一轮收集 全局词项统计,缓解跨分片 BM25 偏差,但增加延迟;扇出度仍由 主分片数 主导。

下一篇进入 聚合管线mapping 爆炸 失效模式:聚合与 mapping


参考资料

规范(A 级)

  1. Elasticsearch 8.x Reference, Run a searchHow searches work(query / fetch 两阶段)。
  2. Elasticsearch 8.x Reference, Searchsearch_typedfs_query_then_fetch
  3. Elasticsearch 8.x Reference, Paginate search resultssearch_after、PIT。
  4. Elasticsearch 8.x Reference, PreferenceRouting
  5. Apache Lucene 9.x, IndexSearcher / TopDocsCollector(与 第 9 篇 一致)。

论文(A 级)

  1. Robertson, S. & Zaragoza, H., The Probabilistic Relevance Framework: BM25 and Beyond, FnTIR 2009(全局 collection 统计假设)。
  2. Zobel, J. & Moffat, A., Inverted Files for Text Search, 2006(分区合并)。

站内对读(B 级)

  1. 搜索引擎架构(分布式查询叙事)。
  2. 本系列 第 6 篇第 10 篇第 11 篇第 12 篇

返回 系列目录 | 上一篇:refresh · flush · translog | 下一篇:聚合与 mapping

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-18 · database / distributed

【列存引擎内核】ReplicatedMergeTree

ReplicatedMergeTree 副本协调:Log entry、ClickHouse Keeper/ZooKeeper 路径、副本同步与 recovery。双节点实验步骤(本环境未部署)。24.x LTS 默认推荐 Keeper。


By .