elasticsearch 分頁查詢實現(xiàn)方案

1. from+size 實現(xiàn)分頁

from表示從第幾行開始,size表示查詢多少條文檔。from默認為0,size默認為10,
注意:size的大小不能超過index.max_result_window這個參數(shù)的設置,默認為10,000。
如果搜索size大于10000,需要設置index.max_result_window參數(shù)

PUT _settings
{
    "index": {
        "max_result_window": "10000000"
    }
}          

內部執(zhí)行原理:
示例:有三個節(jié)點node1、node2、node3,每個節(jié)點上有2個shard分片

node1 node2 node3
shard1 shard3 shard5
shard2 shard4 shard6
1.client發(fā)送分頁查詢請求到node1(coordinating node)上,node1建立一個大小為from+size的優(yōu)先級隊列來存放查詢結果;
2.node1將請求廣播到涉及到的shards上;
3.每個shards在內部執(zhí)行查詢,把from+size條記錄存到內部的優(yōu)先級隊列(top N表)中;
4.每個shards把緩存的from+size條記錄返回給node1;
5.node1獲取到各個shards數(shù)據(jù)后,進行合并并排序,選擇前面的 from + size 條數(shù)據(jù)存到優(yōu)先級隊列,以便 fetch 階段使用。

各個分片返回給 coordinating node 的數(shù)據(jù)用于選出前 from + size 條數(shù)據(jù),所以,只需要返回唯一標記 doc 的 _id 以及用于排序的 _score 即可,這樣也可以保證返回的數(shù)據(jù)量足夠小。
coordinating node 計算好自己的優(yōu)先級隊列后,query 階段結束,進入 fetch 階段。
from+size在深度分頁時,會帶來嚴重的性能問題:
CPU、內存、IO、網(wǎng)絡帶寬
數(shù)據(jù)量越大,越往后翻頁,性能越低

2.scroll

可以把 scroll 理解為關系型數(shù)據(jù)庫里的 cursor,因此,scroll 并不適合用來做實時搜索,而更適用于后臺批處理任務,比如群發(fā)。
可以把 scroll 分為初始化和遍歷兩步,
初始化時將所有符合搜索條件的搜索結果緩存起來,可以想象成快照,
遍歷時,從這個快照里取數(shù)據(jù),也就是說,在初始化后對索引插入、刪除、更新數(shù)據(jù)都不會影響遍歷結果。

1.初始化:

POST http://192.168.18.230:9200/bill/bill/_search?scroll=3m
{
    "query": { "match_all": {}},
    "size": 10 
}

參數(shù) scroll,表示暫存搜索結果的時間
返回一個 _scroll_id,_scroll_id 用來下次取數(shù)據(jù)用

2.遍歷:

POST http://192.168.18.230:9200/_search?scroll=3m
{
  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAHRCFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0QRYtQS1iM0h3YVJJdXAta3IwbnlYSWVBAAAAAAAAdEQWLUEtYjNId2FSSXVwLWtyMG55WEllQQAAAAAAAHRDFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0RRYtQS1iM0h3YVJJdXAta3IwbnlYSWVB"
}

這里的 scroll_id 即 上一次遍歷取回的 _scroll_id 或者是初始化返回的 _scroll_id,同樣的,需要帶 scroll 參數(shù)。
注意,每次都要傳參數(shù) scroll,刷新搜索結果的緩存時間。另外,不需要指定 index 和 type。

3.search_after

官網(wǎng)上的說明:

The Scroll api is recommended for efficient deep scrolling but scroll contexts are costly and it is not recommended to use it for real time user requests. 
The search_after parameter circumvents this problem by providing a live cursor. The idea is to use the results from the previous page to help the retrieval of the next page.

Scroll 被推薦用于深度查詢,但是contexts的代價是昂貴的,不推薦用于實時用戶請求,而更適用于后臺批處理任務,比如群發(fā)。
search_after 提供了一個實時的光標來避免深度分頁的問題,其思想是使用前一頁的結果來幫助檢索下一頁。

search_after 需要使用一個唯一值的字段作為排序字段,否則不能使用search_after方法
推薦使用_uid 作為唯一值的排序字段

GET twitter/tweet/_search
{
    "size": 10,
    "query": { "match_all": {}},
    "sort": [
        {"date": "asc"},
        {"_uid": "desc"}
    ]
}

每一條返回記錄中會有一組 sort values ,查詢下一頁時,在search_after參數(shù)中指定上一頁返回的 sort values

GET twitter/tweet/_search
{
    "size": 10,
    "query": { "match_all": {}},
    "search_after": [1463538857, "tweet#654323"],
    "sort": [
        {"date": "asc"},
        {"_uid": "desc"}
    ]
}

注意:search_after不能自由跳到一個隨機頁面,只能按照 sort values 跳轉到下一頁

4.總結

  • 深度分頁不管是關系型數(shù)據(jù)庫還是Elasticsearch還是其他搜索引擎,都會帶來巨大性能開銷,特別是在分布式情況下。
  • 有些問題可以考業(yè)務解決而不是靠技術解決,比如很多業(yè)務都對頁碼有限制,google 搜索,往后翻到一定頁碼就不行了。
  • scroll 并不適合用來做實時搜索,而更適用于后臺批處理任務,比如群發(fā)。
  • search_after不能自由跳到一個隨機頁面,只能按照 sort values 跳轉到下一頁。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容