这个坑值得记一笔。Vespa 8.396 上用 YQL 查 select * from doc where userQuery(),rank-profile 配了 match-phase 的 attribute:limit 1000,接口返回 root.fields.totalCount 一直是 10,而库里实际有 1200 条命中。

刚开始盯着「查询里带了 grouping,totalCount 的语义变成了分组数」,折腾了半个多小时,排掉了。

原因不止一个,得分开看。match-phase 的 attribute limit 截断了参与排序的候选;客户端读的是 hits 数组长度,不是 totalCount 字段;文档还没 feed 完,索引里的可见条数本来就少。

动手时先把 hits 设为 0 只看 totalCount

curl -s 'http://localhost:8080/search/?yql=select%20*%20from%20doc%20where%20true&hits=0' | jq '.root.fields.totalCount'

然后核对 match-phase 的配置是否压了候选数

curl -s 'http://localhost:8080/application/v2/tenant/default/application/default/environment/default/region/default/instance/default' | jq '.services[0].config'

接着用 visit 统计实际文档数做对照

vespa-visit --application-id default.default --cluster doc --destination stdout | wc -l

末了放宽 match-phase 后重跑同一条查询

curl -s 'http://localhost:8080/search/?yql=select%20*%20from%20doc%20where%20true&hits=0&ranking.matchPhase.maxHits=100000' | jq '.root.fields.totalCount'

白天有业务在跑,能验证的部分都压到了晚上做。

当时还开着另一个排查窗口,两边日志对照着看才定位到。

别只看报错消失,验证要多做一步。hits=0 时 totalCount 与 visit 统计的条数一致,前端翻页不再只有两页,搜索页的“共 N 条”也对上了。

这里还有个坑。Vespa 的 totalCount 和展示条数是两个概念,看到只有十条先别怀疑索引,把 hits 设成 0 单独看总数,能省掉一大半排查时间。拖得越久,排查成本越高,因为现场状态会被后续操作覆盖。

作者 9823189