如果你的查询不需要按相关性排序,只用于筛选数据,将查询语句放入 filter 上下文或使用 constant_score 查询是禁用评分机制的标准做法。
先说结论:在不需要相关性排序的场景下,通过 filter 上下文或 constant_score 跳过评分计算,可以减少 CPU 开销。
- 先定位:确认业务是否依赖 _score 进行排序或阈值过滤。
- 先做:将 match 等查询包裹在 constant_score 中,或移至 bool 查询的 filter 子句。
- 再验证:检查返回结果中的 _score 字段及查询耗时变化。
命令速用版
以下是两种常见的禁用评分写法,可直接替换原有查询 DSL:
\n// 方式 1:使用 constant_score 包裹查询\n{\n \"query\": {\n \"constant_score\": {\n \"filter\": {\n \"term\": { \"status\": \"active\" }\n }\n }\n }\n}\n\n// 方式 2:在 bool 查询中使用 filter 上下文\n{\n \"query\": {\n \"bool\": {\n \"filter\": [\n { \"term\": { \"status\": \"active\" } }\n ]\n }\n }\n}\n为什么会这样
Elasticsearch 的查询上下文分为 Query Context 和 Filter Context。在 Query Context 中,引擎需要计算文档与查询条件的相关性评分(_score),这涉及词频、逆文档频率等复杂计算。而在 Filter Context 中,引擎只判断文档是否匹配(是或否),不计算评分,且结果可以被缓存。
当业务只需要筛选数据(如状态过滤、时间范围)而不需要按相关性排序时,计算评分是多余的开销。官方文档明确指出,Filter 上下文适合不需要评分且希望利用缓存的场景。
分步处理
1. 检查现有查询:查看当前使用的 DSL,确认是否使用了 match、multi_match 等默认开启评分的查询,且是否真的需要 _score。
2. 修改查询 DSL:
- 如果是精确匹配(term、range、exists 等),直接放入 bool 查询的 filter 数组中。
- 如果是全文检索但不需要排序,使用 constant_score 包裹原有查询。
3. 检查排序配置:确认代码中没有显式指定按 _score 排序。如果禁用了评分,默认排序可能变为文档 ID 或插入顺序,需显式指定其他字段排序。
4. 回滚准备:保留原有查询语句备份,一旦业务发现需要相关性排序,可立即恢复。
怎么验证是否生效
1. 检查响应体:查询返回的 hits 数组中,每个文档的 _score 字段。在使用 filter 上下文时,_score 通常为 null 或 1.0(取决于版本和具体写法),且不会随匹配程度变化。
2. 使用 Profile API:在查询参数中添加 \"profile\": true,查看执行详情。确认是否跳过了 scoring 阶段的计算细节。
3. 观察耗时:对比修改前后的查询耗时。注意需在相同负载和数据量下对比,公开资料中没有看到可靠的量化数据,具体提升取决于查询复杂度和数据规模。
常见坑
1. 排序失效:禁用评分后,无法再按 _score 排序。如果业务隐式依赖默认的相关性排序,结果顺序会变化。
2. 缓存限制:Filter 缓存并非无限,受限于 indices.queries.cache.size 设置。高基数字段(如唯一 ID)的过滤可能不会有效缓存。
3. 功能限制:某些查询(如 function_score、script_score)本质上依赖评分,强行禁用可能导致逻辑错误或报错。
参考来源
- Elasticsearch 官方文档 - Query and filter context:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html
- Elasticsearch 官方文档 - Constant score query:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html