Elasticsearch `_search` API Query DSL、性能开关与实战范式
1. _search
是什么?
_search
用来在 索引/数据流 上执行查询与聚合,请求体用 Query DSL(JSON)编写。返回值里 hits.hits
是前 N 条结果(默认 10 条)。(Elastic)
最小示例:
GET /my-index-000001/_search
{"query": {"match": { "user.id": "kimchy" }}
}
这会按相关性(_score
)返回 top10。(Elastic)
2. Query DSL 基础与组合
- 全文查询:
match
/multi_match
面向分析后的文本; - 词项级(精确)查询:
term
/terms
/range
适合过滤; - 布尔组合:
bool
的must
/should
/filter
/must_not
;filter
不计分、可缓存,适合“先过滤后打分”。(Elastic)
3. 常用能力清单(带实用片段)
3.1 同时搜多个索引/数据流
支持 逗号分隔 与 通配:
GET /logs-2025.08-*,metrics-*/_search
也可以对某些索引加权(indices_boost
)。(Elastic)
3.2 只取需要的字段
默认每条命中都会返回完整 _source
。若只需少量字段,建议用 _source
include 或 fields
:(Elastic)
{"_source": ["@timestamp","message","user.id"],"fields": ["computed_runtime_field"] // 运行时字段用 fields 取值
}
3.3 排序与打分
默认按 _score
。要按业务字段排序,用 sort
;需要自定义相关性,可用 script_score
(注意性能)。(Elastic)
3.4 分页三件套
from
/size
:简单但大页会慢;search_after
:深分页推荐,基于上页最后一条的 sort 值;scroll
:离线批量遍历用,实时搜索不建议。(Elastic)
3.5 运行时字段:查询里“临时造字段”
不改 mapping,也能在查询期临时定义字段并参与聚合/过滤:(Elastic)
GET /my-index/_search
{"runtime_mappings": {"day_of_week": {"type": "keyword","script": {"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ENGLISH))"}}},"aggs": {"by_dow": { "terms": { "field": "day_of_week" } }}
}
3.6 超时与取消
timeout
:给每个分片一个收集时间上限;到期用已收集到的命中返回(适合低延迟优先)。(Elastic)- 取消:HTTP 连接断开或通过 Task Management 取消任务。(Elastic)
3.7 track_total_hits
:命中数精度 vs 性能
- 默认精确到 10,000,超过则返回“下界”(
relation: "gte"
); - 设为
true
得到精确总数(禁用 MaxWAND,更慢); - 也可设整数阈值(如
100
),精确计到该阈值即可。(Elastic)
{ "track_total_hits": 100, "query": { "match": { "user.id": "elkbee" } } }
3.8 只想知道“有没有命中”
最快路径:size: 0
+ (可选) terminate_after: 1
(每分片找到第一条就停)。注意 terminate_after
有使用注意,尽量让 ES 自动提前终止;跨多温层数据流时不要用。(Elastic)
GET /_search?q=user.id:elkbee&size=0&terminate_after=1
3.9 异步搜索:长查询的标配
大数据量或跨多集群用 Async Search:先提交、拿到 id
,随后轮询获取阶段性/最终结果。可用 wait_for_completion_timeout
把“提交与等待”合成一步。(Elastic)
POST /my-index-*/_async_search?wait_for_completion_timeout=2s&keep_alive=10m
{ "query": { "range": { "@timestamp": { "gte": "now-1h" } } } }GET /_async_search/<id>
4. “一眼看懂”选型与性能开关
目标 | 推荐做法 |
---|---|
高频在线检索 | bool + filter 先过滤后打分;只取需要字段;限制 from+size |
深分页 | 使用 search_after ,避免 from 很大 |
实时聚合 | 尽量聚合小范围数据;必要时用 timeout 防长尾 |
要精确总数 | track_total_hits: true (注意性能)或设一个合理上限值 |
仅判断是否存在 | size: 0 +(可选)terminate_after |
查询期造临时字段 | runtime_mappings + fields 取值 |
长耗时查询 | _async_search + keep_alive ,前台不阻塞 |
参考项见官方 _search
、Query DSL、runtime fields、Async Search 文档。(Elastic)
5. 实战范式:日志检索(含聚合与分页)
GET /logs-2025.08-*/_search
{"track_total_hits": 1000,"_source": ["@timestamp","service.name","message","trace.id"],"query": {"bool": {"filter": [{ "range": { "@timestamp": { "gte": "now-15m" } } },{ "term": { "service.env": "prod" } }],"must": [{ "simple_query_string": { "query": "\"timeout\" | \"latency\"", "fields": ["message^2","error.message"] } }]}},"aggs": {"top_services": { "terms": { "field": "service.name", "size": 10 } }},"sort": [{ "@timestamp": "desc" }],"size": 50
}
- 过滤放
filter
,全文匹配放must
; - 既要列表又要统计,用聚合;
- 需要深翻页时,把
sort
的最后一条带回做search_after
。(Elastic)
6. 常见坑位与排雷
- 把过滤写成打分:
must
的term/range
也会参与评分,推荐放filter
。(Elastic) - 过度追求精确总数:
track_total_hits: true
会牺牲性能,评估业务是否真的需要。(Elastic) terminate_after
滥用:官方建议谨慎,避免多层数据流/大范围;让 ES 自行早停更稳。(Elastic)- 大页
from
:越往后越慢;用search_after
。(Elastic) - 长查询阻塞前端:改用
_async_search
,并合理设置keep_alive
、wait_for_completion_timeout
。(Elastic)
7. 收尾
把 _search
玩明白,关键是三件事:写好 Query DSL、只拿必要数据、用对性能开关。线上一旦出现慢查/长尾,用 timeout
或转 _async_search
,同时从过滤建模、索引/分片设计与缓存命中率几路并进优化。需要的话,我可以根据你的业务字段给一份 查询模板 + 指标看板(took/timeout/命中率) 的落地清单。