深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之列存(二)

Elasticsearch,作为当下最流行的开源搜索和分析引擎之一,其内部的数据结构和存储机制对于确保高效搜索和快速响应查询至关重要。在这其中,列存储(Doc Values)扮演了一个核心角色。本文将深入探讨Doc Values是什么、它们如何工作,以及为什么它们对Elasticsearch的性能如此关键。

深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)

什么是 Doc Values

Doc Values 是 Elasticsearch 中的一个内部数据结构,用于在字段级别存储排序和聚合所需的数据。与传统的行存储(将文档的每个字段值作为文档的一部分存储)不同,Doc Values 采用列式存储,这意味着它们按字段组织数据,而不是按文档。这种结构优化了读取性能,特别是当执行排序、聚合或脚本计算等操作时。

为什么需要Doc Values

在Elasticsearch中,排序和聚合操作对于处理和分析大量数据至关重要。然而,传统的倒排索引,尽管在全文检索时表现出色,但在执行这些操作时却显得力不从心。这是因为倒排索引是为快速查找包含特定词项的文档而设计的,而不是为收集特定文档集中的所有词项而优化的。

当我们对某个字段进行排序或聚合时,Elasticsearch需要访问每个匹配到的文档,以获取该字段的值。例如,考虑一个倒排索引,其中列出了不同词项及其所在的文档:

Term      | Doc_1 | Doc_2 | Doc_3  
------------------------------------  
brown     |   X   |   X   |  
dog       |   X   |       |   X
  • 在这个结构中,快速找到包含“brown”的文档(Doc_1和Doc_2)是很容易的,因为倒排索引直接映射了词项到文档的关系。但是,如果我们想进一步分析这些文档,比如找出这些文档中所有唯一的词项,以便进行聚合,倒排索引就不那么高效了。

  • 问题在于,为了使用倒排索引收集Doc_1和Doc_2中的所有词项,我们必须遍历索引中的每个词项,检查它是否属于这两个文档。这个过程随着词项和文档数量的增加而变得越来越慢,因为每次检查都需要在倒排列表中进行查找。

  • 为了解决这个问题,Elasticsearch引入了Doc Values。Doc Values是一种列式的数据结构,它存储了每个文档字段值的完整、排序好的列表。与倒排索引不同,Doc Values不是将词项映射到文档,而是将文档映射到它们所包含的词项。这种结构使得按文档收集字段值变得非常高效。

以下是Doc Values如何表示数据的一个示例:

Doc      | Terms  
-----------------------------------------------------------------  
Doc_1    | brown, dog, fox, jumped, lazy, over, quick, the  
Doc_2    | brown, dogs, foxes, in, lazy, leap, over, quick, summer

在这个Doc Values结构中,每个文档都有一个与之直接关联的词项列表。因此,当需要收集Doc_1和Doc_2中所有唯一的词项时,我们只需直接访问这两个文档的词项列表,并执行集合的并集操作。这比使用倒排索引要快得多,因为无需遍历整个索引来收集特定文档的词项。

Doc Values 的工作原理

在 Elasticsearch 中,当索引一个文档时,除了将字段值存储在倒排索引中以支持全文搜索外,还会为需要排序或聚合的字段生成 Doc Values。这些 Doc Values 是字段值的压缩、列式表示,它们与倒排索引分开存储,并且针对快速、随机访问进行了优化。

Doc Values 的关键优势在于它们能够直接提供字段值,而无需重新解析存储的原始 JSON 文档(通常存储在 _source 字段中)。这是因为 Doc Values 是在索引时预先计算和存储的,因此它们可以非常快速地加载到内存中,并直接用于排序和聚合操作。以下是 Doc Values 的工作原理的详细解释:

  • 数据生成与存储
    当文档被索引到 Elasticsearch 时,除了生成倒排索引外,还会为文档的每个字段生成 Doc Values。这些 Doc Values 是字段值的列式存储,按文档顺序排列。
    对于不需要进行全文检索的字段(如数字、日期或枚举类型),Doc Values 通常是这些字段值的直接存储形式。而对于需要进行全文检索的文本字段,Doc Values 可能会存储该字段的一个或多个分词结果。
  • 内存与磁盘使用
    Doc Values 通常被序列化到磁盘上,以节省 JVM 堆内存的使用。由于它们是按列存储的,因此可以高效地加载到操作系统的文件系统缓存中(OS cache)。
    当执行排序或聚合操作时,Elasticsearch 会尽可能地从 OS cache 中读取 Doc Values,从而减少对磁盘的直接 I/O 操作,提高性能。
  • 查询过程
    当执行排序或聚合查询时,Elasticsearch 需要收集特定文档集中的字段值。使用 Doc Values,它可以直接访问这些文档的字段值列表,而无需遍历整个倒排索引。
    Doc Values 的列式存储结构使得对这些值的操作(如排序、去重或聚合计算)非常高效,因为它们已经按文档顺序排列好了。
  • 性能优化
    由于 Doc Values 是为快速读取而设计的,它们通常比从倒排索引中收集字段值要快得多。这是因为倒排索引是为快速查找文档而优化的,而不是为收集字段值而优化的。
    Elasticsearch 还利用 Doc Values 来执行某些类型的过滤操作,如地理位置过滤,因为这些操作需要快速访问文档的字段值。
  • 与倒排索引的关系
    Doc Values 并不是要替代倒排索引,而是作为其补充。倒排索引仍然用于全文检索和快速查找包含特定词项的文档。

Doc Values 和倒排索引一起工作,使得 Elasticsearch 能够在处理大量数据时提供高效的检索、排序和聚合功能。
通过了解 Doc Values 的工作原理,可以更好地理解 Elasticsearch 如何优化排序和聚合操作,并在实际应用中更有效地使用这些功能。

Doc Values 的类型及存储

es 支持多种类型的 Doc Values,包括数字、日期、IP 地址和二进制等。每种类型都有其特定的编码方式,以优化存储空间和查询性能。例如,数字类型的 Doc Values 可能会使用高效的压缩算法来减少存储空间,而日期类型的 Doc Values 则可能会存储为可快速比较的长整型时间戳。

持久化(Persistence)

Doc Values 是在索引文档时与倒排索引一同生成。Doc Values 基于每个段(per-segment)且是不可变的,这意味着一旦创建,它们就不会再改变。为了高效地存储和访问这些数据,Doc Values 会被序列化并持久化到磁盘上。

这样做的好处是可以充分利用操作系统的内存,而不是仅仅局限于 JVM 的 Heap 内存。当工作集(working set)远小于系统的可用内存时,系统会自动将 Doc Values 加载到内存中,使其读写操作非常快速。然而,当工作集远大于可用内存时,操作系统会根据需要将 Doc Values 从磁盘读取到内存中,或者将其写入磁盘。这种方式确保了 Doc Values 的大小不再受到服务器内存的限制,同时也避免了因为 JVM Heap 内存不足而导致的程序崩溃问题。

然而,需要注意的是,当工作集所需的内存空间非常大时,Doc Values 可能会被操作系统从内存中置换出去,这可能会导致访问速度的降低。

压缩(Compression)
Doc Values 的列式存储结构非常适合进行压缩,尤其是数字类型的字段。这种压缩不仅可以减少磁盘空间的使用,还可以提高数据的访问速度。Doc Values 在压缩过程中会使用多种策略,例如:

  • 如果所有数值都各不相同(或缺失),它会设置一个标记并记录这些值。
  • 如果这些值小于 256,它将使用一个简单的编码表。
  • 如果这些值大于 256,它会检测是否存在一个最大公约数,这有助于进一步压缩数据。例如,如果所有数字都是 100 的倍数,那么可以通过除以 100 来减小数值的大小,从而减少存储所需的位数。
  • 如果没有最大公约数,它会从最小的数值开始,统一计算偏移量进行编码。
  • 对于字符串类型的字段,Doc Values 也可以通过顺序表对字符串进行数字编码,然后再对数字类型构建 Doc Values。这种方式间接地支持了字符串类型的压缩。

综上所述,Doc Values 的持久化机制确保了其可以灵活地处理不同大小的工作集,而压缩机制则有助于减少存储空间的占用并提高数据访问的效率。这些特性共同使得 Doc Values 成为 Elasticsearch 中优化排序和聚合操作性能的关键组件。

使用 Doc Values

Doc Values 默认对所有非分词字段生效,因为分词字段会产生大量 tokens,而 Doc Values 对其效果有限。如果确定不需要对某个字段进行聚合、排序或脚本操作,可以选择关闭该字段的 Doc Values,以节省磁盘空间并提高索引速度。

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "session_id": {
          "type":       "string",
          "index":      "not_analyzed",
          "doc_values": false
        }
      }
    }
  }
}

设置doc_values: false,这个字段将不再支持据聚合、排序和脚本执行(Script);

同时也可以对倒排索引做类似的配置:

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "customer_token": {
          "type":       "string",
          "index":      "not_analyzed",
          "doc_values": true,
          "index": "no"
        }
      }
    }
  }
}

这个可以支持聚合,但不支持查询,因为不会对这个字段生成倒排索引。

结论

Doc Values 是 Elasticsearch 性能优化的关键组成部分,它们通过预先计算和存储字段值,以及采用列式存储结构,大大提高了排序和聚合等查询操作的性能。对于需要处理大量数据和复杂查询的 Elasticsearch 集群来说,理解和优化 Doc Values 的使用至关重要。

你可能感兴趣的:(工具,elasticsearch,java,spring,cloud,spring,boot)