Elasticsreach--Doc Values 和FieldData应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JY_He/article/details/79676140

Doc Values :

在Elasticsearch中,检索时使用的是倒排索引,即一个词找到对应的文档列表映射;倒排索引能够快速查找包含某项的文档,但是对于聚合排序的操作并不高效。

在聚合排序操作时,通过在倒排索引基础上建立的正排索引数据结构来完成,即一个文档到词的列表映射,正排索引就是列式存储,它在数据字段的一列存储所有value,在排序聚合操作上回表现的更加高效,在es中被称为doc_values,它将所有单字段存储在单数列中,可以使聚合更快、更高效并且内存友好。

Doc Values是一种列式存储结构,在索引创建时与倒排索引同时生成,都是基于Segment生成并不变的,并序列化到磁盘。常被应用到以下场景:对字段进行排序或者聚合操作,某些过滤以及字段相关的脚本计算。

倒排索引能够基于一个词,快速访问包含耨个词的文档列表。但在排序,聚合的时候需要一种结构能够查询到文档到词的映射,这个时候倒排索引无法高效完成,eg:

当想要找到一个包含"brown"的文档,我们可以基于倒排索引快速访问到包含"brown"的文档,比如"doc_1"和"doc_2",然而,在聚合部分,我们需要找doc_1和doc_2中找到所有的词,在倒排索引完成代价很高,即不得不迭代索引中的每一个词,是否包含在doc_1和doc_2中,这个过程非常缓慢,因为随着文档词量增加,执行聚合的诗句就会随之增加。

通过以下查询体得到包含brown的文档的词的完整列表:

  • 查询部分:

倒排索引在词表中迅速找到brown,然后,扫描所有列找出包含brown的文档,简单高效的定位到Doc_1 和 Doc_2 包含 brown 这个 token。

  • 聚合部分:

用倒排索引做这件事情代价很高:我们会迭代索引里的每个词项并收集 Doc_1 和 Doc_2 列里面 token。这很慢而且难以扩展:随着词项和文档的数量增加,执行时间也会增加。

Doc Values通过转置两者之间的关系来解决这个问题。倒排索引将词项映射到包含它们的文档,doc values将文档映射到包含它们的项:

通过转置后,再收集到Doc_1和 Doc_2 的唯一token变得非常容易。获取每个文档的行,获取所有的词项,然后求并集。搜索使用倒排索引查找文档,聚合操作收集和聚合doc values里的数据。

doc_values支持绝大部分字段类型,除了"analyzed"类型的string字段(因为对于string的列存储会先转换为数字id再存)。如果某些字段不需要进行聚合,排序或者使用脚本的操作,可以禁用特定字段的 Doc Values,达到节省磁盘空间。

禁用 Doc Values :

在创建字段映射mappin时,通过设置 doc_values: false ,这个字段将不能被用于聚合、排序以及脚本操作

反过来,让一个字段可以被聚合,通过禁用倒排索引,使它不能被正常搜索:

Doc_values工作内存:

doc_values是在创建索引的时候随之倒排索引一起生成的,即基于每个索引段生成且不可改变的,和倒排索引一样,也会被序列化到磁盘上。当需要进行聚合排序操作时,doc_value会被从磁盘中加载到系统内存中,性能和在JVM内存表现是一样的。如果工作集大于操作可用内存时,操作系统会按需加载doc_values,这种情况下的访问速度会明显低于全量加载doc_values,由于它不受JVM堆内存管理,在一定情况下下,可以让JVM的GC工作在更小的堆内存,更快的执行GC,把更多的内存留出来给操作系统来换出doc values

  1. Fielddata

ES在查询时,会将索引数据缓存在内存(JVM)中,在JVM Heap中有两条界限:驱逐线 和 断路器

ES堆内存里的数据分为两类:FieldData和其他数据。当缓存数据达到驱逐线时,会自动驱逐掉部分缓存数据,把缓存保持在安全范围内。当用户继续执行查询操作,断路器就起作用,把缓存数据+当前查询需要缓存数据量达到断路器限制,就会返回Data too large错误,阻止用户执行这个查询操作。

Doc values 不支持 analyzed 字符串字段,因为它们不能很有效的表示多值字符串,对于分词后的字符串聚合操作需要使用fielddata 的数据结构。与doc values 不同,fielddata 构建和管理 100% 在内存中,常驻于 JVM 内存堆(默认是不自动清理的)。一旦分析字符串被加载到 fielddata ,他们会一直在那里,直到被驱逐(或者节点崩溃)。

与 doc values 不同,fielddata延迟加载,它不会在索引时创建。相反,是在查询运行时,动态填充。如果结果中 fielddata 大小超过了指定 大小 ,其他的值将会被回收从而获得空间。默认情况下,设置都是 unbounded ,Elasticsearch 永远都不会从 fielddata 中回收数据。

可以使用提供的api,对field data内存使用进行监控:

  • 查看索引的field data 内存使用: GET /_stats/fielddata?fields=*;
  • 查看节点的field data内存使用: GET /_nodes/stats/indices/fielddata?fields=*;

为了防止发生这样的事情,可以通过在 config/elasticsearch.yml 文件中增加配置为 fielddata 设置一个上限:

(a)  indices.fielddata.cache.size: 20% 控制为 fielddata 分配的堆空间大小。有了这个设置,最久未使用(LRU)的 fielddata 会被回收为新数据腾出空间;(没有进行配置,es就不回收缓存了。缓存到达限制大小,无法往里插入数据。)

 (b) indices.breaker.fielddata.limit 此参数设置Fielddata断路器限制大小(公式:预计算内存 + 现有内存 <= 断路器设置内存限制),默认是60%JVM堆内存,当查询尝试加载更多数据到内存时会抛异常(以此来阻止JVM OOM发生

(c) indices.breaker.request.limit 这个 request 断路器估算完成查询的其他部分要求的结构的大小,比如创建一个聚集通, 以及限制它们到堆大小的40%,默认情况下;

(d) indices.breaker.total.limit这个total断路器封装了 request 和 fielddata 断路器去确保默认情况下这2个 使用的总内存不超过堆大小的70%;

(e)  indices.fielddata.cache.expire 这个参数已经被弃用, 不建议使用;

因此,在聚合字符串字段之前,请评估情况:

  • 是否是一个not_analyzed 字段,如果是,可以通过 doc values 节省内存 ;
  • 否则,这是一个 analyzed 字段,它将使用 fielddata 并加载到内存中。

猜你喜欢

转载自blog.csdn.net/JY_He/article/details/79676140