elasticsearch深度分頁問題

elasticsearch專欄:https://www.cnblogs.com/hello-shf/category/1550315.htmlhtml

1、深度分頁方式from + size

es 默認採用的分頁方式是 from+ size 的形式,在深度分頁的狀況下,這種使用方式效率是很是低的,好比咱們執行以下查詢數據庫

1 GET /student/student/_search
2 {
3   "query":{
4     "match_all": {}
5   },
6   "from":5000,
7   "size":10
8 }

 

意味着 es 須要在各個分片上匹配排序並獲得5010條數據,協調節點拿到這些數據再進行排序等處理,而後結果集中取最後10條數據返回。api

咱們會發現這樣的深度分頁將會使得效率很是低,由於我只須要查詢10條數據,而es則須要執行from+size條數據而後處理後返回。less

其次:es爲了性能,限制了咱們分頁的深度,es目前支持的最大的 max_result_window = 10000;也就是說咱們不能分頁到10000條數據以上。 curl

例如:elasticsearch

 

 from + size <= 10000因此這個分頁深度依然可以執行。ide

 

 繼續看上圖,當size + from > 10000;es查詢失敗,而且提示性能

Result window is too large, from + size must be less than or equal to: [10000] but was [10001]

 

接下來看還有一個很重要的提示大數據

See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting

 

有關請求大數據集的更有效方法,請參閱滾動api。這個限制能夠經過改變[索引]來設置。哦呵,原來es給咱們提供了另外的一個API scroll。難道這個 scroll 能解決深度分頁問題?ui

 

2、深度分頁之scroll

在es中若是咱們分頁要請求大數據集或者一次請求要獲取較大的數據集,scroll都是一個很是好的解決方案。

使用scroll滾動搜索,能夠先搜索一批數據,而後下次再搜索一批數據,以此類推,直到搜索出所有的數據來scroll搜索會在第一次搜索的時候,保存一個當時的視圖快照,以後只會基於該舊的視圖快照提供數據搜索,若是這個期間數據變動,是不會讓用戶看到的。每次發送scroll請求,咱們還須要指定一個scroll參數,指定一個時間窗口,每次搜索請求只要在這個時間窗口內能完成就能夠了。

一個滾屏搜索容許咱們作一個初始階段搜索而且持續批量從Elasticsearch里拉取結果直到沒有結果剩下。這有點像傳統數據庫裏的cursors(遊標)。

滾屏搜索會及時製做快照。這個快照不會包含任何在初始階段搜索請求後對index作的修改。它經過將舊的數據文件保存在手邊,因此能夠保護index的樣子看起來像搜索開始時的樣子。這樣將使得咱們沒法獲得用戶最近的更新行爲。

scroll的使用很簡單

執行以下curl,每次請求兩條。能夠定製 scroll = 5m意味着該窗口過時時間爲5分鐘。

1 GET /student/student/_search?scroll=5m
2 {
3   "query": {
4     "match_all": {}
5   },
6   "size": 2
7 }

 

 1 {
 2   "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB",
 3   "took" : 0,
 4   "timed_out" : false,
 5   "_shards" : {
 6     "total" : 5,
 7     "successful" : 5,
 8     "skipped" : 0,
 9     "failed" : 0
10   },
11   "hits" : {
12     "total" : 6,
13     "max_score" : 1.0,
14     "hits" : [
15       {
16         "_index" : "student",
17         "_type" : "student",
18         "_id" : "5",
19         "_score" : 1.0,
20         "_source" : {
21           "name" : "fucheng",
22           "age" : 23,
23           "class" : "2-3"
24         }
25       },
26       {
27         "_index" : "student",
28         "_type" : "student",
29         "_id" : "2",
30         "_score" : 1.0,
31         "_source" : {
32           "name" : "xiaoming",
33           "age" : 25,
34           "class" : "2-1"
35         }
36       }
37     ]
38   }
39 }

 在返回結果中,有一個很重要的 

_scroll_id

 

在後面的請求中咱們都要帶着這個 scroll_id 去請求。

如今student這個索引中共有6條數據,id分別爲 1, 2, 3, 4, 5, 6。當咱們使用 scroll 查詢第4次的時候,返回結果應該爲kong。這時咱們就知道已經結果集已經匹配完了。

繼續執行3次結果以下三圖所示。

1 GET /_search/scroll
2 {
3   "scroll":"5m",
4   "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC0YFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtGRZpZVI1dUEyMlNuVzBwU3JNVzR6RVlBAAAAAAAALRsWaWVSNXVBMjJTblcwcFNyTVc0ekVZQQAAAAAAAC0aFmllUjV1QTIyU25XMHBTck1XNHpFWUEAAAAAAAAtHBZpZVI1dUEyMlNuVzBwU3JNVzR6RVlB"
5 }

 

 

 

由結果集咱們能夠發現最終確實分別獲得了正確的結果集,而且正確的終止了scroll。

 

3、search_after

from + size的分頁方式雖然是最靈活的分頁方式,可是當分頁深度達到必定程度將會產生深度分頁的問題。scroll可以解決深度分頁的問題,可是其沒法實現實時查詢,即當scroll_id生成後沒法查詢到以後數據的變動,由於其底層原理是生成數據的快照。這時 search_after應運而生。其是在es-5.X以後才提供的。

search_after 是一種假分頁方式,根據上一頁的最後一條數據來肯定下一頁的位置,同時在分頁請求的過程當中,若是有索引數據的增刪改查,這些變動也會實時的反映到遊標上。爲了找到每一頁最後一條數據,每一個文檔必須有一個全局惟一值,官方推薦使用 _uid 做爲全局惟一值,可是隻要能表示其惟一性就能夠。

爲了演示,咱們須要給上文中的student索引增長一個uid字段表示其惟一性。

執行以下查詢:

 1 GET /student/student/_search
 2 {
 3   "query":{
 4     "match_all": {}
 5   },
 6   "size":2,
 7   "sort":[
 8     {
 9       "uid": "desc"
10     }  
11   ]
12 }

 結果集:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 6,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "student",
        "_type" : "student",
        "_id" : "6",
        "_score" : null,
        "_source" : {
          "uid" : 1006,
          "name" : "dehua",
          "age" : 27,
          "class" : "3-1"
        },
        "sort" : [
          1006
        ]
      },
      {
        "_index" : "student",
        "_type" : "student",
        "_id" : "5",
        "_score" : null,
        "_source" : {
          "uid" : 1005,
          "name" : "fucheng",
          "age" : 23,
          "class" : "2-3"
        },
        "sort" : [
          1005
        ]
      }
    ]
  }
}
View Code

 

 下一次分頁,須要將上述分頁結果集的最後一條數據的值帶上。

 1 GET /student/student/_search
 2 {
 3   "query":{
 4     "match_all": {}
 5   },
 6   "size":2,
 7   "search_after":[1005],
 8   "sort":[
 9     {
10       "uid": "desc"
11     }  
12   ]
13 }

 

 這樣咱們就使用search_after方式實現了分頁查詢。

 

4、三種分頁方式比較

分頁方式 性能 優勢 缺點 場景
from + size 靈活性好,實現簡單 深度分頁問題 數據量比較小,能容忍深度分頁問題
scroll 解決了深度分頁問題

沒法反應數據的實時性(快照版本)

維護成本高,須要維護一個 scroll_id

海量數據的導出(好比筆者剛遇到的將es中20w的數據導入到excel)

須要查詢海量結果集的數據

search_after

性能最好

不存在深度分頁問題

可以反映數據的實時變動

實現複雜,須要有一個全局惟一的字段

連續分頁的實現會比較複雜,由於每一次查詢都須要上次查詢的結果

海量數據的分頁

 

 

 

  參考文獻:

  《elasticsearch-權威指南》

 

  若有錯誤的地方還請留言指正。

  原創不易,轉載請註明原文地址:http://www.javashuo.com/article/p-ebrlqejs-bo.html

相關文章
相關標籤/搜索