有个人长的像洋葱,走着走着就哭了…….

请教一个elasticsearch中的一个慢查询问题(聚合)

Elasticsearch | 作者 lunatictwo | 发布于2017年10月09日 | 阅读数:4742

现在有一个聚合查询,大致查询语句如下:
{
"query": {
"bool": {
"must": [
{
"query_string": {
"query": "uri.keyword:\"/example.html\"",
"analyze_wildcard": true
}
},
{
"range": {
"logdate": {
"gte": 1507534451358,
"lte": 1507535351358,
"format": "epoch_millis"
}
}
}
],
"must_not": []
}
},
"size": 0,
"_source": {
"excludes": []
},
"aggs": {
"2": {
"terms": {
"field": "login_uid.keyword",
"size": 10,
"order": {
"1": "desc"
}
},
"aggs": {
"1": {
"cardinality": {
"field": "mockField.search.keyword"
}
}
}
}
}
}
测试发现该查询对于长周期(大于一周)的数据,效果很不理想,查询很慢并且会有OOM的情况发生。
ES版本是5.4.0.
请问下大概是什么问题导致的?是查询不够优化还是其他问题?
已邀请:

lunatictwo

赞同来自: Durango

这个问题已经解决,主要还是es对于聚合的优化还待加强,需要在每一层terms aggregation内部加一个 "execution_hint": "map"。
具体原因:
Terms aggregation默认的计算方式并非直观感觉上的先查询,然后在查询结果上直接做聚合。

ES假定用户需要聚合的数据集是海量的,如果将查询结果全部读取回来放到内存里计算,内存消耗会非常大。因此ES利用了一种叫做global ordinals的数据结构来对聚合的字段来做bucket分配,这个ordinals用有序的数值来代表字段里唯一的一个字符串,因此为每个ordinals值分配一个bucket就等同于为每个唯一的term分配了bucket。 之后遍历查询结果的时候,可以将结果映射到各个bucket里,就可以很快的统计出每个bucket里的文档数了。

这种计算方式主要开销在构建global ordinals和分配bucket上,如果索引包含的原始文档非常多,查询结果包含的文档也很多,那么默认的这种计算方式是内存消耗最小,速度最快的。

如果指定execution_hint:map则会更改聚合执行的方式,这种方式不需要构造global ordinals,而是直接将查询结果拿回来在内存里构造一个map来计算,因此在查询结果集很小的情况下会显著的比global ordinals快。

要注意的是这中间有一个平衡点,当结果集大到一定程度的时候,map的内存开销带来的代价可能就抵消了构造global ordinals的开销,从而比global ordinals更慢,所以需要根据实际情况测试对比一下才能找好平衡点。
参考: https://elasticsearch.cn/question/1008

要回复问题请先登录注册