Appearance
Elasticsearch 查询突然变慢时怎么定位
Elasticsearch 慢查询是线上非常典型的一类问题,因为业务看到的往往只是一个结果:
- 搜索结果半天出不来
- 聚合接口开始超时
- 应用线程堆满在 ES 调用上
- 首页、搜索页、日志检索页一起抖
这类故障最怕两种误判:
- 一上来就把节点加倍,却没看到慢的是某几类重查询
- 一上来只盯 ES 机器,却没看到问题其实来自写入高峰、索引膨胀或查询模型突变
先说结论
- ES 变慢时先判断是单类查询变慢,还是整个集群都在退化
- 第一目标是止血,限制最重的查询和最危险的时间范围,不要让慢请求把应用也拖死
- 第二目标是判断瓶颈在查询模型、字段设计、分片布局、写入高峰还是资源争抢
- 很多 ES 慢查询不是参数问题,而是深分页、高基数聚合、脚本查询和动态字段膨胀叠加出来的结果
一个典型故障现场
以一个商品搜索系统为例,工作日白天搜索接口 P95 一直在 80ms 左右,某天下午突然出现:
- 搜索页 RT 从 80ms 升到 1.5s
- 聚合筛选接口大量超时
- 应用侧线程池开始堆积
- ES 节点 CPU 飙高,但并不是所有节点都一样高
- 同时恰好有一批商品数据正在批量重建索引
这时如果只看“CPU 高了”,很容易遗漏真正关键的问题:
- 是查询变重了
- 还是写入和查询抢资源
- 是所有索引一起慢
- 还是少数大索引、热索引在拖垮整体
第一阶段:先止血,不要让慢查询拖跨应用
1. 先限制最危险的查询入口
如果你已经看到:
- 深分页请求很多
- 时间范围查询被放大到近 30 天或 90 天
- 某类筛选页带了很多高基数聚合
先在应用层或网关侧限一下:
- 最大分页深度
- 最大时间范围
- 复杂聚合开关
- 某些高成本排序
这一步的意义不是“治好 ES”,而是先把最贵的请求压下来。
2. 看是否要临时降级聚合或推荐模块
很多搜索页真正昂贵的不是主搜索本身,而是:
- 侧边栏聚合
- 推荐词
- 联想词
- 多维筛选统计
如果业务允许,先把这些附加能力降级,通常比盲目扩容更快见效。
3. 避免应用线程被 ES 调用一直占满
如果 ES 已经明显抖动,应用侧也要尽快启用:
- 更严格的超时
- 隔离线程池
- 熔断降级
- 缓存兜底
否则 ES 慢查询很快就会演变成应用整体不可用。
第二阶段:先判断是“局部慢”还是“整体退化”
这一步非常关键,因为两种场景的排查方向完全不同。
1. 如果只有少数接口变慢
更像是:
- 某类 DSL 最近变复杂了
- 某些索引 Mapping 出了问题
- 某几个字段聚合、排序特别重
- 某个新功能放开了深分页或脚本查询
2. 如果所有查询都在变慢
更像是:
- 集群资源整体紧张
- 写入和查询发生激烈竞争
- shard 过多或热点集中
- 磁盘、合并、刷新、GC 同时在抖
很多时候只要先分清这一步,定位速度就会快一大截。
第三阶段:看四组最关键的排查线索
1. 查询类型有没有明显变化
先看慢的是哪类请求:
match、bool这类普通搜索- 大范围聚合
- 深分页
- 排序
- 脚本查询
如果慢请求只集中在某一类,通常先从 DSL 和字段设计下手,而不是从集群层面大动干戈。
2. 索引和时间范围有没有放大
很多线上慢查询不是 DSL 写坏了,而是命中的范围突然变大:
- 原来查近 1 天,后来放开成近 30 天
- 原来只打一个索引别名,现在扫了一组滚动索引
- 原来按租户过滤,现在某次改版把过滤条件漏掉了
一旦检索范围放大,ES 的扫描和聚合成本会非常明显。
3. 写入侧是不是也在同一时间抖
如果恰好同时出现:
- 批量导入
- 索引重建
- refresh/merge 压力升高
- 副本同步和段合并增多
那就要警惕“查询慢并不是单独发生”,而是查询和写入正在争资源。
4. 热点是不是集中在少数 shard 或少数节点
如果只有个别节点持续很高,通常要看:
- 某些 shard 是否过热
- 某些索引是否集中落在少数节点
- 查询是否反复命中热点索引
这类问题用“整集群平均指标”很容易看不出来。
常见根因画像
1. 深分页和大时间范围一起出现
最典型,也最常见。管理后台、运营报表、日志检索页很容易把 ES 当数据库列表页来用。
2. 高基数字段做重聚合
比如对用户 ID、设备 ID、订单号这类字段做大范围聚合,再叠加排序和筛选,成本非常高。
3. Mapping 膨胀或动态字段失控
字段数量越来越多、嵌套层级越来越乱时,查询和聚合都会越来越重,写入成本也会上升。
4. 写入高峰和查询高峰重叠
批量导入、索引重建、热点更新和用户查询同时发生时,集群会明显抖动。
5. shard 过多或索引拆得过碎
很多系统不是单个索引太大,而是“小 shard 太多”,协调开销和管理开销一起上来。
一个更实用的排查顺序
线上 ES 变慢时,我通常按这个顺序判断:
- 先看是单类接口慢,还是整站检索都慢。
- 再看慢请求的 DSL 类型,是聚合、深分页、排序还是脚本。
- 看命中的索引范围、时间范围、租户范围有没有被放大。
- 看同一时间段写入、refresh、merge 是否也在上升。
- 看热点是否集中在少数节点、少数 shard。
- 最后再决定是先改查询、降范围、拆索引,还是临时扩资源。
这个顺序能避免一开始就把问题归因到“ES 不行了”。
止血后的治理方向
1. 给高成本查询设硬边界
包括:
- 最大时间范围
- 最大分页深度
- 高基数聚合白名单
- 脚本查询限制
2. 让索引模型更贴近查询路径
字段该用 keyword 还是 text,该不该支持聚合、排序、多字段,要提前规划。
3. 把写入高峰和查询高峰尽量错开
批量导入、重建索引、补数据这类动作,最好别和核心检索高峰撞在一起。
4. 做慢查询基线和热点索引监控
不要等接口超时了才知道 ES 有问题,最好平时就能看到:
- 哪类查询最贵
- 哪些索引最热
- 哪些节点最偏
总结
Elasticsearch 慢查询的关键,不是看到 RT 变高就先扩容,而是先判断:
- 是哪些请求变慢了
- 范围是不是被放大了
- 写入是不是也在抢资源
- 热点是不是集中在局部
把“查询模型、索引范围、写入竞争、热点分布”这四条线理顺后,ES 慢查询问题通常都会从模糊的性能波动,变成可定位、可止血、可治理的工程问题。