前言
近期在做新的項目時,使用了ElasticSearch作為數據的存儲和查詢。接到了一個比較惡心的需求,需要對es進行分頁查詢,單次查詢一萬條,最多需要查詢十次。當時也沒想太多,需求評審時並沒有及時反駁,既然掉坑里了,那就想辦法爬出來吧!
es的分頁
1)from+size淺分頁
我們當時有點想當然了,以為from+size就可以搞定(業務代碼寫多的后果)。實際測試的時候,發現內存消耗特別大,而且速度也很一般。ES的查詢機制如下:
假設我們的ES有三個節點,當分頁查詢請求過來時,如果落到node1節點,那么node1節點將會向node2和node3發送同樣的查詢請求,每個節點將topN的文檔返回(這里只返回文檔的id以及打分排序的字段,減少數據傳輸),node1會對三個節點的所有文檔(3*N個)進行排序,取topN后再根據文檔的id到對應的節點上查詢整個文檔數據,最后返回客戶端。
而對於分頁查詢,比如from=10000,szie=10000,其實每個節點需要查詢from+size=20000條數據,排序之后截取后10000條數據。當我們進行深度分頁,比如查詢第十頁數據時,每個節點需要查詢10*size=10W條數據,這個太恐怖了。而且默認情況下,當from+size大於10000時,查詢會拋出一個異常,ES2.0后有一個max_result_window屬性的設置,默認值是10000,也就是from+size的最大限度。當然你可以修改這個值作為臨時的應對策略,不過治標不治本,產品也只會變本加厲!
2)scroll查詢
ES支持scroll滾屏查詢,有興趣的同學可以了解一下,網上相關的文檔不少。不過根據ES官網的描述,scroll查詢是很耗性能的方式,不建議在實時查詢中運用。摘抄自官網:The Scroll api is recommended for efficient deep scrolling but scroll contexts are costly and it is not recommended to use it for real time user requests.
3)search_after查詢
search_after是ES5.0及之后版本提供的新特性,search_after有點類似scroll,但是和scroll又不一樣,它提供一個活動的游標,通過上一次查詢最后一條數據來進行下一次查詢。
比如第一次查詢如下:
GET zm/recall/_search { "query": { "match_all": {} }, "sort": [ { "lastModifyTime": { "order": "desc" } } ], "size": 10 }
這里根據更新時間進行排序,拿到的結果如下:
{ "_index": "zmrecall", "_type": "recall", "_id": "60310505115909", "_score": null, "_source": { "userId": 60310505115909, "score": 1, "city": [ 276 ], "sex": 1, "age": 29, "lastModifyTime": 1545037514 }, "sort": [ 1545037514 ] }
注意到返回結果中有一個sort字段,所以下一次查詢的時候,只需要將本次查詢最后一條數據中的排序字段加入查詢即可:
GET zm/recall/_search { "query": { "match_all": {} }, "sort": [ { "lastModifyTime": { "order": "desc" } } ], "search_after": [1545037514],//這個值與上次查詢最后一條數據的sort值一致,支持多個 "size": 10 }
這里需要說明一下,使用search_after查詢需要將from設置為0或-1,當然你也可以不寫
另外在ES6.5的文檔中有這樣一句話需要注意一下:
大致的意思就是,如果search_after中的關鍵字為654,那么654323的文檔也會被搜索到,所以在選擇search_after的排序字段時需要謹慎,可以使用比如文檔的id或者時間戳等
另外,search_after並不是隨機的查詢某一頁數據,而是並行的滾屏查詢;search_after的查詢順序會在更新和刪除時發生變化,也就是說支持實時的數據查詢
總結
在使用一個工具之前,一定要對其內部的基本流程有一個簡單的了解,否則出現問題時不知從何入手。search_after相比較上面的淺分頁以及scroll滾屏查詢會有很大的性能提升,推薦大家使用!
最后附上ES的官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-request-search-after.html
另外在csdn上也看到一個博客,講的很不錯:https://blog.csdn.net/ctwy291314/article/details/82754652