eager_global_ordinals

编辑

什么是全局序号 (global ordinals)?

编辑

为了支持聚合和其他需要在每个文档的基础上查找字段值的操作,Elasticsearch 使用一种名为 doc values 的数据结构。诸如 keyword 之类的基于词项的字段类型,使用序号映射来存储它们的 doc values,以获得更紧凑的表示。此映射的工作原理是根据词项的字典顺序为每个词项分配一个递增的整数或序号。字段的 doc values 只存储每个文档的序号,而不是原始词项,并使用单独的查找结构在序号和词项之间进行转换。

在聚合期间使用时,序号可以大大提高性能。例如,terms 聚合仅依赖序号在分片级别将文档收集到桶中,然后在跨分片组合结果时将序号转换回其原始词项值。

每个索引段定义自己的序号映射,但聚合会跨整个分片收集数据。因此,为了能够在分片级操作(如聚合)中使用序号,Elasticsearch 创建了一个称为全局序号的统一映射。全局序号映射建立在段序号之上,其工作原理是维护从全局序号到每个段的局部序号的映射。

如果搜索包含以下任何组件,则使用全局序号:

  • keywordipflattened 字段进行某些桶聚合。这包括上面提到的 terms 聚合,以及 compositediversified_samplersignificant_terms
  • 需要在 fielddata 启用的 text 字段上的桶聚合。
  • 来自 join 字段的父文档和子文档的操作,包括 has_child 查询和 parent 聚合。

全局序号映射使用堆内存作为 字段数据缓存 的一部分。对高基数字段进行聚合会占用大量内存并触发 字段数据断路器

加载全局序号

编辑

必须先构建全局序号映射,然后才能在搜索期间使用序号。默认情况下,映射会在第一次需要全局序号时在搜索期间加载。如果你正在优化索引速度,这是正确的方法,但如果搜索性能是首要任务,建议在将在聚合中使用的字段上急切加载全局序号。

resp = client.indices.put_mapping(
    index="my-index-000001",
    properties={
        "tags": {
            "type": "keyword",
            "eager_global_ordinals": True
        }
    },
)
print(resp)
response = client.indices.put_mapping(
  index: 'my-index-000001',
  body: {
    properties: {
      tags: {
        type: 'keyword',
        eager_global_ordinals: true
      }
    }
  }
)
puts response
const response = await client.indices.putMapping({
  index: "my-index-000001",
  properties: {
    tags: {
      type: "keyword",
      eager_global_ordinals: true,
    },
  },
});
console.log(response);
PUT my-index-000001/_mapping
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": true
    }
  }
}

当启用 eager_global_ordinals 时,会在 刷新分片时构建全局序号 — Elasticsearch 始终会在将更改暴露给索引内容之前加载它们。这会将构建全局序号的成本从搜索转移到索引时。Elasticsearch 还会在创建分片的新副本时急切地构建全局序号,当增加副本数量或将分片重新定位到新节点时可能会发生这种情况。

可以通过更新 eager_global_ordinals 设置随时禁用急切加载。

resp = client.indices.put_mapping(
    index="my-index-000001",
    properties={
        "tags": {
            "type": "keyword",
            "eager_global_ordinals": False
        }
    },
)
print(resp)
response = client.indices.put_mapping(
  index: 'my-index-000001',
  body: {
    properties: {
      tags: {
        type: 'keyword',
        eager_global_ordinals: false
      }
    }
  }
)
puts response
const response = await client.indices.putMapping({
  index: "my-index-000001",
  properties: {
    tags: {
      type: "keyword",
      eager_global_ordinals: false,
    },
  },
});
console.log(response);
PUT my-index-000001/_mapping
{
  "properties": {
    "tags": {
      "type": "keyword",
      "eager_global_ordinals": false
    }
  }
}

避免加载全局序号

编辑

通常,全局序号在加载时间和内存使用方面不会带来很大的开销。但是,对于具有大型分片的索引,或者如果字段包含大量唯一词项值,则加载全局序号的成本可能会很高。由于全局序号为分片上的所有段提供统一的映射,因此当新段可见时,也需要完全重建它们。

在某些情况下,可以完全避免加载全局序号:

  • termssamplersignificant_terms 聚合支持一个参数 execution_hint,该参数有助于控制如何收集桶。它默认为 global_ordinals,但可以设置为 map 以改为直接使用词项值。
  • 如果已将分片 强制合并为一个段,那么它的段序号已经是该分片的全局序号。在这种情况下,Elasticsearch 不需要构建全局序号映射,并且使用全局序号不会产生额外的开销。请注意,出于性能原因,您应该仅对您永远不会再写入的索引进行强制合并。