Elasticsearch 查询核心概念

Elasticsearch 查询核心概念

目录

  1. doc_values
  2. fielddata
  3. [doc_values vs fielddata 对比](#doc_values vs fielddata 对比)
  4. [global ordinals](#global ordinals)
  5. collapse(字段折叠)
  6. track_total_hits

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
keyword text
numeric(long/integer/float...) annotated_text
date
boolean
ip
geo_point

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

使用场景

  • 排序(sort):按某字段排序文档
  • 聚合(aggregations):terms agg、range agg、avg/sum 等
  • 脚本访问(script) :在脚本中通过 doc['field'].value 访问

配置示例

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

性能特点

  • 磁盘存储:持久化到磁盘,OS 缓存负责热数据加速
  • 堆内存友好:不占用 JVM 堆,适合大数据量场景
  • 索引速度略降:需要在写入时额外构建列式结构

2. fielddata

概念

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

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

为什么默认禁用

  • 对 text 字段做聚合/排序通常语义错误(你可能想要的是原始值而非分词后的 token)
  • 首次加载时需要全量扫描分片,延迟极高
  • 驻留在 JVM 堆,内存压力大,可能触发 OOM 或 GC 抖动

启用方式

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

推荐替代方案

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

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

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

fielddata 内存控制

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

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


3. doc_values vs fielddata 对比

维度 doc_values fielddata
构建时机 索引时(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

构建时机

  • 默认(lazy 加载):第一次对该字段执行 terms aggregation 时,ES 构建 global ordinals,并缓存在内存中(堆外,使用 Lucene 的 Off-Heap 结构)
  • eager_global_ordinals:可配置为在 refresh 时立即构建,适合高频聚合场景

配置示例

json 复制代码
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 + 取最优记录,但底层实现完全不同。

基础用法

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

inner_hits:展开被折叠的文档

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

返回结构:

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

多级 collapse(ES 7.x+)

json 复制代码
{
  "collapse": {
    "field": "user_id",
    "inner_hits": {
      "name": "by_category",
      "collapse": { "field": "category" },   // 二级折叠
      "size": 5
    }
  }
}

约束与限制

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

典型应用场景

  • 商品搜索:同一 SPU 下多个 SKU,只展示最相关的一个
  • 新闻聚合:同一话题/来源只展示一篇
  • 用户行为:同一用户的多条日志只展示最新/最高分一条

6. track_total_hits

概念

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

背景

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

参数取值

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

默认值10000(ES 7.0+)

示例

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

响应:

json 复制代码
{
  "hits": {
    "total": {
      "value": 1234567,
      "relation": "eq"     // 精确值
    }
  }
}

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

json 复制代码
{
  "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 ordinals Refresh 时 / 首次查询时 加速 terms aggregation 内存(Off-Heap) 高基数字段谨慎开启 eager
collapse 查询时 搜索结果去重/折叠 --- 字段需开启 doc_values
track_total_hits 查询时 控制 total 统计精度 --- 大数据集避免设为 true

延伸阅读

相关推荐
Elastic 中国社区官方博客1 天前
将 Vercel 数据导入 Elastic:无需安装任何东西的无服务器可观测性
大数据·运维·elasticsearch·搜索引擎·云原生·serverless·全文检索
凤山老林3 天前
Spring Boot + OpenSearch 实战:搞定全文检索、向量召回与混合排序
spring boot·后端·全文检索·向量·opensearch·全文索引
醉颜凉4 天前
RedisSearch 全文检索:中文分词、索引构建与搜索结果排序
全文检索·中文分词·索引构建·搜索排序·redissearch
天天喝旺仔4 天前
Elasticsearch 全文检索实战:Lucene 倒排索引与 NoSQL 文档检索落地
elasticsearch·搜索引擎·全文检索·nosql·lucene
Elastic 中国社区官方博客6 天前
Elasticsearch 向量数据库:几分钟内完成部署,以经济高效的方式扩展至数千亿规模
大数据·运维·数据库·elasticsearch·搜索引擎·ai·全文检索
Elastic 中国社区官方博客7 天前
Elasticsearch Python DSL 客户端开发
大数据·数据库·python·elasticsearch·搜索引擎·全文检索
HwJack207 天前
HarmonyOS开发全文检索 FTS 小实战:中文分词与聊天记录搜索
全文检索·中文分词·harmonyos
Elastic 中国社区官方博客8 天前
教程:使用 ES|QL 进行威胁狩猎
大数据·运维·数据库·elasticsearch·搜索引擎·全文检索·安全威胁分析
Elastic 中国社区官方博客8 天前
隐藏在可观测性数据中的安全攻击
大数据·网络·安全·elasticsearch·搜索引擎·全文检索