1. from+size 實現分頁
from表示從第幾行開始,size表示查詢多少條文檔。from默認為0,size默認為10,
注意:size的大小不能超過index.max_result_window這個參數的設置,默認為10,000。
如果搜索size大於10000,需要設置index.max_result_window參數
PUT _settings
{
"index": {
"max_result_window": "10000000"
}
}
內部執行原理:
示例:有三個節點node1、node2、node3,每個節點上有2個shard分片
node1 | node2 | node3 |
---|---|---|
shard1 | shard3 | shard5 |
shard2 | shard4 | shard6 |
1.client發送分頁查詢請求到node1(coordinating node)上,node1建立一個大小為from+size的優先級隊列來存放查詢結果;
2.node1將請求廣播到涉及到的shards上;
3.每個shards在內部執行查詢,把from+size條記錄存到內部的優先級隊列(top N表)中;
4.每個shards把緩存的from+size條記錄返回給node1;
5.node1獲取到各個shards數據后,進行合並並排序,選擇前面的 from + size 條數據存到優先級隊列,以便 fetch 階段使用。
各個分片返回給 coordinating node 的數據用於選出前 from + size 條數據,所以,只需要返回唯一標記 doc 的 _id 以及用於排序的 _score 即可,這樣也可以保證返回的數據量足夠小。
coordinating node 計算好自己的優先級隊列后,query 階段結束,進入 fetch 階段。
from+size在深度分頁時,會帶來嚴重的性能問題:
CPU、內存、IO、網絡帶寬
數據量越大,越往后翻頁,性能越低
2.scroll
可以把 scroll 理解為關系型數據庫里的 cursor,因此,scroll 並不適合用來做實時搜索,而更適用於后台批處理任務,比如群發。
可以把 scroll 分為初始化和遍歷兩步,
初始化時將所有符合搜索條件的搜索結果緩存起來,可以想象成快照,
遍歷時,從這個快照里取數據,也就是說,在初始化后對索引插入、刪除、更新數據都不會影響遍歷結果。
1.初始化:
POST http://192.168.18.230:9200/bill/bill/_search?scroll=3m
{
"query": { "match_all": {}},
"size": 10
}
參數 scroll,表示暫存搜索結果的時間
返回一個 _scroll_id,_scroll_id 用來下次取數據用
2.遍歷:
POST http://192.168.18.230:9200/_search?scroll=3m
{
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAHRCFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0QRYtQS1iM0h3YVJJdXAta3IwbnlYSWVBAAAAAAAAdEQWLUEtYjNId2FSSXVwLWtyMG55WEllQQAAAAAAAHRDFi1BLWIzSHdhUkl1cC1rcjBueVhJZUEAAAAAAAB0RRYtQS1iM0h3YVJJdXAta3IwbnlYSWVB"
}
這里的 scroll_id 即 上一次遍歷取回的 _scroll_id 或者是初始化返回的 _scroll_id,同樣的,需要帶 scroll 參數。
注意,每次都要傳參數 scroll,刷新搜索結果的緩存時間。另外,不需要指定 index 和 type。
3.search_after
官網上的說明:
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的代價是昂貴的,不推薦用於實時用戶請求,而更適用於后台批處理任務,比如群發。
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參數中指定上一頁返回的 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.總結
- 深度分頁不管是關系型數據庫還是Elasticsearch還是其他搜索引擎,都會帶來巨大性能開銷,特別是在分布式情況下。
- 有些問題可以考業務解決而不是靠技術解決,比如很多業務都對頁碼有限制,google 搜索,往后翻到一定頁碼就不行了。
- scroll 並不適合用來做實時搜索,而更適用於后台批處理任務,比如群發。
- search_after不能自由跳到一個隨機頁面,只能按照 sort values 跳轉到下一頁。
</div>
</div>