如何使用elasticsearch實現千萬級分組的精確聚合
首先如下邏輯結構圖,index由三個shard組成。
正常場景下這種模式返回的數據都是精確的,比如我們經常使用的關鍵字查找,排序打分。因為求的是某一個文檔的先后順序,shard中文檔在整個index中是獨一份,不存在shard之間的歧義。但是在聚合(group by)場景下,這個模式會有一些問題,比如,我們要統計index中排名前三的國家及人口數量,按照es的分布式原理,會在每個shard中求出前3,然后再匯總節點根據每個shard的返回再匯總求top3,這個時候問題來了,如果數據是如下分布的
匯總節點拿到的數據是shard1/shard2/shard3返回的?A B C E?四個國家做最終匯總,但是很明顯,國家D其實可以排入前三,其實ES在官網特意指出了這個問題。
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html
大概意思就是講如果group by最終的分組的個數(國家個數也叫term的個數)大于返回的top size,那么有可能出現應該返回的數據沒有返回,類似上面的D國。
如何解決呢?其實也有如下幾個方法
1、? 每個shard返回更多的結果,每個節點返回top100,減少誤差
2、? 將相同的term發送到同一個shard,比如A國都在shard1(route方法)
3、? 最暴力的方法,直接不分片,一個shard
問題真的解決了嗎?并沒有,上面的方法缺陷都很明顯
方法1:到底需要返回多少冗余數據才會準確,并且返回冗余數據的計算代價(CPU 內存 磁盤IO等)也是昂貴的。
方法2:如果term有成千上萬甚至上億,每一個index是TB級別呢,這個時候每個shard的會巨大,并且這個還需要業務感知,需要在入庫的時候指定route
方法3:單個shard容量是有上限的,大小一般建議值在30~50GB,并且單shard無法體會分布式的魅力,集群能力用不起來
Hadoop的MR過程
我們想象一下,如果能夠將每個shard的數據(精確到每個term)發送到一個指定的地方計算,那么能夠得到這個值得精確結果,其實在上文中的方法2也提到了,但是這個不經過處理的中間數據會巨大,很多場景不適用。我們這里回憶一下Hadoop的MR,用過hadoop的人肯定對Map-Reduce過程都不陌生,下圖大概介紹了MR中如何操作一個Map-Reduce任務。
每個map任務產生了?1/2/3三份數據(這個三份數據可以有很多理解,比如表面上的三個國家,hash之后的三個取模值,甚至三個區間等等),這里有一個很特別的shuffle過程,shuffler過程會把每一個map的同一份數據送到一個reduce里面,對于這個reduce來說,對這一份數據進行統計就能夠得到這一份數據的精確結果。其他的數據也類似,這樣整個過程就能夠得到精確值。
為什么ES不這樣做?ES是一個商業公司,肯定很早就意識到這個問題了,為什么不做?更多的是為了性能考慮,現在很多場景ES已經把準實時搜索引擎寫成實時搜索引擎了(大數據場景下,s級快速的返回99%準確性的數據比遠比耗費大量時間在計算一個精確值上重要),除了這個之外,為了實時性,ES也舍棄了其他方面的東西,這里就不展開了。
當然我們下面介紹的方法跟這個還有點出入,不過核心思想是有一個調度中心能夠講相同的數據發送到一起并且能夠滿足可行性。
預聚合過程
我們并不把沒有處理過的文檔數據直接通過route發送到指定shard。我們會先使用預聚合來加速,什么是預聚合加速?
基本原理:用戶在制作報表時,主要是對時序數據進行聚合,跨越不同粒度的時間生成報表時,大部分的數據是重復計算的。只要找到用戶報表的最小時間粒度,根據其語句內容找到涉及的字段,自動進行聚合成粗粒度的數據,后續就僅僅需要在粗粒度上進行聚合即可
就是使用預先計算,緩存中間結果,達到加速的目的。我們看下面一張表舉例,每一個時間內有源源不斷地數據流入
聚合后的中間數據形式,這里是三層聚合(時間/年齡段/教育程度),統計指標有最大薪水,平均薪水,數量。
聚合任務會把一個時間粒度內的某一個字段(這里是一分鐘學歷、年齡這兩個字段)計算統計指標存入中間索引。同樣,中間索引會根據term使用route將數據存入指定shard。具體我們可以看下圖:
這樣做可以帶來什么好處,假設一分鐘原始數據有10w條,按照1分鐘的粒度中間索引只需要(聚合的term粒度?*?統計指標)條數,大大減少了預聚合任務分發到中間索引的數據(這樣使用route分發后大大降低膨脹的概率)。
我們曾經有過一些數據可以參考(100GB的原始數據,通過預聚合計算生成的中間索引大概在10MB)
那么可能問題又來了,圖中預聚合任務查詢原始數據(過程1)不也面臨數據不準確的問題嗎?相對于直接查詢大批量數據,因為預聚合任務的時間是有粒度的,任務會每個粒度周期執行一次,比如一分鐘,這樣分鐘內的數據量相對可控,結果也相對準確。為了進一步提升預聚合任務的精確性,在預聚合任務時使用composite aggs翻頁查找,得出精確值,關于composite aggs?可以參考:
htps://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html
查詢過程
有了中間索引之后,對于已經建立好中間索引的查詢,會優先使用中間索引加速,我們會自動識別,本應該查詢源數據100GB的請求,直接會查詢10MB中間索引,時延大幅降低。ES公司在他的商業插件X-pack中也提供了一個預聚合插件,叫做rollup功能,x-pack生成中間索引后,為了使用加速功能,需要修改原始的查詢語句,這樣用戶感知并且無法兼容kibana,siren等可視化插件。
巧的是,這個能力已經在華為云云搜索服務中實現了,云搜索服務是基于開源的elasticsearch的一款云服務,我們做了很多增強,官網鏈接華為云CSS服務。
關于云搜索服務的其他功能介紹可以參考:https://support.huaweicloud.com/usermanual-css/css_01_0007.html
更多關于云搜索服務的增強版功能使用,歡迎提工單進行咨詢。
云搜索服務 CSS EI企業智能 AI
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。