elasticsearch專欄:https://www.cnblogs.com/hello-shf/category/1550315.html
一、深度分頁方式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條數據返回。
我們會發現這樣的深度分頁將會使得效率非常低,因為我只需要查詢10條數據,而es則需要執行from+size條數據然后處理后返回。
其次:es為了性能,限制了我們分頁的深度,es目前支持的最大的 max_result_window = 10000;也就是說我們不能分頁到10000條數據以上。
例如:
from + size <= 10000所以這個分頁深度依然能夠執行。
繼續看上圖,當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 能解決深度分頁問題?
二、深度分頁之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。
三、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 ] } ] } }
下一次分頁,需要將上述分頁結果集的最后一條數據的值帶上。
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方式實現了分頁查詢。
四、三種分頁方式比較
分頁方式 | 性能 | 優點 | 缺點 | 場景 |
from + size | 低 | 靈活性好,實現簡單 | 深度分頁問題 | 數據量比較小,能容忍深度分頁問題 |
scroll | 中 | 解決了深度分頁問題 | 無法反應數據的實時性(快照版本) 維護成本高,需要維護一個 scroll_id |
海量數據的導出(比如筆者剛遇到的將es中20w的數據導入到excel) 需要查詢海量結果集的數據 |
search_after | 高 | 性能最好 不存在深度分頁問題 能夠反映數據的實時變更 |
實現復雜,需要有一個全局唯一的字段 連續分頁的實現會比較復雜,因為每一次查詢都需要上次查詢的結果 |
海量數據的分頁 |
參考文獻:
《elasticsearch-權威指南》
如有錯誤的地方還請留言指正。
原創不易,轉載請注明原文地址:https://www.cnblogs.com/hello-shf/p/11543453.html