Elasticsearch分頁(yè)查詢總結(jié)

使用 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階段:

  1. 當(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。
  2. 此節(jié)點(diǎn)將請(qǐng)求廣播到其他相關(guān)節(jié)點(diǎn)上,從每個(gè)Shards中取Top 10100條的score和id。
  3. 所有Shards獲取的結(jié)果匯聚到Node1,假如有5個(gè)Shards,則一共會(huì)取到5 * 10100 = 50500條數(shù)據(jù)。
  4. 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é):

  1. 如果數(shù)據(jù)量小(10000條內(nèi)),或者只關(guān)注結(jié)果集的TopN數(shù)據(jù),可以使用from / size 分頁(yè),簡(jiǎn)單粗暴
  2. 數(shù)據(jù)量大,深度翻頁(yè),后臺(tái)批處理任務(wù)(數(shù)據(jù)遷移)之類的任務(wù),使用 scroll 方式
  3. 數(shù)據(jù)量大,深度翻頁(yè),用戶實(shí)時(shí)、高并發(fā)查詢需求,使用 search after 方式
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容