数据库
Elasticsearch Query DSL…
当 Elasticsearch 的 Query DSL 搜索变慢、profile 显示某个子句开销过大、精确匹配过滤条件被放在评分上下文、或前导通配符主导延迟时,用 profiling 定位并改写查询。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
Elasticsearch Query 特定
解决思路
- 确认连通性并定位目标索引:调用 GET /;若失败则停止,不要猜测端点或凭据;当用户给出索引模式(如 logs-*)时,用 GET /_cat/indices 缩小候选范围,选出查询实际指向的索引或模式;只有在索引已知时才继续,需要索引名或模式以及慢查询的 Query DSL 请求体。
- 对慢查询做 profiling 找出主要开销:用 POST /{index}/_search 并带上 "profile": true,保持用户查询不变;读取 took,然后检查 profile.shards[].searches[].query,按 time_in_nanos 对子收集器排序,找出最大贡献者;根据 profile 证据分类瓶颈:must 中与评分子句并列的 TermQuery/PointRangeQuery/MatchNoDocsQuery 说明精确匹配或范围过滤被不必要地评分(改到 filter 上下文);带前导 * 的 WildcardQuery(如 message:*timeout*)无法使用倒排索引、逐文档扫描词项(去掉前导通配符);text 字段上的 MatchQuery 是预期的评分开销,只有在 filter 上下文修复后仍占主导时才优化;高 aggregation 时间属于聚合调优,除非用户明确询问否则不在范围内;需要包含 type、description、time_in_nanos 和 breakdown(尤其是通配符的 next_doc)的 profile 树,解释诊断时逐字引用最大贡献者。
- 改写前先检查字段映射:调用 GET /{index}/_mapping,对每个将要移动或改写的子句确认字段类型;对精确值使用 term/terms/filter 时字段必须是 keyword(或其他非分析类型),在 text 字段上用 term 是常见 bug,若类型不对要指出并建议正确的子字段(如 service.keyword)或修改映射,不要静默改写;match/match_phrase 应作用于 text(分析)字段;wildcard 可用于 keyword 或 wildcard 类型,但无论类型如何,前导 * 仍会强制扫描;只提出与已确认类型匹配的改写,需要查询中每个字段的映射。
- 改写查询以消除 profile 出的瓶颈。4a. 将非评分子句从 must 移到 filter:当精确匹配的 term/terms/range/match(作用于 keyword 或其他非评分意图)与应驱动相关性的全文 match 并列在 must 中时,把精确匹配子句移入 bool.filter(或 filter 数组项),只在 bool.must 中保留必须影响 _score 的子句(通常是全文 match);原因是 filter 上下文跳过评分并在重复查询时参与 filter/bitset 缓存;语义上匹配的文档相同,只有评分和性能变化,要明确说明这一点。示例改写模式:{"query":{"bool":{"filter":[{"term":{"status":"active"}},{"term":{"tenant_id":"acme"}}],"must":[{"match":{"description":"wireless keyboard"}}]}}}。4b. 消除前导通配符:当 profile 显示 WildcardQuery 的 description 类似 message:*timeout* 且 next_doc 时间很高时,前导 * 阻止索引查找;根据映射和用户意图(子串 vs 前缀 vs 精确)选择修复:日志中的全文子串用分析过的 message text 字段上的 match 或 match_phrase;keyword 上的字面子串用 wildcard 类型字段或重建索引时使用 ngram 分析器;仅前缀(timeout*)用 keyword 上的 prefix 查询或索引时 edge ngram;同时把任何非评分精确匹配(如 keyword 上的 {"match":{"service":"checkout"}})移入 filter,映射确认后在 keyword 字段上用 term。示例改写模式:{"query":{"bool":{"filter":[{"term":{"service.keyword":"checkout"}}],"must":[{"match":{"message":"timeout"}}]}}};根据第 3 步的映射调整字段名(service vs service.keyword)。4c. 可选——在 profiling 前验证改写:当语义不确定时(例如把 wildcard 改成 match 可能包含 wildcard 排除的分析词项),用改写后的查询调用 POST /{index}/_validate/query?explain=true 并阅读解释以发现明显不匹配;选择能解决 profile 开销的最小改写,需要改写后的 Query DSL 请求体。
- 对改写后的查询重新 profiling 并比较:再次用 "profile": true 和改写后的查询调用 POST /{index}/_search,将 took 和最大 profile 收集器与第 2 步的基线比较;只有当主要收集器发生变化或 time_in_nanos 显著下降时才报告成功;如果 profile 仍显示前导通配符或被评分的过滤条件,就继续迭代——不要仅凭 took 就宣布胜利而没有 profile 确认;需要前后 profile 摘要(最大收集器的 type、description、time_in_nanos)。
- 按以下顺序报告发现:1. 根因——引用 profile(例如 "WildcardQuery message:*timeout* ≈ 3.8s,主要是 next_doc");2. 改写——展示优化后的 bool 结构,区分 filter 与 must;3. 映射说明——来自 GET /{index}/_mapping 的 keyword vs text 确认;4. 实测改进——第 5 步的前后 profile 或 took;5. 语义注意事项——仅当改写可能改变匹配的文档时(例如 match vs 子串 wildcard)。
- 遵循准则:profile 优先,如果用户提供了 profile 摘要就使用它,但改动后仍建议重新 profiling;filter 用于相等性,must 用于相关性,当文本查询驱动排序时,状态、租户 ID、服务名和时间范围很少应放在 must 中;前导通配符几乎从不是日志搜索的正确修复,优先使用分析过的 match/match_phrase,仅在 keyword 或 wildcard 类型字段上为后缀模式(timeout*)保留 wildcard;不要把慢与错误混为一谈,慢查询可能返回正确结果,优化会保留结果集,除非你明确警告语义权衡;深入参考 profile 收集器类型、filter 缓存行为和通配符替代方案见 references/query-optimization-reference.md。
适用边界 · 注意事项
- 不适用于将查询迁移到 ES|QL——本技能只优化用户已有的 bool/match/term/wildcard 结构。
- 当 profile 已明确指出具体子句(如 WildcardQuery 约 3.8s)时,不要把"增加分片"或"扩容硬件"作为主要修复;基础设施变更需要证据表明查询已经最优。
- 高 aggregation 时间属于聚合调优,除非用户明确询问聚合,否则不在本技能范围内。