您好,登錄后才能下訂單哦!
本篇內容介紹了“ElasticSearch深度分頁怎么實現”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
ElasticSearch 是一個實時的分布式搜索與分析引擎,常用于大量非結構化數據的存儲和快速檢索場景,具有很強的擴展性。
from + size 分頁方式是 ES 最基本的分頁方式,類似于關系型數據庫中的 limit 方式。from 參數表示:分頁起始位置;size 參數表示:每頁獲取數據條數。例如:
GET /wms_order_sku/_search { "query": { "match_all": {} }, "from": 10, "size": 20 }
該條 DSL 語句表示從搜索結果中第 10 條數據位置開始,取之后的 20 條數據作為結果返回。這種分頁方式在 ES 集群內部是如何執行的呢?在 ES 中,搜索一般包括 2 個階段,Query 階段和 Fetch 階段,Query 階段主要確定要獲取哪些 doc,也就是返回所要獲取 doc 的 id 集合,Fetch 階段主要通過 id 獲取具體的 doc。
如上圖所示,Query 階段大致分為 3 步:
第一步:Client 發送查詢請求到 Server 端,Node1 接收到請求然后創建一個大小為 from + size 的優先級隊列用來存放結果,此時 Node1 被稱為 coordinating node(協調節點);
第二步:Node1 將請求廣播到涉及的 shard 上,每個 shard 內部執行搜索請求,然后將執行結果存到自己內部的大小同樣為 from+size 的優先級隊列里;
第三步:每個 shard 將暫存的自身優先級隊列里的結果返給 Node1,Node1 拿到所有 shard 返回的結果后,對結果進行一次合并,產生一個全局的優先級隊列,存在 Node1 的優先級隊列中。(如上圖中,Node1 會拿到 (from + size) * 6 條數據,這些數據只包含 doc 的唯一標識_id 和用于排序的_score,然后 Node1 會對這些數據合并排序,選擇前 from + size 條數據存到優先級隊列);
如上圖所示,當 Query 階段結束后立馬進入 Fetch 階段,Fetch 階段也分為 3 步:
第一步:Node1 根據剛才合并后保存在優先級隊列中的 from+size 條數據的 id 集合,發送請求到對應的 shard 上查詢 doc 數據詳情;
第二步:各 shard 接收到查詢請求后,查詢到對應的數據詳情并返回為 Node1;(Node1 中的優先級隊列中保存了 from + size 條數據的_id,但是在 Fetch 階段并不需要取回所有數據,只需要取回從 from 到 from + size 之間的 size 條數據詳情即可,這 size 條數據可能在同一個 shard 也可能在不同的 shard,因此 Node1 使用 multi-get 來提高性能)
第三步:Node1 獲取到對應的分頁數據后,返回給 Client;
依據上述我們對 from + size 分頁方式兩階段的分析會發現,假如起始位置 from 或者頁條數 size 特別大時,對于數據查詢和 coordinating node 結果合并都是巨大的性能損耗。例如:索引 wms_order_sku 有 1 億數據,分 10 個 shard 存儲,當一個請求的 from = 1000000, size = 10。在 Query 階段,每個 shard 就需要返回 1000010 條數據的_id 和_score 信息,而 coordinating node 就需要接收 10 * 1000010 條數據,拿到這些數據后需要進行全局排序取到前 1000010 條數據的_id 集合保存到 coordinating node 的優先級隊列中,后續在 Fetch 階段再去獲取那 10 條數據的詳情返回給客戶端。分析:這個例子的執行過程中,在 Query 階段會在每個 shard 上均有巨大的查詢量,返回給 coordinating node 時需要執行大量數據的排序操作,并且保存到優先級隊列的數據量也很大,占用大量節點機器內存資源。
private SearchHits getSearchHits(BoolQueryBuilder queryParam, int from, int size, String orderField) { SearchRequestBuilder searchRequestBuilder = this.prepareSearch(); searchRequestBuilder.setQuery(queryParam).setFrom(from).setSize(size).setExplain(false); if (StringUtils.isNotBlank(orderField)) { searchRequestBuilder.addSort(orderField, SortOrder.DESC); } log.info("getSearchHits searchBuilder:{}", searchRequestBuilder.toString()); SearchResponse searchResponse = searchRequestBuilder.execute().actionGet(); log.info("getSearchHits searchResponse:{}", searchResponse.toString()); return searchResponse.getHits(); }
其實 ES 對結果窗口的返回數據有默認 10000 條的限制(參數:index.max_result_window = 10000),當 from + size 的條數大于 10000 條時 ES 提示可以通過 scroll 方式進行分頁,非常不建議調大結果窗口參數值。
scroll 分頁方式類似關系型數據庫中的 cursor(游標),首次查詢時會生成并緩存快照,返回給客戶端快照讀取的位置參數(scroll_id),后續每次請求都會通過 scroll_id 訪問快照實現快速查詢需要的數據,有效降低查詢和存儲的性能損耗。
scroll 分頁方式在 Query 階段同樣也是 coordinating node 廣播查詢請求,獲取、合并、排序其他 shard 返回的數據_id 集合,不同的是 scroll 分頁方式會將返回數據_id 的集合生成快照保存到 coordinating node 上。Fetch 階段以游標的方式從生成的快照中獲取 size 條數據的_id,并去其他 shard 獲取數據詳情返回給客戶端,同時將下一次游標開始的位置標識_scroll_id 也返回。這樣下次客戶端發送獲取下一頁請求時帶上 scroll_id 標識,coordinating node 會從 scroll_id 標記的位置獲取接下來 size 條數據,同時再次返回新的游標位置標識 scroll_id,這樣依次類推直到取完所有數據。
“ElasticSearch深度分頁怎么實現”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。