【Elasticsearch】ES Elasticsearch查询优化


在这里插入图片描述

1.概述

转载:ES Elasticsearch查询优化

1.前言

Elasticsearch的查询分为两阶段,查询(Query)阶段和提取(Fetch)阶段。Elasticsearch的数据分布在多个Shard以及多台机器上,当进行查询时,潜在的结果数据会在当前查询index的所有Shard中,所以Elasticsearch查询的时候需要查询所有Shard(同一个Shard的Primary和Replica选择一个即可),查询会分发给所有的Shard,每个Shard中都是一个独立的查询引擎,最后合并所有Shard查询的结果并返回。

2 合理的集群规划

前期Elasticsearch集群的数据合理规划是必须的,一个集群可以容纳的index的数量(准确的说是shard数据量)和shard的大小以及数据结点的数量和硬件配置有关,因此不可能有一个统一的标准。 最好通过监控数据来做容量管理,如果一个数据结点的heap压力很大,load过高导致查询或者写入慢,就应该考虑扩容加节点了。

因此建议尽量控制index和shard的数量,以一个Elasticsearch实例内存最大31G为例:

  1. 一个数据实例的shard数保持在600个以内

  2. 每个shard大小不要超过30GB,推荐20-30GB,100 GB以下的index设置成3-5个shard。大的index单shard不超过30GB。

合理的数据和集群规划是提高查询速度的前提条件。在集群状况良好的前提下进行参数调优。

当index/shard数量过多时,可以考虑从以下几个方面改进:

  1. 降低数据量较小的index的shard数量;

  2. 把一些关联的index合并成一个index;

  3. 关闭不使用的历史索引。

3 数据模型优化

3.1 精心设计Mapping

Elasticsearch默认的Mapping是动态Mapping,系统自动识别字段类型。因根据实际的业务数据去设置优化Mapping,根据具体的字段和需求去选择对应的类型和设置。

  1. string类型默认分成:text和keyword两种类型。需要分词:text,否则keyword。

  2. 枚举类型,基于性能keyword,即便是整形。

  3. 数值类型,尽量选择贴近大小的类型。

  4. 其他类型,布尔、日期、地理位置。如果需要基于时间轴做分析,必须date类型,如果仅需秒级返回,建议使用keyword。

  5. 如果某个字段不需要被检索,将index设置为false。

  6. 如果字段完全不需要检索,排序,聚合分析,将enable设置为false。

3.2 选择合理的分词器

  如果使用IK分词器,建议使用ik_max_word,粗粒度的分词结果基本包含细粒度ik_smart的结果。

4 查询限制

限制的内容包括:查询范围,单次查询数量等。

  1. 根据实际业务需求去规划查询范围,查询越少的字段越快,过大的查询范围不仅会导致查询效率低,而且会使Elasticsearch集群资源耗费急剧增加,甚至可能造成集群崩溃。通过_source参数可以控制返回字段信息,尽量避免读取大字段。

  2. 单次查询数量限制是为了保证内存不会被查询内存大量占用,Elasticsearch默认的查询请求通常返回排序后的前10条记录,最多一次读取10000条记录。通过from和size参数控制读取记录范围,避免一次读取过多的记录。一次性查询大量的数据,使用scroll。

查询请求示例:

curl -XGET "http://127.0.0.1:24100/myindex-001/_search?pretty"  -H 'Content-Type: application/json' -d'
{
    
    
  "from": 0,
  "size": 10,
  "_source": "age",
  "query": {
    
    
      "match": {
    
    
        "age": "56"
      }
  },
  "sort": [
    {
    
    
      "age": {
    
    
        "order": "asc"
      }
    }
  ]
}'

5 段合并(segment merge)

每个Shard是基于多个segment组成创建的,segment的个数的减少可以大幅的提高查询的速度,定时的进行手动索引段合并,可以提高查询速度。

curl -XPOST 'http://127.0.0.1:24100/myindex-001/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true&pretty'

注意:segment merge操作是需要耗费大量的磁盘I/O,所以建议在业务比较空闲的时间进行后台段合并。

关于索引合并这里有异议:【Elasticsearch】索引 强制合并 缓存 refresh flush 等操作

6 过滤查询(filter)

Elasticsearch的查询操作分为2种:查询(query)和过滤(filter),查询(query)默认会计算每个返回文档的得分,然后根据得分排序;而过滤(filter)只会筛选出符合的文档,并不计算得分,且可以缓存文档。

对于非全文检索的使用场景,用户并不关心查询结果和查询条件的相关度,只是想查找目标数据。此时可以使用filter来提高查询效率。

query查询:

curl -XGET "http://127.0.0.1:24100/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "match": {
    
    
      "age": "56"
    }
  }
}'

filter查询:

curl -XGET "http://127.0.0.1:24100/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "bool": {
    
    
      "filter": {
    
    
         "match": {
    
    
          "age": "56"
        }
      }
    }
  }
}'

7 路由(routing)

Elasticsearch写入文档时,文档会通过一个公式路由到一个索引中的一个分片上。默认公式如下:

shard_num = hash(_routing) % num_primary_shards

_routing字段的取值,默认是_id字段,可以根据业务场景设置经常查询的字段作为路由字段。例如可以考虑将用户id、地区作为路由字段,查询时可以过滤不必要的分片,加快查询速度。

写入时指定路由:

curl -PUT "http://127.0.0.1:24100/my_index/my_type/1?routing=user1&refresh=true" -H 'Content-Type: application/json' -d'
{
    
    
  "title": "This is a document"
}'

查询时不指定路由,需要查询所有的分片:

curl -XGET "http://127.0.0.1:24100/my_index/_search?pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "match": {
    
    
      "title": "document"
    }
  }
}'

查询是指定路由,只需要查询1个分片:

curl -XGET "http://127.0.0.1:24100/my_index/_search?routing=user1&pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "match": {
    
    
      "title": "document"
    }
  }
}'

8 配置Client角色

Client角色可以用于发送查询请求到其他节点,收集和合并结果,以及响应发出查询的客户端。通过给Client节点配置更高规格的CPU和内存,可以加快查询运算速度,提升缓存命中数,增加Client节点可以增加查询并发数。

9 游标查询(scroll)

Elasticsearch为了避免深分页,不允许使用分页(from&size)查询10000条以后的数据,需要使用scroll(游标)查询。

curl -XGET  "http://127.0.0.1:24102/myindex-001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "match": {
    
    
      "age": "36"
    }
  },
  "size":10000
}'

优化scroll,在一般场景下,scroll通常用来取得需要排序过来的大量数据,但很多时候只需要把数据取出来就好,这时候可以对scroll进行优化。使用_doc去sort返回的结果不会有排序,此时执行效率最快。

curl -XGET  "http://127.0.0.1:24102/myindex-001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "query": {
    
    
    "match": {
    
    
      "age": "36"
    }
  },
  "size":10000,
  "sort": "_doc"
}'

注意:在设置开启scroll时,设置了一个scroll的存活时间,但是如果能够在使用完顺手关闭,可以提早释放资源,降低ES的负担。

curl -XDELETE  "http://127.0.0.1:24102/_search/scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
    
    
  "scroll_id":"id"
}'

10 避免使用wildcard模糊匹配查询

Elasticsearch默认支持通过*?正则表达式来做模糊匹配,数据量级别达到TB+甚至更高之后,模糊匹配查询通常会耗时比较长,甚至可能导致内存溢出,卡死乃至崩溃宕机的情况。所以数据量大的情况下,不要使用模糊匹配查询。

11 聚合优化

大多时候对单个字段的聚合查询还是比较快的,但是当需要聚合多个字段时,就会产生大量的分组,最终结果就是占用Elasticsearch大量的内存,从而导致内存溢出的情况发生。尽量根据业务优化,减少聚合次数。

11.1 默认深度优化聚合改为广度优先聚合

添加设置:"collect_mode": "breadth_first"

depth_first :直接进行子聚合的计算

breadth_first :先计算出当前聚合的结果,针对这个结果在对子聚合进行计算

11.2 在每一层terms aggregation内部加一个增加:“execution_hint”: “map”

添加设置:"execution_hint": "map"

注意:

1、查询结果直接放入到内存中构建map,在查询结果集小的场景下,速度极快;

2、 但如果查询结果集合很大(百万-亿级别)的时候,传统聚合方式会比map方式快。

猜你喜欢

转载自blog.csdn.net/qq_21383435/article/details/114003934