查询语句优化
查询语句优化的内容包括:查询范围,单次查询数量等。
- 根据实际业务需求去规划查询范围,查询越少的字段越快,过大的查询范围不仅会导致查询效率低,而且会使Elasticsearch集群资源耗费急剧增加,甚至可能造成集群崩溃。通过_source参数可以控制返回字段信息,尽量避免读取大字段;
- 单次查询数量限制是为了保证内存不会被查询内存大量占用,Elasticsearch默认的查询请求通常返回排序后的前10条记录,最多一次读取10000条记录。通过from和size参数控制读取记录范围,避免一次读取过多的记录。一次性查询大于10000条的数据,使用scroll查询,请参考3.7.6。
安全模式下查询示例:
curl -XGET --tlsv1.2 --negotiate -k -v -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 -v -u : 'https://ip:httpport/myindex-001/_forcemerge?only_expunge_deletes=false&max_num_segments=1&flush=true&pretty'
多索引安全模式下示例:
curl -XPOST --tlsv1.2 --negotiate -k -v -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 -v -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。
过滤查询(filter)
Elasticsearch的查询操作分为2种:查询(query)和过滤(filter),查询(query)默认会计算每个返回文档的得分,然后根据得分排序;而过滤(filter)只会筛选出符合的文档,并不计算得分,且可以缓存文档。
对于非全文检索的使用场景,如果不关心查询结果和查询条件的相关度,只是想查找目标数据,可以使用filter来提高查询效率。
query安全模式下查询示例:
curl -XGET --tlsv1.2 --negotiate -k -v -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 -v -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 -v -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 -v -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 -v -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 -v -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 -v -u : "https://ip:httpport/myindex-001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"match": {
"age": "36"
}
},
"size":1000,
"sort": "_doc"
}'
避免使用wildcard模糊匹配查询
Elasticsearch默认支持通过*?正则表达式来做模糊匹配,数据量级别达到TB+甚至更高之后,模糊匹配查询通常会耗时比较长,甚至可能导致内存溢出,卡死乃至崩溃宕机的情况。所以数据量大的情况下,不要使用模糊匹配查询。
安全模式下模糊匹配查询示例:
curl -XGET --tlsv1.2 --negotiate -k -v -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”。
- 查询结果直接放入到内存中构建map,在查询结果集小的场景下,速度极快;
- 但如果查询结果集合很大(百万-亿级别)的时候,传统聚合方式会比map方式快。
安全模式下聚合查询示例:
curl -XGET --tlsv1.2 --negotiate -k -v -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 -v -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"
}
}
}
}'
配置EsClient角色(协调节点)
EsClient角色可以用于发送查询请求到其他节点,收集和合并结果,以及响应发出查询的客户端。通过配置EsClient角色可以加快查询运算速度,提升缓存命中数。
mappings优化
请确认mappings设置是否合理。
- 对于只需要精确查询的字段,例如时间戳,应该设置为keyword。
- 对需要进行全文检索的字段设置合理的分词器,不同的分词器查询效率相差较大。
超时参数
在对查询结果的精确度要求较低的场景下,如果低响应时间比搜索结果更重要,可以使用如下两个参数来提升查询性能:
- terminate_after:表示每个分片收集的文档的最大数量,一旦达到该数量,查询请求提前终止。
- timeout:表示每个分片上的查询超时时间,在请求超时之前,Elasticsearch将会返回已经成功从每个分片上获取的结果。
安全模式下使用示例
:
curl -XGET
–tlsv1.2 –negotiate -k -v -u : “https://ip:httpport/_search?pretty&timeout=10ms&terminate_after=10″