将搜索延迟拆解到每个阶段(查询理解10ms、候选生成40ms、特征拉取25ms、排序模型Xms),用预算分配替代感性优化,只有可度量的阶段才能被修复。
无法优化一个尚未拆解的延迟。提前分配预算,把「搜索很慢」变成一句关于哪一步超支的陈述,它是唯一能把对话从感觉拉向修复方案的东西。
从目标出发,而非从性能剖析出发。以服务端 p95 全量搜索页面 200 ms 为基准。以下每一个数字都是一次分配——关于每个阶段允许消耗多少的决定,而非关于某个具体系统实际做了什么的主张。
加起来恰好 200 ms,没有任何余量,这正是把它写下来的意义:任何一步超支都意味着另一步必须压缩。这件事的价值不在于这些数字——你的数字肯定不同——而在于关于重排的争论变成了关于它那 60 ms 花在了哪里的争论,而非关于重排本身是否值得的抽象讨论。
有三个阶段在动手优化之前值得先了解其特性。
重排是线性缩放的,且仅支持线性缩放。如果 cross-encoder 每秒处理 a 个文档,重排 k2 个文档耗时 k2 / a 秒。k2 减半,耗时也减半。流水线上没有其他步骤能提供如此直接的杠杆,这是它成为首要排查点的原因,也是 k2 的选择值得在 retrieve、rank、rerank 中所述那般认真评估的原因。
特征补全本质上是扇出的一种伪装。为一千个候选拉取二十个特征是两万次查询。合并成一次往返是一跳网络;逐条去做则是两万跳,足以吃掉整个预算。这一行通常靠批量化与就近部署来解决,而非让某个东西变得更快。
ANN 搜索以召回率换时间,规则是显式的。向量索引暴露了一个搜索 effort 参数——HNSW 实现中的 ef——以曲线方式用延迟购买召回率,这条曲线是可以画出来的。这就使它成为少数几个真正有旋钮可调而非需要重写的行之一。机制在 HNSW 页面中讲过了;这里要说的是这个旋钮确实存在,而且正确的设置是一个预算决策,而非使用默认值。
以下这个效应让分布式搜索比其各组件之和显得更慢。一个向 n 个分片扇出的查询,要等到最慢的分片返回才结束。如果每个分片独立超过某阈值的概率为 q:
P(at least one shard is slow) = 1 - (1 - q)^n
q = 0.01 (each shard's p99), n = 20 shards
1 - 0.99^20 = 1 - 0.8179 = 0.182
18% 的查询会碰到某个正处于自身最慢 1% 的分片。因此整个查询的 p99 是由每个分片分布中更远处的东西决定的。换过来问:要达到 20 分片 p99,每个分片需要什么样的可靠性?
want 1 - (1 - q)^20 = 0.01
(1 - q)^20 = 0.99
1 - q = 0.99^(1/20) = 0.99950
q = 0.00050
每个分片必须将延迟控制在 p99.95,而非 p99。
这是 Dean 和 Barroso 在「大规模尾延迟」(CACM, 2013)中提出的论点,也是扇出系统中尾延迟是系统问题而非单组件优化问题的原因。他们的缓解方案至今仍在使用:hedged requests,即在第一个请求超过 p95 后向另一个副本发送重复请求并取消输家,多消耗少量负载但消除了大部分尾延迟;tied requests,即两个副本互相感知,第二个请求主动取消自己;以及 micro-partitioning,使慢分片可以被迁移而非被动等待。
一个值得带出搜索场景的一般原则:在扇出规模下,组件的尾延迟变成了系统的中位数。同样的推导应用于模型服务参见 LLM 调用的延迟百分位。
减少重排的文档数量。线性、即时,质量损失可衡量而非可假设。 sweeping k2 对比 nDCG,找到曲线平坦的位置;超过那一点的文档只是在浪费延迟而没有任何收益。
缓存头部。根据 long-tail 页面的 Zipf 推导,一千个查询占了半数流量。以规范化查询加过滤条件加权限集(如果相关)为 key 的结果缓存,可以从内存中服务这半数流量。这是代价最低的大幅提升,唯一绕开它的理由是个性化排序——那会让 key 过于具体而无法命中。
将 facets 和 snippets 的计算与排序并行进行,而非排在其后。它们依赖结果集,但其他步骤也一样,并发运行可以把上面预算中的 45 ms 从关键路径上移除。
用 hedged requests 替代过度扩容。尾延迟的算术告诉我们,在 p95 之后发第二个请求代价低且有效。靠加容量来修复扇出导致的尾延迟代价高昂,而且基本无效。
有结果就立即推送。结果先行,facets 和计数次之。这不会改变预算中的任何数字,但改变了用户的感知——那才是真正应该作为目标的那个数字。
How Modern Search Works: Retrieve, Rank, Rerank
Building Search on a Small Budget
Autocomplete and Typeahead That People Trust