Skip to content

查询优化 ​

1. 使用 filter 替代 query ​

json
# query context(计算相关性评分)
{ "query": { "range": { "price": { "gte": 100 } } } }

# filter context(不计算评分,可能命中节点查询缓存)
{ "query": { "bool": { "filter": { "range": { "price": { "gte": 100 } } } } } }

filter context 不计算相关性评分,符合条件的查询可能受益于节点查询缓存。缓存命中取决于查询类型、重复使用频率和分片状态,并非所有 filter 查询都必然命中缓存。

2. 避免深度分页 ​

json
# 深度分页,候选结果开销随分页深度增加
{ "from": 10000, "size": 10 }

# 快:search_after 取上一页最后一条的 sort 值,需配合稳定排序
GET /my-index/_search
{
  "size": 10,
  "sort": [{ "price": "desc" }, { "order_id": "asc" }],
  "search_after": [99.9, "order_007"]
}

search_after 依赖上一页最后一条命中的 sort 数组,取值类型与顺序必须和 sort 一致。排序字段组合需能稳定区分文档,例如 price 降序后追加唯一的 order_id,否则分页可能重复或遗漏。普通 search_after 可独立使用;需要在翻页期间保持固定数据视图时,再结合 PIT。完整方案见分页查询。

3. 使用 _source 过滤 ​

json
GET /my-index/_search
{
  "_source": ["name", "age"],
  "query": { "match_all": {} }
}

_source 过滤主要减少返回的响应体大小,实际收益取决于响应体大小和文档取数成本,不保证按同样比例降低 _source 的读取与解析成本。

4. 避免脚本查询 ​

json
# 慢
{ "query": { "script": { "script": "doc['price'].value > 100" } } }

# 快:优先用能利用索引的原生查询
{ "query": { "range": { "price": { "gt": 100 } } } }

脚本并非一概低效,其成本取决于执行方式和候选文档数量。优先用能利用索引结构的原生查询表达条件;确需脚本时,用实际执行数据评估成本。

5. 使用 routing 优化查询 ​

json
# 索引时指定 routing
PUT /my-index/_doc/1?routing=user_123
{
  "user_id": "user_123",
  "order_id": "order_001"
}

# 查询时指定 routing(只查询特定分片)
GET /my-index/_search?routing=user_123
{
  "query": {
    "term": { "user_id": "user_123" }
  }
}

routing 减少参与查询的分片数量,但要求查询路由键与索引时一致,并评估数据倾斜和热点分片风险。使用自定义 routing 后,后续查询或更新该文档都需带上正确的 routing 参数,否则可能定位不到文档;一次查询需要覆盖多个 routing 值时,也不能只传单个值。分片路由机制见分片机制。

6. 使用 Profile 分析查询 ​

json
# 使用 profile API 分析查询执行耗时
GET /my-index/_search
{
  "profile": true,
  "query": {
    "match": { "title": "Elasticsearch" }
  }
}

profile 用于分析查询内部各阶段的耗时,本身不负责预热缓存,且有测量开销,不代表完整端到端延迟。

7. 查询性能优化清单 ​

优化手段机制适用条件与成本
filter 替代 query不计算评分,可能命中节点查询缓存结果不需计分时适用
search_after 替代深度分页不再要求每个分片保留从起始到目标位置的候选结果需稳定排序,必要时配合 PIT
_source 过滤减少返回数据量收益取决于响应体大小
routing 查询减少参与查询的分片路由键需正确,注意数据倾斜
避免 script 查询原生查询利用索引结构仅当原生查询语义等价时成立
合理设置分片数减少查询协调与调度开销需权衡并行度,视数据规模与查询模式而定

8. 慢查询日志配置 ​

json
PUT /my-index/_settings
{
  "index.search.slowlog.threshold.query.warn": "5s",
  "index.search.slowlog.threshold.query.info": "2s",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.level": "info"
}

慢查询日志是索引级设置,query 阶段与 fetch 阶段分别按各自配置的耗时阈值记录慢日志。index.search.slowlog.level 是日志级别,不代表所有查询都会记录。它用于发现真实慢请求,是性能诊断的入口。

9. 最佳实践 ​

  • 生产环境开启慢查询日志,先定位真实慢请求
  • 用 _profile API 分析慢请求内部各阶段耗时,找出瓶颈
  • 对照优化前后的延迟分位数、吞吐量和资源消耗验证效果
  • 优先用能利用索引的原生查询替代脚本
  • 高频查询考虑使用 filter context 利用缓存
  • 监控查询延迟的 P99,而非平均值

相关优化手段的总览见索引优化。

Released under the MIT License.