es调优涉及问题

精准度优化:

1 匹配精准度不够参数:minimum_should_match    我们可以通过代码的方式进行再match匹配的字段上添加匹配力度  ,这样如果搜索有4个词汇,那么需要匹配至少3个才行。 

2 匹配精准度不够参数:boots,在匹配某一个指定的字段时,一旦匹配成功让其增加score分值,提升排名。

3 再有就是分值计算不精准到时结果不精准,索引如果分了多个shard那么这些shard上的数据应该更加平均,不应该有数据倾斜,否则es通过TF/IDF,算法中的IDF算法是根据某些词在整体shard中的document去计算的,如果shard中的文档数量相差较大,可能会影响分值。

查询速度优化(filter过滤和索引创建的粒度):

4 条件过滤优化,使用query中的filter可以增加速度,因为默认情况下es对filter的结果做了bitset缓存。而且不参与score的计算会减少时间复杂度,然后在小范围内,通过match进行匹配会更提升数据拉取效率。

5 es搜索效率优化,因为es搜索数据量较大情况下,很大程度依赖于操作系统的oscache进行数据缓存的检索,如果数据量过大,需要考虑提升机器本身内存的扩容,或者是增加机器,并增加replicshard数量,用来接受更多的请求提升搜索性能。

例如  8g的数据,3个机器上(指定了1个副本),每个机器当时是1g的内存,发现偶尔搜索会有延时5s左右的情况,增加机器本身8g内存后解决了问题。

索引创建经验:使用滑动时间创建新的索引并以时间单位作为后缀,这样可以减少单个索引的分片数量,在有针对的时间点范围的查询能走更少的datemerge过程,如果一类数据仅仅使用一个索引的话,如果数据增长速度惊人,是不利于维护的,并且如果扩展shard也会有效率的损失,不如在创建索引,这个最初阶段直接进行数据分散,做索引的增量,如果真要使用全范围时间维度查询,可以使用多索引映射到同一别名,来直接操作索引别名,或使用模糊后缀的方式如:GET /index_2020*/_search 这种,可支持跨索引查询。

es索引的数据量是有限制的,而且官方建议不超过50G,合理范围是20~40G,而且单个shard不应该超过2的23次方条(20亿条)这个值倒是很难挑战,而且不建议索引分片过多,如果多的话数据量多起来以后会影响查询效率,实际要通过协调节点分shard取数, 数据通过网络io汇总后,再处理。

以千万数量作为分界点吧,如果日均能产生千万数据,可以使用天为粒度创建索引。

transport 升级为rest客户端的优化:

es7.0 以后就废除了transport 9300 客户端的方式,而提倡使用resthighlevelclient,本身rest性能也是比较不错的了,而且transport相当于一个“集群中内部节点”的启动方式,安全性是不够的,而且每个节点间还会默认建立(16根连接)长连接,如果客户端较多性能也是有问题的,而且es本身客户端连接任意一个节点都可以进行通信,如果使用transport相当于打破了这个规则,时协调节点不受控制;如果使用rest就可以容易的控制,可以在最前端专门提供一个协调节点。

聚合方式:

POST /person_list/_mapping/info
{
  "properties": {
        "字段名": {
            "type": "text",
            "fielddata": true,
            "fields": {"raw": {"type": "keyword"}}
            }
        }
}

分词字段需要设置"fielddata": true,才能进行聚合运算,而且需要使用内部不分词的方式,否则会出现分词后在聚合的现象,这种方式未必是我们想要的而且会花费掉大量的内存空间。

内部原理

analyzed字符串的字段,字段分词后占用空间很大,正排索引不能很有效的表示多值字符串,所以正排索引不支持此类字段。

fielddata结构与正排索引类似,是另外一份数据,构建和管理100%在内存中,并常驻于JVM内存堆,极易引起OOM问题。

加载过程

fielddata加载到内存的过程是lazy加载的,对一个analzyed field执行聚合时,才会加载,而且是针对该索引下所有的文档进行field-level加载的,而不是匹配查询条件的文档,这对JVM是极大的考验。

fielddata是query-time创建,动态填充数据,而不是不是index-time创建,

内存限制

indices.fielddata.cache.size 控制为fielddata分配的堆空间大小。 当你发起一个查询,分析字符串的聚合将会被加载到fielddata,如果这些字符串之前没有被加载过。如果结果中fielddata大小超过了指定大小,其他的值将会被回收从而获得空间(使用LRU算法执行回收)。

默认无限制,限制内存使用,但是会导致频繁evict和reload,大量IO性能损耗,以及内存碎片和gc,这个参数是一个安全卫士,必须要设置:

indices.fielddata.cache.size: 20%

监控fielddata内存使用

Elasticsearch提供了监控监控fielddata内存使用的命令,我们在上面可以看到内存使用和替换的次数,过高的evictions值(回收替换次数)预示着内存不够用的问题和性能不佳的原因:

# 按索引使用 indices-stats API
GET /_stats/fielddata?fields=*

# 按节点使用 nodes-stats API
GET /_nodes/stats/indices/fielddata?fields=*

# 按索引节点
GET /_nodes/stats/indices/fielddata?level=indices&fields=*

fields=*表示所有的字段,也可以指定具体的字段名称。

熔断器

indices.fielddata.cache.size的作用范围是当前查询完成后,发现内存不够用了才执行回收过程,如果当前查询的数据比内存设置的fielddata 的总量还大,如果没有做控制,可能就直接OOM了。

熔断器的功能就是阻止OOM的现象发生,在执行查询时,会预算内存要求,如果超过限制,直接掐断请求,返回查询失败,这样保护Elasticsearch不出现OOM错误。

常用的配置如下:

  • indices.breaker.fielddata.limit:fielddata的内存限制,默认60%
  • indices.breaker.request.limit:执行聚合的内存限制,默认40%
  • indices.breaker.total.limit:综合上面两个,限制在70%以内

最好为熔断器设置一个相对保守点的值。fielddata需要与request断路器共享堆内存、索引缓冲内存和过滤器缓存,并且熔断器是根据总堆内存大小估算查询大小的,而不是实际堆内存的使用情况,如果堆内有太多等待回收的fielddata,也有可能会导致OOM发生。

字符串分词后排序不准的问题:(6.X中string已经废除,可以额外指定keyword以及fileddate=true来创建正排索引)

原文地址:https://www.cnblogs.com/zzq-include/p/13371601.html