本篇文章不是講ElasticSearch(下面簡稱ES)聚合分析的基本概念和用法的,這些網上的資料不少,不清楚的能夠自行查閱。app
我下面聚合分析使用的數據都是kibana自帶的,這樣方便有些讀者實際測試文中的示例。分佈式
ES爲了知足搜索的實時性,在聚合分析的一些場景會經過損失精準度的方式加快結果的返回。這其實ES在實時性和精準度中間的權衡。性能
須要明確的是,並非全部的聚合分析都會損失精準度,好比min,max等這些就沒有精準度的問題。測試
可能這樣直接說很差理解,下面會有詳細的分析。spa
咱們經過一個示例引入問題。code
首先我會把kibana自帶的航班信息索引(名爲kibana_sample_data_flights
)reindex
到我自定義的一個索引(名爲my_flights
)中,個人mapping和自帶的索引徹底同樣,惟一的區別在於我設置了20個分片。索引的設置以下:對象
PUT my_flights { "settings": { "number_of_shards": 20 }, "mappings" : { "properties" : { "AvgTicketPrice" : { "type" : "float" }, 省略其它部分
reindex(之後有專門的文章講reindex)的過程比較慢,個人電腦大概須要一分鐘左右。blog
POST _reindex { "source": { "index": "kibana_sample_data_flights" }, "dest": { "index": "my_flights" } }
而後咱們執行聚合分析的查詢,這個查詢是根據航班的目的地進行分桶。索引
GET my_flights/_search { "size": 0, "aggs": { "dest": { "terms": { "field": "DestCountry" } } } }
結果以下,ip
{ "took" : 9, "timed_out" : false, "_shards" : { "total" : 20, "successful" : 20, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 10000, "relation" : "gte" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "dest" : { "doc_count_error_upper_bound" : 52, "sum_other_doc_count" : 3187, "buckets" : [ { "key" : "IT", "doc_count" : 2371 }, { "key" : "US", "doc_count" : 1987 }, 其它部分省略
在返回結果的aggregations中,有兩個值:doc_count_error_upper_bound和sum_other_doc_count,我先來解釋下,
而這個doc_count_error_upper_bound
就是咱們本文要關注的重點對象,這個指標其實就是告訴用戶本次的聚合結果究竟有多不精確。
ES基於分佈式,聚合分析的請求都是分發到全部的分片上單獨處理,最後彙總結果。ES的terms聚合自己是前幾個(size指定)結果,這就致使告終果必然有偏差。
如上圖所示,咱們進行一個terms分桶查詢,取前面3個結果。ES給出的結果是 A,B,C三個term,文檔數量分別是12, 6, 4。
可是咱們看最下面兩個分片上的文檔分佈,人工也能看出來其實D應該是在結果中的,由於D的文檔數量有6個,比C多,因此比較精確的結果應該是A,B,D。
產生問題的緣由在於ES在對每一個分片單獨處理的時候,第一個分片的結果是A,B,C,第二個分片是A,B,D,而且第一個分片的C的文檔數量大於D。因此彙總後的結果是A,B,C。
討論完了問題,如今來看看如何解決問題。通常的方案有幾種:
設置主分片爲1,也就是不分片了。這個顯而易見,上面分析聚合不精確的核心緣由就在於分片,因此不分片確定能夠解決問題。可是缺點也是顯然的,只適用於數據量小的狀況下,若是數據量大都在一個分片上會影響ES的性能。
咱們來作個測試,看看不分片的效果。咱們使用自帶的kibana_sample_data_flights
索引來執行分桶聚合。
GET kibana_sample_data_flights/_search { "size": 0, "aggs": { "dest": { "terms": { "field": "DestCountry" , "size": 3 } } } }
結果是,
{ "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 10000, "relation" : "gte" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "dest" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 7605, "buckets" : [ { "key" : "IT", "doc_count" : 2371 }, 其它部分省略
由於kibana_sample_data_flights
索引的分片數量是1,因此沒有損失精準度。
以下所示,把size設置成20(默認狀況是10)聚合查詢。size是指定聚合返回的結果數量。返回的結果越多,精確度確定就越高。
GET my_flights/_search { "size": 0, "aggs": { "dest": { "terms": { "field": "DestCountry" , "size": 20 } } } }
結果,
"aggregations" : { "dest" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 571, "buckets" : [ { "key" : "IT", "doc_count" : 2371 }, 其它部分省略
結果也是沒有精準度的損失了。
這個值表示要從分片上拿來計算的文檔數量。默認狀況下和size是同樣的。取得size的值越大,結果會越接近準確,不過很明顯會影響性能。
參考: