Elasticsearch,作为当下最流行的开源搜索和分析引擎之一,其内部的数据结构和存储机制对于确保高效搜索和快速响应查询至关重要。在这其中,列存储(Doc Values)扮演了一个核心角色。本文将深入探讨Doc Values是什么、它们如何工作,以及为什么它们对Elasticsearch的性能如此关键。
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)
Doc Values 是 Elasticsearch 中的一个内部数据结构,用于在字段级别存储排序和聚合所需的数据。与传统的行存储(将文档的每个字段值作为文档的一部分存储)不同,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中所有唯一的词项时,我们只需直接访问这两个文档的词项列表,并执行集合的并集操作。这比使用倒排索引要快得多,因为无需遍历整个索引来收集特定文档的词项。
在 Elasticsearch 中,当索引一个文档时,除了将字段值存储在倒排索引中以支持全文搜索外,还会为需要排序或聚合的字段生成 Doc Values。这些 Doc Values 是字段值的压缩、列式表示,它们与倒排索引分开存储,并且针对快速、随机访问进行了优化。
Doc Values 的关键优势在于它们能够直接提供字段值,而无需重新解析存储的原始 JSON 文档(通常存储在 _source 字段中)。这是因为 Doc Values 是在索引时预先计算和存储的,因此它们可以非常快速地加载到内存中,并直接用于排序和聚合操作。以下是 Doc Values 的工作原理的详细解释:
Doc Values 和倒排索引一起工作,使得 Elasticsearch 能够在处理大量数据时提供高效的检索、排序和聚合功能。
通过了解 Doc Values 的工作原理,可以更好地理解 Elasticsearch 如何优化排序和聚合操作,并在实际应用中更有效地使用这些功能。
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 在压缩过程中会使用多种策略,例如:
综上所述,Doc Values 的持久化机制确保了其可以灵活地处理不同大小的工作集,而压缩机制则有助于减少存储空间的占用并提高数据访问的效率。这些特性共同使得 Doc Values 成为 Elasticsearch 中优化排序和聚合操作性能的关键组件。
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 的使用至关重要。