elasticserach數據庫深度分頁查詢的原理


深度分頁存在的問題

https://segmentfault.com/a/1190000019004316?utm_source=tag-newest

在實際應用中,分頁是必不可少的,例如,前端頁面展示數據給用戶往往都是分頁進行展示的。

1、ES分頁搜索
Elasticsearch分頁搜索采用的是from+size。from表示查詢結果的起始下標,size表示從起始下標開始返回文檔的個數。
示例:

什么是深分頁(deep paging)?簡單來說,就是搜索的特別深,比如總共有60000條數據,三個primary shard,每個shard上分了20000條數據,每頁是10條數據,這個時候,你要搜索到第1000頁,實際上要拿到的是10001~10010。

注意這里千萬不要理解成每個shard都是返回10條數據。這樣理解是錯誤的!

下面做一下詳細的分析:
請求首先可能是打到一個不包含這個index的shard的node上去,這個node就是一個協調節點coordinate node,那么這個coordinate node就會將搜索請求轉發到index的三個shard所在的node上去。比如說我們之前說的情況下,要搜索60000條數據中的第1000頁,實際上每個shard都要將內部的20000條數據中的第10001~10010條數據,拿出來,不是才10條,是10010條數據。3個shard的每個shard都返回10010條數據給協調節點coordinate node,coordinate node會收到總共30030條數據,然后在這些數據中進行排序,根據_score相關度分數,然后取到10001~10010這10條數據,就是我們要的第1000頁的10條數據。
如下圖所示:

 

 deep paging問題就是說from + size分頁太深,那么每個shard都要返回大量數據給coordinate node協調節點,會消耗大量的帶寬,內存,CPU。

 

深度分頁問題之所以存在,是和Elasticsearch搜索內部執行原理分不開的。

如果你想查詢第5000-5100數據,查詢官網API你很容易就知道,發送如下查詢條件就可以做到:

POST auditlog_operation/operlog/_search

{

“from”:5000 //from:定義從哪里開始拿數據

“size”:100 //size:定義一共拿多少條數據

}

查詢流程如下:

  1. 客戶端發送請求到某個node節點。

  2. 此node將請求廣播到各分片,各分片各自查詢前5100條數據。

  3. 查詢結果返回給node節點,node對結果進行合並整合,取出前5100條數據。

  4. 返回給客戶端。

流程大概如下

 

 

 相信就算是技術小白也能看出上述深度分頁查詢的問題,如果你要深度獲取1000000到1000100頁的數據,性能問題會非常明顯的暴露出來:CPU、內存、IO、網絡帶寬等等,而且Elasticsearch本身就是個Java應用,若並發上去,Elasticsearch會快就會OOM

{
    "query": {
        "match_all": {}
    },
    "from": 9990,
    "size": 10
}


{
    "query": {
        "match_all": {}
    },
    "from": 9999,
    "size": 10
}

我們在獲取第9999條到10009條數據的時候,其實每個分片都會拿到10009條數據,然后集合在一起,總共是10009*3=30027條數據,針對30027數據再次做排序處理,最終會獲取最后10條數據。

如此一來,搜索得太深,就會造成性能問題,會耗費內存和占用cpu。而且es為了性能,他不支持超過一萬條數據以上的分頁查詢。那么如何解決深度分頁帶來的性能呢?其實我們應該避免深度分頁操作(限制分頁頁數),比如最多只能提供100頁的展示,從第101頁開始就沒了,畢竟用戶也不會搜的那么深,我們平時搜索淘寶或者百度,一般也就看個10來頁就頂多了。

查詢請求:

POST auditlog_operation/operlog/_search

{

“from”:10000

“size”:100

}

如果你嘗試發送上述from+size請求來獲取10000-10100條數據,對不起會返回錯誤:

如果你嘗試發送上述from+size請求來獲取10000-10100條數據,對不起會返回錯誤:

{"error":{"root_cause":[{"type":"query_phase_execution_exception","reason":"Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index levelparameter."}],"type":"search_phase_execution_exception","reason":"allshards failed","phase":"query_fetch","grouped":true,"failed_shards":[{"shard":0,"index":"auditlog_operation","node":"iqu-KVKjTRmT3YcT9XAu_w","reason":{"type":"query_phase_execution_exception","reason":"Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index level parameter."}}]},"status":500}

迅速查詢官網得到更明確的提示:

翻譯成中文為:注意 from+size不再適用於查詢數據超過index.max_result_window設置值,此默認值為10000。查看 Scroll 或 Search After來獲取更高效的深層分頁(滾動)。

由此可以得到兩個結論:

  1. 你可以修改index.max_result_window設置值來繼續使用from+size做分頁查詢。當然效率肯定不高。

  2. 如果要找更高效的深度分頁方式,請使用Scroll 或者Search After。

Scroll API

Scroll API更適用於檢索大量數據(甚至全部數據)。它先做一個初始階段搜索然后持續批量從Elasticsearch里拉取結果直到返回結果為空。這有點像傳統數據庫里的cursors(游標)。

 

https://blog.csdn.net/lisongjia123/article/details/79041402

https://www.sohu.com/a/165387407_465944

https://segmentfault.com/a/1190000019004316?utm_source=tag-newest

除了效率上的問題,還有一個無法解決的問題是,es 目前支持最大的 skip 值是 max_result_window ,默認

為 10000 。也就是當 from + size > max_result_window 時,es 將返回錯誤

[root@dnsserver ~]# curl -XGET 127.0.0.1:9200/custm/_settings?pretty { "custm" : { "settings" : { "index" : { "max_result_window" : "50000", .... } } } }
 

最開始的時候是線上客戶的es數據出現問題,當分頁到幾百頁的時候,es 無法返回數據,此時為了恢復正常使用,我們采用了緊急規避方案,就是將 max_result_window 的值調至 50000,其中custm是索引的名稱,http請求提交的方式必須是put請求

[root@dnsserver ~]# curl -XPUT "127.0.0.1:9200/custm/_settings" -d '{ "index" : { "max_result_window" : 50000 } }'
 

然后這種方式只能暫時解決問題,當es 的使用越來越多,數據量越來越大,深度分頁的場景越來越復雜時,如何解決這種問題呢?

custm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM