ElasticSearch?深度分頁示例解析
1 前言
ElasticSearch 是一個實時的分布式搜索與分析引擎,常用于大量非結(jié)構(gòu)化數(shù)據(jù)的存儲和快速檢索場景,具有很強的擴展性??v使其有諸多優(yōu)點,在搜索領(lǐng)域遠超關(guān)系型數(shù)據(jù)庫,但依然存在與關(guān)系型數(shù)據(jù)庫同樣的深度分頁問題,本文就此問題做一個實踐性分析探討
2 from + size 分頁方式
from + size 分頁方式是 ES 最基本的分頁方式,類似于關(guān)系型數(shù)據(jù)庫中的 limit 方式。from 參數(shù)表示:分頁起始位置;size 參數(shù)表示:每頁獲取數(shù)據(jù)條數(shù)。例如:
GET /wms_order_sku/_search { "query": { "match_all": {} }, "from": 10, "size": 20 }
該條 DSL 語句表示從搜索結(jié)果中第 10 條數(shù)據(jù)位置開始,取之后的 20 條數(shù)據(jù)作為結(jié)果返回。這種分頁方式在 ES 集群內(nèi)部是如何執(zhí)行的呢?在 ES 中,搜索一般包括 2 個階段,Query 階段和 Fetch 階段,Query 階段主要確定要獲取哪些 doc,也就是返回所要獲取 doc 的 id 集合,F(xiàn)etch 階段主要通過 id 獲取具體的 doc。
2.1 Query 階段
如上圖所示,Query 階段大致分為 3 步:
- 第一步:Client 發(fā)送查詢請求到 Server 端,Node1 接收到請求然后創(chuàng)建一個大小為 from + size 的優(yōu)先級隊列用來存放結(jié)果,此時 Node1 被稱為 coordinating node(協(xié)調(diào)節(jié)點);
- 第二步:Node1 將請求廣播到涉及的 shard 上,每個 shard 內(nèi)部執(zhí)行搜索請求,然后將執(zhí)行結(jié)果存到自己內(nèi)部的大小同樣為 from+size 的優(yōu)先級隊列里;
- 第三步:每個 shard 將暫存的自身優(yōu)先級隊列里的結(jié)果返給 Node1,Node1 拿到所有 shard 返回的結(jié)果后,對結(jié)果進行一次合并,產(chǎn)生一個全局的優(yōu)先級隊列,存在 Node1 的優(yōu)先級隊列中。(如上圖中,Node1 會拿到 (from + size) * 6 條數(shù)據(jù),這些數(shù)據(jù)只包含 doc 的唯一標識_id 和用于排序的_score,然后 Node1 會對這些數(shù)據(jù)合并排序,選擇前 from + size 條數(shù)據(jù)存到優(yōu)先級隊列);
2.2 Fetch 階段
如上圖所示,當(dāng) Query 階段結(jié)束后立馬進入 Fetch 階段,F(xiàn)etch 階段也分為 3 步:
- 第一步:Node1 根據(jù)剛才合并后保存在優(yōu)先級隊列中的 from+size 條數(shù)據(jù)的 id 集合,發(fā)送請求到對應(yīng)的 shard 上查詢 doc 數(shù)據(jù)詳情;
- 第二步:各 shard 接收到查詢請求后,查詢到對應(yīng)的數(shù)據(jù)詳情并返回為 Node1;(Node1 中的優(yōu)先級隊列中保存了 from + size 條數(shù)據(jù)的_id,但是在 Fetch 階段并不需要取回所有數(shù)據(jù),只需要取回從 from 到 from + size 之間的 size 條數(shù)據(jù)詳情即可,這 size 條數(shù)據(jù)可能在同一個 shard 也可能在不同的 shard,因此 Node1 使用 multi-get 來提高性能)
- 第三步:Node1 獲取到對應(yīng)的分頁數(shù)據(jù)后,返回給 Client;
2.3 ES 示例
依據(jù)上述我們對 from + size 分頁方式兩階段的分析會發(fā)現(xiàn),假如起始位置 from 或者頁條數(shù) size 特別大時,對于數(shù)據(jù)查詢和 coordinating node 結(jié)果合并都是巨大的性能損耗。例如:索引 wms_order_sku 有 1 億數(shù)據(jù),分 10 個 shard 存儲,當(dāng)一個請求的 from = 1000000, size = 10。在 Query 階段,每個 shard 就需要返回 1000010 條數(shù)據(jù)的_id 和_score 信息,而 coordinating node 就需要接收 10 * 1000010 條數(shù)據(jù),拿到這些數(shù)據(jù)后需要進行全局排序取到前 1000010 條數(shù)據(jù)的_id 集合保存到 coordinating node 的優(yōu)先級隊列中,后續(xù)在 Fetch 階段再去獲取那 10 條數(shù)據(jù)的詳情返回給客戶端。分析:這個例子的執(zhí)行過程中,在 Query 階段會在每個 shard 上均有巨大的查詢量,返回給 coordinating node 時需要執(zhí)行大量數(shù)據(jù)的排序操作,并且保存到優(yōu)先級隊列的數(shù)據(jù)量也很大,占用大量節(jié)點機器內(nèi)存資源。
2.4 實現(xiàn)示例
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(); }
2.5 小結(jié)
其實 ES 對結(jié)果窗口的返回數(shù)據(jù)有默認 10000 條的限制(參數(shù):index.max_result_window = 10000),當(dāng) from + size 的條數(shù)大于 10000 條時 ES 提示可以通過 scroll 方式進行分頁,非常不建議調(diào)大結(jié)果窗口參數(shù)值。
3 Scroll 分頁方式
scroll 分頁方式類似關(guān)系型數(shù)據(jù)庫中的 cursor(游標),首次查詢時會生成并緩存快照,返回給客戶端快照讀取的位置參數(shù)(scroll_id),后續(xù)每次請求都會通過 scroll_id 訪問快照實現(xiàn)快速查詢需要的數(shù)據(jù),有效降低查詢和存儲的性能損耗。
3.1 執(zhí)行過程
scroll 分頁方式在 Query 階段同樣也是 coordinating node 廣播查詢請求,獲取、合并、排序其他 shard 返回的數(shù)據(jù)_id 集合,不同的是 scroll 分頁方式會將返回數(shù)據(jù)_id 的集合生成快照保存到 coordinating node 上。Fetch 階段以游標的方式從生成的快照中獲取 size 條數(shù)據(jù)的_id,并去其他 shard 獲取數(shù)據(jù)詳情返回給客戶端,同時將下一次游標開始的位置標識_scroll_id 也返回。這樣下次客戶端發(fā)送獲取下一頁請求時帶上 scroll_id 標識,coordinating node 會從 scroll_id 標記的位置獲取接下來 size 條數(shù)據(jù),同時再次返回新的游標位置標識 scroll_id,這樣依次類推直到取完所有數(shù)據(jù)。
以上就是ElasticSearch 深度分頁示例解析的詳細內(nèi)容,更多關(guān)于ElasticSearch 深度分頁的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java?SpringBoot集成文件之如何使用POI導(dǎo)出Word文檔
這篇文章主要介紹了Java?SpringBoot集成文件之如何使用POI導(dǎo)出Word文檔,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08Java中的this、super、final關(guān)鍵字詳解
這篇文章主要介紹了Java中的this、super、final關(guān)鍵字詳解,它在方法內(nèi)部使用,表示這個方法所屬對象的引用,它在構(gòu)造器內(nèi)部使用,表示該構(gòu)造器正在初始化的對象,this 可以調(diào)用類的屬性、方法和構(gòu)造器,需要的朋友可以參考下2023-09-09spring?boot集成jasypt?并實現(xiàn)自定義加解密的詳細步驟
由于項目中的配置文件?配置的地方過多,現(xiàn)將配置文件統(tǒng)一放到nacos上集中管理?且密碼使用加密的方式放在配置文件中,配置文件的加密使用加密庫jasypt,本文給大家介紹spring boot集成jasypt并實現(xiàn)自定義加解密,感興趣的朋友一起看看吧2023-08-08Java設(shè)計模式之備忘錄模式(Memento模式)介紹
這篇文章主要介紹了Java設(shè)計模式之備忘錄模式(Memento模式)介紹,memento是一個保存另外一個對象內(nèi)部狀態(tài)拷貝的對象,這樣以后就可以將該對象恢復(fù)到原先保存的狀態(tài),需要的朋友可以參考下2015-03-03SpringBoot中使用JdbcTemplate訪問Oracle數(shù)據(jù)庫的案例詳解
JdbcTemplate是Spring框架中的一個核心類,用于簡化Java應(yīng)用程序與關(guān)系型數(shù)據(jù)庫的交互操作,本文給大家介紹SpringBoot中使用JdbcTemplate訪問Oracle數(shù)據(jù)庫的方法,感興趣的朋友跟隨小編一起看看吧2023-10-10