ES 系列 #05:ES 查询最佳实践

发布于 2026-05-26 10:25 👁 6 次阅读
#性能#elasticsearch#best-practice#query

从字段类型选择、查询语法优化、翻页策略、结果集控制四个维度梳理 ES 查询性能最佳实践,并给出各类型查询的性能基准数据。

ES 系列ES 简介 · ES 的查询过程分析 · ES 高风险查询识别与规避 · ES 集群与分片配置最佳实践


目录

章节 说明
查询耗时拆解 了解耗时在哪里,才能有针对性优化
字段类型对查询的影响 keyword vs 数值,term vs range
查询语法优化 filter cache、避免危险查询
翻页策略 from+size、scroll、search_after
结果集控制 控制返回字段、控制 size
聚合查询优化 嵌套深度、terms size 控制
机器与集群配置 内存、CPU、预热缓存
查询性能基准参考 Term/Match/聚合 QPS 参考值

查询耗时拆解

一次 ES 查询的主要耗时分布: query then fetch

结论:ES 查询的主要耗时在数据节点。优化方向是减少数据节点需要遍历的数据量和计算量。


字段类型对查询的影响

keyword vs 数值类型的 term 查询

类型 term 查询性能 range 查询性能 适用场景
keyword ✅ 最优(倒排索引) ❌ 不适合 精确匹配、枚举值、ID
long / integer ❌ 性能差(BKD 树不适合精确查找) ✅ 最优(BKD 树) 范围查询、排序、聚合

⚠️ 数字类型做 term 查询性能非常差。ES 5.x 起相比 2.x 下降约 80%,CPU 利用率飙升 30%+。不需要范围查询时,一律用 keyword

// 错误:数字类型做 term 查询
{ "term": { "userId": 12345 }}

// 正确:精确匹配用 keyword 类型存储
{ "term": { "userId": "12345" }}

整值型 vs 范围型字段

整值型(double、long、integer、short、byte)
  → 底层 BKD 树,适合 range 查询

范围型(float_range、long_range、date_range、double_range)
  → 存储一个区间,用于"文档区间是否包含查询值"场景

查询语法优化

优先使用 filter(利用 Query Cache)

// 推荐:bool-filter,结果会被缓存
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" }},
        { "range": { "price": { "lte": 100 }}}
      ],
      "must": [
        { "match": { "title": "手机" }}
      ]
    }
  }
}

避免模糊查询或严格限制

查询类型 危险程度 替代方案
wildcard(前后通配 *词* 🔴 极危险 改用 match 分词查询
regexp(正则) 🔴 极危险 提前计算好存入字段
fuzzy 🟡 较危险 控制 fuzziness 参数
prefix 🟡 注意 词长不超过 32 字符

模糊查询的危险原因:wildcard、regexp、fuzzy 内部都需要构建有穷自动机(DFA),词越长、状态数越多,CPU 直接被打满,集群不可用。

// ❌ 危险:前后通配
{ "wildcard": { "name": { "value": "*手机*" }}}

// ✅ 安全:分词查询(性能好 1 倍以上)
{ "match": { "name": "手机" }}

// ✅ 也可以用 wildcard 字段类型(ES 7.9+),性能更好

避免深度嵌套查询

// ❌ 避免:nested 查询
{
  "query": {
    "nested": {
      "path": "comments",
      "query": { "term": { "comments.author": "alice" }}
    }
  }
}

避免 script 查询

// ❌ 避免:查询时用脚本计算
{
  "query": {
    "script": {
      "script": "doc['price'].value * 0.9 > 50"
    }
  }
}

// ✅ 提前计算好存入 ES
{ "range": { "discount_price": { "gt": 50 }}}

不要用 match_all 无条件查询

// ❌ 极度危险:全量扫描
{ "query": { "match_all": {} }, "sort": [{ "createTime": "desc" }]}

翻页策略

方案 适用场景 上限 特点
from + size 用户翻页(前 N 页) 10000 简单,但深翻页时内存消耗大
scroll 批量导出/遍历全量数据 无上限 有状态,不适合实时查询
search_after 实时翻页(无随机跳页需求) 无上限 无状态,高效,推荐

from + size 的问题

from=9000, size=100 → 每个分片返回 9100 条 → 协调节点汇总后取 100 条
分片数越多,内存开销越大,是引发 Full GC 的常见原因

规则from + size 不得超过 10000size 单次不超过 1000。

search_after 示例

// 第一页
{
  "size": 20,
  "query": { "match_all": {} },
  "sort": [{ "createTime": "desc" }, { "_id": "asc" }]
}

// 后续页:传入上一页最后一条的 sort 值
{
  "size": 20,
  "query": { "match_all": {} },
  "sort": [{ "createTime": "desc" }, { "_id": "asc" }],
  "search_after": [1704067200000, "doc_id_xyz"]
}

结果集控制

只返回需要的字段

{
  "_source": {
    "includes": ["id", "name", "price"],
    "excludes": ["*.description", "raw_content"]
  },
  "query": { "term": { "status": "active" }}
}

控制 terms/should 参数规模

// ❌ terms 参数过多
{ "terms": { "skuId": [1001, 1002, ..., 10000] }}

别名查询定期清理


聚合查询优化

嵌套深度控制

// ❌ 危险:三层以上嵌套聚合
{
  "aggs": {
    "by_date": {
      "terms": { "field": "date" },
      "aggs": {
        "by_supplier": {
          "terms": { "field": "supplierId" },
          "aggs": {
            "by_sku": {
              "terms": { "field": "skuId" },
              "aggs": { ... }  // 第四层,危险!
            }
          }
        }
      }
    }
  }
}

预计算优化

合理利用 index sorting

// 索引创建时配置排序,对按该字段排序的查询有显著加速
PUT /my_index
{
  "settings": {
    "index.sort.field": "createTime",
    "index.sort.order": "desc"
  }
}

机器与集群配置

JVM Heap 配置原则

机器内存 64G → JVM Heap 32G → OS Cache 32G

预热文件系统缓存

# elasticsearch.yml 或 index settings
index.store.preload: ["nvd", "dvd"]

分片均衡

PUT /my_index/_settings
{
  "index.routing.allocation.total_shards_per_node": 2
}

冷热数据隔离


查询性能基准参考

以下为索引 50 GB、文档数 2 亿+、字段数 8 个的简单查询压测值,单个数据节点。

机器类型 配置 Term QPS Term avg(ms) Match QPS Match avg(ms)
SSD(8C 16G) VM/物理机 ~1000 12-16 ~1000 11-13
高性能 SSD(16C 32G) - ~1000 15 ~2000 12

聚合查询(Bucket Agg / Matrix Agg):

机器类型 Bucket Agg QPS Matrix Agg QPS
SSD(8C 16G) ~1000 ~1500
高性能 SSD(16C 32G) ~1500 ~2000

结论


参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。

发表评论