使用 from / size 分頁(yè)
from - 表示起始位置,size - 表示每頁(yè)數(shù)量;類似與 MySQL 的 limit + offset。
示例:
GET /_search
{
"from" : 10, "size" : 10,
"query" : {
"term" : { "user" : "kimchy" }
}
}
需要注意的是,from + size 不能超過(guò)10000,也就是說(shuō)在前10000條之內(nèi),可以隨意翻頁(yè),10000條之后就不行了。
實(shí)際上,通過(guò)設(shè)置 index.max_result_window 可以修改這個(gè)限制,但是不建議這么做,因?yàn)檫@種方式翻頁(yè)越深效率越低。
原理:
Query階段:
- 當(dāng)一個(gè)請(qǐng)求發(fā)送到某個(gè)ES節(jié)點(diǎn)時(shí),該節(jié)點(diǎn)(Node1)會(huì)根據(jù)from和size,建立一個(gè)結(jié)果集窗口,窗口大小為from+size。假如from=10000,size=100,則窗口大小為10100。
- 此節(jié)點(diǎn)將請(qǐng)求廣播到其他相關(guān)節(jié)點(diǎn)上,從每個(gè)Shards中取Top 10100條的score和id。
- 所有Shards獲取的結(jié)果匯聚到Node1,假如有5個(gè)Shards,則一共會(huì)取到5 * 10100 = 50500條數(shù)據(jù)。
- Node1進(jìn)行歸并排序,并選擇Top 10100條,存入結(jié)果集窗口。
Fetch階段:
根據(jù)Query階段得到的排序結(jié)果,從 from 位置取 size 條數(shù)據(jù),抓取文檔詳細(xì)內(nèi)容返回。
從此過(guò)程中可以看出,翻頁(yè)越靠后,需要參與排序的文檔就越多,效率也就越低。所以,如果結(jié)果集很大,不建議用這種分頁(yè)方式。
關(guān)于ES搜索請(qǐng)求過(guò)程,推薦一篇博文:https://blog.csdn.net/caipeichao2/article/details/46418413(其中query_and_fetch已經(jīng)在對(duì)外接口中去掉了,所以只需了解query_then_fetch過(guò)程即可。)
使用 scroll 分頁(yè)
使用scroll就像傳統(tǒng)數(shù)據(jù)庫(kù)中的游標(biāo)一樣,方式如下:
第一步
POST /twitter/_search?scroll=1m
{
"size": 100,
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
scroll=1m,表示“search context”存活時(shí)間1分鐘。返回結(jié)果中會(huì)帶有一個(gè)“_scroll_id”,這個(gè)值在后續(xù)的翻頁(yè)過(guò)程中使用。
第二步
POST /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}
不用指定index和type,也不用其他查詢條件,只要把上一步的_scroll_id即可。
之后翻頁(yè)一直如此,每次執(zhí)行會(huì)自動(dòng)滾動(dòng)100條數(shù)據(jù),直到返回的結(jié)果為空為止。
每次執(zhí)行間隔不要超過(guò)1分鐘,否則“search context”會(huì)釋放掉。
第三步
DELETE /_search/scroll
{
"scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}
結(jié)果遍歷完成后,刪除scroll_id。這一步也可以不做,等1分鐘后沒(méi)有繼續(xù)翻頁(yè)請(qǐng)求,“search context”會(huì)自動(dòng)釋放掉,不過(guò)建議還是手動(dòng)清除,節(jié)省資源。
優(yōu)化:
如果目的是為了遍歷所有結(jié)果,而不關(guān)心結(jié)果的順序,那么可以按“_doc”排序來(lái)提高性能
POST /twitter/_search?scroll=1m
{
"size": 100,
"query": {
"match" : {
"title" : "elasticsearch"
}
},
"sort": ["_doc"]
}
與 from/size 分頁(yè)方式不同,使用 scroll 分頁(yè)只能單向順序翻頁(yè),不能隨機(jī)翻頁(yè),適用于遍歷結(jié)果集的場(chǎng)景。
scroll 翻頁(yè)能夠深度翻頁(yè),但是翻頁(yè)期間需要維護(hù)“search context”,這是需要占用一定資源的。
所以對(duì)于用戶高并發(fā)訪問(wèn)的場(chǎng)景,不推薦用這種方式,scroll 更適用于批處理類的后臺(tái)任務(wù)。
使用 search after 分頁(yè)
這種方式同樣可以深度翻頁(yè),但是彌補(bǔ)了 scroll 方式的不足。其思想是:用前一次的查詢結(jié)果作為下一次的查詢條件。
示例:
首次查詢
GET /user_model/_search
{
"size": 10,
"query": {"match_all": {}},
"sort": [
{"_id": "asc"}
]
}
返回結(jié)果:
{
"took" : 4379,
"timed_out" : false,
"_shards" : {
"total" : 6,
"successful" : 6,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 38213940,
"max_score" : null,
"hits" : [
...
{
"_index" : "user_model",
"_type" : "_doc",
"_id" : "00000f78f59644b1967783986c35496c",
"_score" : null,
"_source" : {
...
},
"sort" : [
"00000f78f59644b1967783986c35496c"
]
}
]
}
}
后續(xù)查詢
GET /user_model/_search
{
"size": 10,
"query": {"match_all": {}},
"sort": [
{"_id": "asc"}
],
"search_after": ["00000f78f59644b1967783986c35496c"]
}
其中,search_after 為上次查詢結(jié)果中最后一條記錄的 sort 值。
總結(jié):
- 如果數(shù)據(jù)量小(10000條內(nèi)),或者只關(guān)注結(jié)果集的TopN數(shù)據(jù),可以使用from / size 分頁(yè),簡(jiǎn)單粗暴
- 數(shù)據(jù)量大,深度翻頁(yè),后臺(tái)批處理任務(wù)(數(shù)據(jù)遷移)之類的任務(wù),使用 scroll 方式
- 數(shù)據(jù)量大,深度翻頁(yè),用戶實(shí)時(shí)、高并發(fā)查詢需求,使用 search after 方式