java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Elasticsearch 查询

Elasticsearch 查询核心概念详解

作者:小虾米 ~

Elasticsearch是分布式搜索与分析引擎,核心围绕集群、索引和文档构建,底层基于 Lucene 实现 ,‌‌‌这篇文章给大家介绍Elasticsearch 查询核心概念,感兴趣的朋友一起看看吧

1. doc_values

概念

doc_values 是 Elasticsearch 在索引时(index time)构建的一种列式存储(column-oriented store)数据结构,存储在磁盘上。

它以文档 → 字段值的映射方式存储数据,但在物理层面是按列组织的,非常适合需要"遍历某个字段所有文档值"的操作。

工作原理

倒排索引(用于全文搜索):
  "apple" → [doc1, doc3, doc7]
  "banana" → [doc2, doc5]
doc_values(列式存储,用于聚合/排序):
  doc1 → "apple"
  doc2 → "banana"
  doc3 → "apple"

支持的字段类型

支持 doc_values不支持 doc_values
keywordtext
numeric(long/integer/float…)annotated_text
date
boolean
ip
geo_point

text 类型不支持 doc_values,因为 text 经过分词,原始值已碎片化。

使用场景

配置示例

PUT /my_index
{
  "mappings": {
    "properties": {
      "price": {
        "type": "double",
        "doc_values": true   // 默认为 true,可显式关闭
      },
      "description": {
        "type": "keyword",
        "doc_values": false  // 若不需要排序/聚合,关闭可节省磁盘
      }
    }
  }
}

性能特点

2. fielddata

概念

fielddata 是 Elasticsearch 针对 text 类型字段的一种**运行时(query time)**内存数据结构,用于支持对 text 字段进行排序和聚合。

由于 text 字段经过分词,无法直接使用 doc_values,因此 ES 在查询时将倒排索引"反转"成列式结构,加载到 JVM 堆内存中。

为什么默认禁用

启用方式

PUT /my_index/_mapping
{
  "properties": {
    "content": {
      "type": "text",
      "fielddata": true   // 慎用!明确知道自己在做什么时才开启
    }
  }
}

推荐替代方案

与其开启 text 的 fielddata,更推荐:

{
  "properties": {
    "title": {
      "type": "text",          // 用于全文搜索
      "fields": {
        "keyword": {
          "type": "keyword"    // 用于聚合/排序,利用 doc_values
        }
      }
    }
  }
}

查询时使用 title.keyword 进行聚合或排序。

fielddata 内存控制

# elasticsearch.yml
indices.fielddata.cache.size: 20%   # 限制 fielddata 缓存最大占堆比例

超出限制时,ES 会驱逐最久未使用的 fielddata 缓存。

3. doc_values vs fielddata 对比

维度doc_valuesfielddata
构建时机索引时(index time)查询时(query time)
存储位置磁盘(OS page cache 加速)JVM 堆内存
适用字段keyword、numeric、date 等text(不推荐)
默认状态默认开启默认关闭
内存影响几乎不占堆直接占用 JVM 堆
首次访问延迟低(已在磁盘)高(需运行时构建)
推荐程度✅ 推荐⚠️ 慎用

核心结论:能用 doc_values 就用 doc_values;text 字段需要聚合时,用 fields 多字段映射加一个 keyword 子字段。

4. global ordinals

概念

global ordinals 是 Elasticsearch 在 keyword 字段(以及开启 fielddata 的 text 字段)上建立的一种全局序号映射,用于加速 terms aggregation 等操作。

问题背景

Lucene 段(segment)级别存在局部序号(local ordinals):

segment 1: { "apple"→0, "banana"→1, "cherry"→2 }
segment 2: { "apple"→0, "date"→1 }

跨段做 terms aggregation 时,需要将各段的 local ordinals 映射到统一的全局序号,这就是 global ordinals 的作用:

global ordinals:
  "apple"  → 0
  "banana" → 1
  "cherry" → 2
  "date"   → 3

构建时机

配置示例

PUT /my_index
{
  "mappings": {
    "properties": {
      "category": {
        "type": "keyword",
        "eager_global_ordinals": true   // refresh 时立即构建,减少首次查询延迟
      }
    }
  }
}

性能影响

场景建议
高基数字段(cardinality 高,如 user_id)谨慎使用 eager,构建成本高
低基数字段(如 status、category)推荐 eager_global_ordinals
写多读少(如日志索引)不必 eager
读多写少(如维度表)eager_global_ordinals 收益显著

与 terms aggregation 的关系

Terms aggregation 的核心流程:

1. 利用 global ordinals 将每个文档的字段值映射为整数 ID
2. 用整数数组统计每个 ordinal 的文档数
3. 选出 top-N ordinals
4. 将 ordinal 反查回原始字符串值

使用整数运算替代字符串比较,大幅提升聚合性能。

5. collapse(字段折叠)

概念

collapse 是 Elasticsearch 的搜索去重功能,允许在搜索结果中按某个字段的值折叠(去重),每个唯一值只保留得分最高的文档。

类似于 SQL 中的 GROUP BY + 取最优记录,但底层实现完全不同。

基础用法

GET /my_index/_search
{
  "query": {
    "match": { "title": "elasticsearch" }
  },
  "collapse": {
    "field": "user_id"   // 按 user_id 折叠,每个用户只返回一条
  },
  "sort": [
    { "score": "desc" }  // 保留得分最高的文档
  ]
}

inner_hits:展开被折叠的文档

GET /my_index/_search
{
  "collapse": {
    "field": "user_id",
    "inner_hits": {
      "name": "top_posts",     // inner_hits 的名称
      "size": 3,               // 每个折叠组展开显示 3 条
      "sort": [{ "date": "desc" }]
    }
  }
}

返回结构:

{
  "hits": [
    {
      "_source": { "user_id": "u1", "title": "..." },
      "inner_hits": {
        "top_posts": {
          "hits": { "hits": [ ... ] }   // 该 user 的 top 3 文档
        }
      }
    }
  ]
}

多级 collapse(ES 7.x+)

{
  "collapse": {
    "field": "user_id",
    "inner_hits": {
      "name": "by_category",
      "collapse": { "field": "category" },   // 二级折叠
      "size": 5
    }
  }
}

约束与限制

限制项说明
字段要求折叠字段必须是 keyword、numeric 或 date 类型,且开启 doc_values
不影响聚合total_hits 统计的是折叠前的文档总数,聚合也在折叠前的文档集上执行
不支持 scrollcollapse 与 scroll API 不兼容
不支持 rescorecollapse 后不能再 rescore
分页行为from + size 针对折叠后的结果计数

典型应用场景

6. track_total_hits

概念

track_total_hits 控制 Elasticsearch 在搜索响应中如何统计命中总数(hits.total)。

背景

精确统计文档总数(如:共找到 1,234,567 条结果)在大数据集上代价很高,因为 ES 需要遍历所有匹配文档。Elasticsearch 7.0 起引入此参数来提供灵活控制。

参数取值

值类型含义
trueboolean精确统计,返回真实 total(relation: “eq”)
falseboolean完全不统计,total 返回 -1(性能最优)
N(正整数)integer当总数 ≤ N 时精确统计;超过 N 时返回 ≥ N 的近似值(relation: “gte”)

默认值:10000(ES 7.0+)

示例

GET /my_index/_search
{
  "track_total_hits": true,   // 精确统计所有文档
  "query": { "match_all": {} }
}

响应:

{
  "hits": {
    "total": {
      "value": 1234567,
      "relation": "eq"     // 精确值
    }
  }
}

使用默认值(10000)时,若超过 10000:

{
  "hits": {
    "total": {
      "value": 10000,
      "relation": "gte"    // 表示"至少 10000 条"
    }
  }
}

性能影响

track_total_hits=false      → 跳过 total 统计,最快
track_total_hits=10000      → 默认,超过阈值后停止精确计数
track_total_hits=true       → 全量遍历,数据量大时最慢

实践建议

场景推荐配置
只关心前几页结果,不显示"共 X 条"false 或较小的整数
显示"约 X 条"近似数即可默认 10000
需要精确分页总数(如后台管理)true
深度分页 + 大数据集用 search_after 替代传统分页,配合 false

综合对比速查表

概念阶段核心作用存储位置注意事项
doc_values索引时排序、聚合、脚本访问磁盘默认开启,text 不支持
fielddata查询时text 字段聚合/排序JVM 堆慎用,推荐用 keyword 子字段替代
global ordinalsRefresh 时 / 首次查询时加速 terms aggregation内存(Off-Heap)高基数字段谨慎开启 eager
collapse查询时搜索结果去重/折叠—字段需开启 doc_values
track_total_hits查询时控制 total 统计精度—大数据集避免设为 true

延伸阅读

到此这篇关于Elasticsearch 查询核心概念详解的文章就介绍到这了,更多相关Elasticsearch 查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文