---
title: "查询调优与慢查询排查"
date: 0001-01-01
description: "从慢查询样本出发，系统地排查 mapping、DSL 结构与资源瓶颈，附常见反模式清单。"
summary: "查询调优与慢查询排查 #  这篇不是“参数大全”，而是一个从症状出发的调优路线：给你一套在遇到“搜索慢/结果怪”时可以照着走的步骤，并总结几类典型反模式。
1. 先确认：是“慢”，还是“查不准”？ #  调优之前先判断你面对的是哪类问题：
 性能问题：延迟高、QPS 上不去、偶发长尾 相关性问题：该上的内容没上来、不该上的跑前面 资源问题：CPU/内存/磁盘/网络被拖垮  很多时候，这三类是纠缠在一起的，但你要先选一个“主目标”：是要先跑得稳，还是先查得准。
2. 性能调优：从慢查询样本开始 #  2.1 收集慢查询样本 #   打开搜索 slowlog（按索引粒度）：  记录超过某个阈值的查询（P95/P99 级别）   从 slowlog 和客户端日志里挑出：  最常出现的慢查询模式 最耗资源的异常查询（比如 fan-out 到海量分片）    2.2 用 profile 看“时间花在哪” #  对代表性查询加上一层 profile: true，观察：
 是哪一部分耗时最长：  filter 还是 query？ 排序/聚合 是否拖慢了整体？   是否在某些字段上做了昂贵操作：  对高基数字段做排序/聚合 对海量分片做全量 scan    2."
---


# 查询调优与慢查询排查

这篇不是“参数大全”，而是一个**从症状出发的调优路线**：给你一套在遇到“搜索慢/结果怪”时可以照着走的步骤，并总结几类典型反模式。

## 1. 先确认：是“慢”，还是“查不准”？

调优之前先判断你面对的是哪类问题：

- **性能问题**：延迟高、QPS 上不去、偶发长尾
- **相关性问题**：该上的内容没上来、不该上的跑前面
- **资源问题**：CPU/内存/磁盘/网络被拖垮

很多时候，这三类是纠缠在一起的，但你要先选一个“主目标”：是要**先跑得稳**，还是**先查得准**。

## 2. 性能调优：从慢查询样本开始

### 2.1 收集慢查询样本

1. 打开搜索 slowlog（按索引粒度）：  
   - 记录超过某个阈值的查询（P95/P99 级别）
2. 从 slowlog 和客户端日志里挑出：  
   - **最常出现**的慢查询模式  
   - **最耗资源**的异常查询（比如 fan-out 到海量分片）

### 2.2 用 profile 看“时间花在哪”

对代表性查询加上一层 `profile: true`，观察：

- 是哪一部分耗时最长：
  - filter 还是 query？
  - 排序/聚合 是否拖慢了整体？
- 是否在某些字段上做了昂贵操作：
  - 对高基数字段做排序/聚合
  - 对海量分片做全量 scan

### 2.3 常见可落地的改造方向

- **把“硬过滤”移到 filter：**
  - 所有业务强约束（租户、状态、时间范围）尽量放到 `bool.filter`，提升缓存命中率。
- **减少 fan-out：**
  - 合理设置索引边界，避免一个查询打到过多分片。
  - 使用 `preference` / routing 让请求更集中。
- **优化排序/聚合字段选择：**
  - 避免对高基数字段做深度排序/嵌套聚合。
  - 观察是否可以用预计算/冗余字段替代复杂 script。

## 3. 相关性调优：从 explain/_score 入手

### 3.1 用 explain 看“一条结果为什么得这个分”

对单条代表性命中文档使用 `explain`：

- 哪些子查询命中了？
- 每个子查询贡献了多少 `_score`？
- 某些不该影响排序的条件是否参与了打分？

结合 `bool` 结构：

- `must`/`should`：负责“贡献分数”
- `filter`：只负责“能不能进候选集”

如果发现很多“硬条件”也在 must 里，就可以考虑迁移到 filter。

### 3.2 优先做“结构级别”的调整

在调参数前，先检查：

- mapping 是否合理（text/keyword 分工、多字段设计）
- 是否使用了合适的查询类型：
  - 精确字段用 `term/terms` 而不是 `match`
  - 自然语言字段用 `match/multi_match` 而不是 `term`

然后再考虑：

- `minimum_should_match`：控制“至少命中多少词”
- `boost` / 字段权重：提升标题、重要字段的影响力
- `function_score`：叠加业务信号（热度、点击、时间衰减等）

## 4. 调优 Checklist：查询结构与 DSL 写法

这一段可以当成写 DSL 时的“自查表”：

- **bool 结构：**
  - 是否清晰区分了 must / should / filter / must_not？
  - 是否有过度嵌套的 bool，可以简化为单层？
- **filter 使用：**
  - 所有“硬约束”（租户、权限、状态、时间范围）是否都放在 filter？
  - 是否有可以被缓存的条件混在 query 里？
- **字段与查询类型匹配：**
  - text 字段是否主要用 `match` 系列？
  - keyword/数值/日期字段是否主要用 `term`/`range`/聚合？
- **分页与 from/size：**
  - 是否在做非常深的分页（`from` 很大）？  
    → 可以考虑 `search_after` 或滚动扫描（scroll/point-in-time）。

## 5. 典型反模式清单（看到就该警觉）

- **在 text 字段上大量使用通配符/正则（`*foo*`/`.*foo.*`）：**
  - 词典扫描开销巨大，极易拖垮节点。
  - 建议：只在极少量字段上、配合足够长的前缀使用；更推荐索引时用 n-grams 方案。

- **到处是 script_score，且逻辑复杂：**
  - script 在评分阶段执行，很难缓存，CPU 压力大。
  - 建议：能用字段 + function_score 的地方尽量不用 script_score，把重逻辑前移到索引/预计算。

- **深分页（from/size 非常大）还要求排序稳定：**
  - 代价：每个分片都要维护超大的优先队列，内存/CPU 消耗非常高。
  - 建议：用 `search_after`，让翻页基于上一页的 sort 值，而不是偏移量。

- **query 与 filter 乱用：**
  - 例如把权限条件也放到 must 里，既影响排序又浪费分数计算。
  - 建议：权限/租户/状态/固定标签等，都放在 filter。

- **单个请求打所有索引/所有分片：**
  - 常见于 UI 默认为 `_all` 或 `*`，且没有任何 filter。
  - 建议：为不同业务提供有边界的索引/alias，避免“全库扫描式”的查询。

## 6. 和运维侧协同：不要只盯着 DSL

查询调优不只在 DSL 层面，和运维侧配合也很重要：

- 是否有足够的硬件支撑当前查询模式？
- 是否需要拆读写集群，或为重查询业务建专用索引/集群？
- 是否在监控中设置了合适的延迟/错误告警阈值？

## 7. 小结与延伸阅读

- 慢查询调优从 **样本** 开始：slowlog → profile → explain
- 先改“结构与 mapping”，再改“参数与权重”
- bool/filter 结构、字段类型、查询类型是否匹配，是最常见的突破口
- 谨慎使用 wildcard/regexp/script_score/深分页等高成本特性

建议继续阅读：

- [监控](../operations/monitoring.md)
- [故障排查](../operations/troubleshooting.md)
- [相关性与评分调优](../features/fulltext-search/relevance/_index.md)

## 参考手册（API 与参数）

遇到需要查具体字段与参数时，可以从这些 Reference 页面切入：

- [查询与过滤上下文]({{< relref "/docs/features/query-dsl/query-filter-context.md" >}})
- [精确查询与全文检索的对比]({{< relref "/docs/features/query-dsl/term-based-query/_index.md" >}})
- [最小匹配（minimum_should_match）]({{< relref "/docs/features/query-dsl/minimum-should-match.md" >}})
- [Term 查询家族索引页]({{< relref "/docs/features/query-dsl/term-based-query/_index.md" >}})
- [全文查询索引页]({{< relref "/docs/features/fulltext-search/full-text/_index.md" >}})

