Elasticsearch Terms聚合效能提升10倍
{ "size": 0, "query": { "bool": { "filter": { "range": { "requestTime": { "from": 1536127200000, "to": 1536135000006, "include_lower": true, "include_upper": true } } } } }, "aggregations": { "ip": { "terms": { "field": "ip", "size": 50 }, "aggregations": { "modeZero": { "filter": { "term": { "mode": "0" } }, "aggregations": { "userName": { "cardinality": { "field": "userName" } } } }, "resultOne": { "filter": { "term": { "result": "1" } }, "aggregations": { "userName": { "cardinality": { "field": "userName" } } } } } } } }
- 結果:
"took": 3435, "timed_out": false, "_shards": { "total": 25, "successful": 25, "failed": 0 }, "hits": { "total": 715625, "max_score": 0, "hits": [ ] }
速度很慢,修改下語句,terms aggregation內部加一個 "execution_hint": "map"。
-
結果:
{ "took": 265, "timed_out": false, "_shards": { "total": 25, "successful": 25, "failed": 0 }, "hits": { "total": 715625, "max_score": 0, "hits": [ ] }
效能提升了10倍以上。原因如下:
-
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更慢,所以需要根據實際情況測試對比一下才能找好平衡點。
-
terms聚合測試結果可以看:https://blog.csdn.net/laoyang360/article/details/79253294