ElasticSearch - 解決ES的深分頁問題 (游標 scroll)


https://www.jianshu.com/p/f4d322415d29

 

1.簡介

ES為了避免深分頁,不允許使用分頁(from&size)查詢10000條以后的數據,因此如果要查詢第10000條以后的數據,要使用ES提供的 scroll(游標) 來查詢

假設取的頁數較大時(深分頁),如請求第20頁,Elasticsearch不得不取出所有分片上的第1頁到第20頁的所有文檔,並做排序,最終再取出from后的size條結果作爲最終的返回值

假設你有16個分片,則需要在coordinate node彙總到 shards* (from+size)條記錄,即需要16*(20+10)記錄后做一次全局排序

所以,當索引非常非常大(千萬或億),是無法使用from + size 做深分頁的,分頁越深則越容易OOM,即便不OOM,也很消耗CPU和內存資源

因此ES使用index.max_result_window:10000作爲保護措施 ,即默認 from + size 不能超過10000,雖然這個參數可以動態修改,也可以在配置文件配置,但是最好不要這麽做,應該改用ES游標來取得數據

2.scroll游標原理

可以把 scroll 理解爲關系型數據庫里的 cursor,因此,scroll 並不適合用來做實時搜索,而更適用於后台批處理任務,比如群發

scroll 具體分爲初始化和遍歷兩步

初始化時將所有符合搜索條件的搜索結果緩存起來,可以想象成快照

在遍歷時,從這個快照里取數據

也就是說,在初始化后對索引插入、刪除、更新數據都不會影響遍歷結果

游標可以增加性能的原因,是因為如果做深分頁,每次搜索都必須重新排序,非常浪費,使用scroll就是一次把要用的數據都排完了,分批取出,因此比使用from+size還好

3.具體實例

初始化

請求

注意要在URL中的search后加上scroll=1m,不能寫在request body中,其中1m表示這個游標要保持開啟1分鍾

可以指定size大小,就是每次回傳幾筆數據,當回傳到沒有數據時,仍會返回200成功,只是hits裡的hits會是空list

在初始化時除了回傳_scroll_id,也會回傳前100筆(假設size=100)的數據

request body和一般搜索一樣,因此可以說在初始化的過程中,除了加上scroll設置游標開啟時間之外,其他的都跟一般的搜尋沒有兩樣 (要設置查詢條件,也會回傳前size筆的數據)

POST 127.0.0.1:9200/my_index/_search?scroll=1m `注意這里的地址` { "query":{ "range":{ "createTime": { "gte": 1522229999999 } } }, "size": 1000 } 

返回結果


{ "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAfv5-FjNOamF0Mk1aUUhpUnU5ZWNMaHJocWcAAAAAAH7-gBYzTmphdDJNWlFIaVJ1OWVjTGhyaHFnAAAAAAB-_n8WM05qYXQyTVpRSGlSdTllY0xocmhxZwAAAAAAdsJxFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbCcBZlZGUwSWpSVlJqeVJiN1dBWHNpUG1R", "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 84, "max_score": 1, "hits": [ { "_index": "video1522821719", "_type": "doc", "_id": "84056", "_score": 1, "_source": { "title": "三個院子", "createTime": 1522239744000 } } ....99 data ] } } 

遍歷數據

請求

使用初始化返回的_scroll_id來進行請求,每一次請求都會繼續返回初始化中未讀完數據,並且會返回一個_scroll_id,這個_scroll_id可能會改變,因此每一次請求應該帶上上一次請求返回的_scroll_id

要注意返回的是_scroll_id,但是放在請求裡的是scroll_id,兩者拼寫上有不同

且每次發送scroll請求時,都要再重新刷新這個scroll的開啟時間,以防不小心超時導致數據取得不完整

POST 127.0.0.1:9200/_search/scroll?scroll=1m `注意地址` { "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB" } 

返回結果

如果沒有數據了,就會回傳空的hits,可以用這個判斷是否遍歷完成了數據

{ "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB", "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 84, "max_score": null, "hits": [] } } 

優化scroll查詢

在一般場景下,scroll通常用來取得需要排序過后的大筆數據,但是有時候數據之間的排序性對我們而言是沒有關系的,只要所有數據都能取出來就好,這時能夠對scroll進行優化

初始化

使用_doc去sort得出來的結果,這個執行的效率最快,但是數據就不會有排序,適合用在只想取得所有數據的場景

POST 127.0.0.1:9200/my_index/_search?scroll=1m
{
    "query": {
        "match_all" : {}
    },
    "sort": [
        "_doc"
        ]
    }
}

清除scroll

雖然我們在設置開啟scroll時,設置了一個scroll的存活時間,但是如果能夠在使用完順手關閉,可以提早釋放資源,降低ES的負擔

DELETE 127.0.0.1:9200/_search/scroll
{
    "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAdsMqFmVkZTBJalJWUmp5UmI3V0FYc2lQbVEAAAAAAHbDKRZlZGUwSWpSVlJqeVJiN1dBWHNpUG1RAAAAAABpX2sWclBEekhiRVpSRktHWXFudnVaQ3dIQQAAAAAAaV9qFnJQRHpIYkVaUkZLR1lxbnZ1WkN3SEEAAAAAAGlfaRZyUER6SGJFWlJGS0dZcW52dVpDd0hB"
}

4.轉發連接

https://blog.csdn.net/weixin_40341116/article/details/80821655


免責聲明!

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



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