ES性能优化之查询优化笔记(一)

文章目录

  • 前言
  • 一、mappings优化
  • 二、查询语句优化
  • 三、强制段合并(force merge)
  • 四、过滤查询(filter)
  • 五、路由(routing)
  • 六、游标查询(scroll)
  • 七、避免使用wildcard模糊匹配查询
  • 八、聚合优化
  • 九、超时参数
  • 总结


前言

优化的场景除了集群基础的性能优化之外,还要区分具体的场景,往往一些参数调整会有相应的好处和坏处,而且往往写入和查询的优化是对立的,要结合相应的业务场景


提示:以下是本篇文章正文内容,下面案例可供参考

一、mappings优化

•对于只需要精确查询的字段,例如时间戳,应该设置为keyword。
•对需要进行全文检索的字段设置合理的分词器,不同的分词器查询效率相差较大。

合理地向Elasticsearch中进行数据索引时,要注意以下几点:
Elasticsearch可以对数据做动态mapping,但请不要这么做,尽量在创建index时便赋予index固定的mapping配置。当大量数据写入的同时伴随着新的字段的增加,会造成大量的put_mapping操作,从而造成EsMaster阻塞,影响整个Elasticsearch集群的运行。不建议使用动态mapping,如果需要使用动态mapping,建议尽量使用较为精准的匹配规则,杜绝*全匹配的通配符操作。当Elasticsearch的实例总数在500以上时,禁止使用动态mapping。
•如果数据量巨大,可以分的字段个数太多,如超过1000个字段,最好给字段赋予不同的级别索引到不同的index中。例如,常用的查询字段可以写入到一个index中,字段长度较长且不常用的索引到另一个index中。
•合理的设计Mapping,根据实际的业务数据去设置优化Mapping,根据具体的字段和需求去选择对应的类型设置,可参考如下几点:
1.字符串类型默认分成:text和keyword两种类型。需要分词:text,否则keyword。
2.枚举类型,基于性能keyword,即便是整形。
3.数值类型,尽量选择贴近大小的类型。
4.日期类型,如果需要基于时间轴做分析,必须date类型,如果仅需秒级返回,建议使用keyword。
5.其他类型,布尔、日期、地理位置,使用对应的类型即可。
6.如果某个字段不需要被检索,将“index”参数设置为“false”。
7.如果字段完全不需要检索,排序,聚合分析,将“enable”参数设置为“false”。
8.“_all”字段,默认将写入的字段拼接成一个大的字符串,并对该字段进行分词,用于支持整个doc的全文检索,“_all”字段在查询时占用更多的CPU,同时占用更多的磁盘存储空间,默认为“false”,不建议开启该字段。
9.norms字段,norm是索引评分因子,如果不用按评分对文档进行排序,设置为“false”,默认是“true”。
10._source字段,默认是开启的,如果不需要update、reindex和高亮操作,将“_source”设置为“false”,节省更多的磁盘空间。

二、查询语句优化

查询语句优化的内容包括:查询范围,单次查询数量等。
1.根据实际业务需求去规划查询范围,查询越少的字段越快,过大的查询范围不仅会导致查询效率低,而且会使Elasticsearch集群资源耗费急剧增加,甚至可能造成集群崩溃。通过_source参数可以控制返回字段信息,尽量避免读取大字段;
2.单次查询数量限制是为了保证内存不会被查询内存大量占用,Elasticsearch默认的查询请求通常返回排序后的前10条记录,最多一次读取10000条记录。通过from和size参数控制读取记录范围,避免一次读取过多的记录。一次性查询大于10000条的数据,使用scroll查询,请参考游标查询(scroll)。

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty"  -H 'Content-Type: application/json' -d' 
{
  "from": 0,
  "size": 10,
  "_source": "age",
  "query": {
      "match": {
        "age": "56"
      }
  },
  "sort": [
    {
      "age": {
        "order": "asc"
      }
    }
  ]
}'

三、强制段合并(force merge)

每个shard是基于多个segment组成创建的,segment的个数的减少可以大幅的提高查询的速度,定时的进行手动索引段合并,可以提高查询速度。支持单索引和多索引批量操作。

单索引安全模式下示例:

curl -XPOST --tlsv1.2 --negotiate -k -u : 'https://ip:httpport/myindex-001/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true&pretty'

多索引安全模式下示例:

curl -XPOST --tlsv1.2 --negotiate -k -u : 'https://ip:httpport/myindex-001,myindex-002/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true&pretty'
curl -XPOST --tlsv1.2 --negotiate -k -u : 'https://ip:httpport/_all/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true&pretty'

•max_num_segments:merge到多少个segments,1的意思是强行merge到1个segment;
•only_expunge_deletes:只清理有deleted标记的segments,推荐值false;
•flush:清理完执行一下flush,默认是true。

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

四、过滤查询(filter)

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

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

query安全模式下查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "age": "56"
    }
  }
}'

filter安全模式下查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": {
         "match": {
          "age": "56"
        }
      }
    }
  }
}'

五、路由(routing)

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

shard_num = hash(_routing) % num_primary_shards

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

安全模式下写入时指定路由:

curl -XPUT --tlsv1.2 --negotiate -k -u : "https://ip:httpport/my_index/my_type/1?routing=user1&refresh=true" -H 'Content-Type: application/json' -d' 
{
  "title": "This is a document"
}'

安全模式下查询时不指定路由示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/my_index/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "title": "document"
    }
  }
}'

需要查询所有的分片,返回结果:

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "1",
        "_score" : 0.2876821,
        "_routing" : "user1",
        "_source" : {
          "title" : "This is a document"
        }
      }
    ]
  }
}

安全模式下查询时指定路由示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/my_index/_search?routing=user1&pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "title": "document"
    }
  }
}'

查询时只需要查询一个分片,查询结果:

{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "1",
        "_score" : 0.2876821,
        "_routing" : "user1",
        "_source" : {
          "title" : "This is a document"
        }
      }
    ]
  }
}

六、游标查询(scroll)

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

安全模式下scroll查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "age": "36"
    }
  },
  "size":1000
}'

说明:
使用scroll查询,应该在初始搜索请求中指定scroll参数,这个参数告诉Elasticsearch保持游标窗口期多长时间。例如:scroll=1m,表示1分钟。

结果返回:

{
  "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoMgAAAAAAAABPFlFHZzExcFdnUWJDU0d5bU==",
  "took" : 55,
  "timed_out" : false,
  "_shards" : {
    "total" : 50,
    "successful" : 50,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 16692062,
    "max_score" : 0.0,
    "hits" : [...1000 data ]
  }
}

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

安全模式下示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match": {
      "age": "36"
    }
  },
  "size":1000,
  "sort": "_doc"
}'

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

curl -XDELETE --tlsv1.2 --negotiate -k -u : "https://ip:httpport/_search/scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
  "scroll_id":"DnF1ZXJ5VGhlbkZldGNoMgAAAAAAAABPFlFHZzExcFdnUWJDU0d5bU=="
}'

七、避免使用wildcard模糊匹配查询

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

安全模式下模糊匹配查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "query": {
    "wildcard" : {
	"name" : "*优" 
	}
  }
}'

八、聚合优化

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

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

添加设置:“collect_mode”: “breadth_first”。

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

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

优化聚合执行方式

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

添加设置:“execution_hint”: “map”。

说明:

1.查询结果直接放入到内存中构建map,在查询结果集小的场景下,速度极快;
2.但如果查询结果集合很大(百万-亿级别)的时候,传统聚合方式会比map方式快。

安全模式下聚合查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size" : 0,
  "aggregations": {
    "count_age" : {
	"terms" : {
		   "field" : "age"
		} 
	}
  }
}'

安全模式下聚合优化后查询示例:

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/myindex-001/_search?pretty" -H 'Content-Type: application/json' -d'
{
  "size" : 0,
  "aggregations": {
    "count_age" : {
	"terms" : {
		   "field" : "age",
		   "execution_hint": "map",
		   "collect_mode": "breadth_first"
		} 
	}
  }
}'

九、超时参数

在对查询结果的精确度要求较低的场景下,如果低响应时间比搜索结果更重要,可以使用如下两个参数来提升查询性能:
1.terminate_after:表示每个分片收集的文档的最大数量,一旦达到该数量,查询请求提前终止。
2.timeout:表示每个分片上的查询超时时间,在请求超时之前,Elasticsearch将会返回已经成功从每个分片上获取的结果。
安全模式下使用示例 :

curl -XGET --tlsv1.2 --negotiate -k -u : "https://ip:httpport/_search?pretty&timeout=10ms&terminate_after=10"

“terminate_after”和“timeout”的使用可能导致查询结果不准确,请按照实际业务场景谨慎使用。这两个参数的取值需结合业务场景来调整。

总结

在索引创建时要合理的规划mapping,先过滤后查询缩小数据范围,可以通过指定路由字段加快查询的速度,尽量避免使用模糊匹配查询,对于深度分页场景使用游标查询,在聚合场景下可以使用breadth_first方式经行预聚合。

你可能感兴趣的:(ElasticSearch,elasticsearch,大数据)