Elasticsearch 分頁(yè)查詢(xún)

[TOC]

前言

我們?cè)趯?shí)際工作中,有很多分頁(yè)的需求,商品分頁(yè)、訂單分頁(yè)等,在MySQL中我們可以使用limit,那么在Elasticsearch中我們可以使用什么呢?

ES 分頁(yè)搜索一般有三種方案,from + size、search after、scroll api,這三種方案分別有自己的優(yōu)缺點(diǎn),下面將進(jìn)行分別介紹。

使用的數(shù)據(jù)是kibana中的kibana_sample_data_flights。

from + size

這是ES分頁(yè)中最常用的一種方式,與MySQL類(lèi)似,from指定起始位置,size指定返回的文檔數(shù)。

GET kibana_sample_data_flights/_search
{
  "from": 10,
  "size": 2, 
  "query": {
    "match": {
      "DestWeather": "Sunny"
    }
  },
  "sort": [
    {
      "timestamp": {
        "order": "asc"
      }
    }
  ]
}

這個(gè)例子中查詢(xún)航班中,目的地的天氣是晴朗的,并且按時(shí)間進(jìn)行排序。

使用簡(jiǎn)單,且默認(rèn)的深度分頁(yè)限制是1萬(wàn),from + size 大于 10000會(huì)報(bào)錯(cuò),可以通過(guò)index.max_result_window參數(shù)進(jìn)行修改。

{
  "error": {
    "root_cause": [
      {
        "type": "query_phase_execution_exception",
        "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
      }
    ],
    "type": "search_phase_execution_exception",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "kibana_sample_data_flights",
        "node": "YRQNOSQqS-GgSo1TSzlC8A",
        "reason": {
          "type": "query_phase_execution_exception",
          "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
        }
      }
    ]
  },
  "status": 500
}

這種分頁(yè)方式,在分布式的環(huán)境下的深度分頁(yè)是有性能問(wèn)題的,一般不建議用這種方式做深度分頁(yè),可以用下面將要介紹的兩種方式。

理解為什么深度分頁(yè)是有問(wèn)題的,我們可以假設(shè)在一個(gè)有 5 個(gè)主分片的索引中搜索。 當(dāng)我們請(qǐng)求結(jié)果的第一頁(yè)(結(jié)果從 1 到 10 ),每一個(gè)分片產(chǎn)生前 10 的結(jié)果,并且返回給協(xié)調(diào)節(jié)點(diǎn) ,協(xié)調(diào)節(jié)點(diǎn)對(duì) 50 個(gè)結(jié)果排序得到全部結(jié)果的前 10 個(gè)。

現(xiàn)在假設(shè)我們請(qǐng)求第 1000 頁(yè),結(jié)果從 10001 到 10010 。所有都以相同的方式工作除了每個(gè)分片不得不產(chǎn)生前10010個(gè)結(jié)果以外。 然后協(xié)調(diào)節(jié)點(diǎn)對(duì)全部 50050 個(gè)結(jié)果排序最后丟棄掉這些結(jié)果中的 50040 個(gè)結(jié)果。

可以看到,在分布式系統(tǒng)中,對(duì)結(jié)果排序的成本隨分頁(yè)的深度成指數(shù)上升。

search after

search after 利用實(shí)時(shí)有游標(biāo)來(lái)幫我們解決實(shí)時(shí)滾動(dòng)的問(wèn)題。第一次搜索時(shí)需要指定 sort,并且保證值是唯一的,可以通過(guò)加入 _id 保證唯一性。

GET kibana_sample_data_flights/_search
{
  "size": 2, 
  "query": {
    "match": {
      "DestWeather": "Sunny"
    }
  },
  "sort": [
    {
      "timestamp": {
        "order": "asc"
      },
      "_id": {
        "order": "desc"
      }
    }
  ]
}

在返回的結(jié)果中,最后一個(gè)文檔有類(lèi)似下面的數(shù)據(jù),由于我們排序用的是兩個(gè)字段,返回的是兩個(gè)值。

"sort" : [
  1614561419000,
  "6FxZJXgBE6QbUWetnarH"
]

第二次搜索,帶上這個(gè)sort的信息即可,如下

GET kibana_sample_data_flights/_search
{
  "size": 2,
  "query": {
    "match": {
      "DestWeather": "Sunny"
    }
  },
  "sort": [
    {
      "timestamp": {
        "order": "asc"
      },
      "_id": {
        "order": "desc"
      }
    }
  ],
  "search_after": [
    1614561419000,
    "6FxZJXgBE6QbUWetnarH"
  ]
}

scroll api

創(chuàng)建一個(gè)快照,有新的數(shù)據(jù)寫(xiě)入以后,無(wú)法被查到。每次查詢(xún)后,輸入上一次的 scroll_id。目前官方已經(jīng)不推薦使用這個(gè)API了,使用search_after即可。

GET kibana_sample_data_flights/_search?scroll=1m
{
  "size": 2,
  "query": {
    "match": {
      "DestWeather": "Sunny"
    }
  },
  "sort": [
    {
      "timestamp": {
        "order": "asc"
      },
      "_id": {
        "order": "desc"
      }
    }
  ]
}

在返回的數(shù)據(jù)中,有一個(gè)_scroll_id字段,下次搜索的時(shí)候帶上這個(gè)數(shù)據(jù),并且使用下面的查詢(xún)語(yǔ)句。

POST _search/scroll
{
  "scroll" : "1m",
  "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAA6UWWVJRTk9TUXFTLUdnU28xVFN6bEM4QQ=="
}

上面的scroll指定搜索上下文保留的時(shí)間,1m代表1分鐘,還有其他時(shí)間可以選擇,有d、h、m、s等,分別代表天、時(shí)、分鐘、秒。

搜索上下文有過(guò)期自動(dòng)刪除,但如果自己知道什么時(shí)候該刪,可以自己手動(dòng)刪除,減少資源占用。

DELETE /_search/scroll
{
  "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAA6UWWVJRTk9TUXFTLUdnU28xVFN6bEM4QQ=="
}

總結(jié)

from + size 的優(yōu)點(diǎn)是簡(jiǎn)單,缺點(diǎn)是在深度分頁(yè)的場(chǎng)景下系統(tǒng)開(kāi)銷(xiāo)比較大。

search after 可以實(shí)時(shí)高效的進(jìn)行分頁(yè)查詢(xún),但是它只能做下一頁(yè)這樣的查詢(xún)場(chǎng)景,不能隨機(jī)的指定頁(yè)數(shù)查詢(xún)。

scroll api 方案也很高效,但是它基于快照,不能用在實(shí)時(shí)性高的業(yè)務(wù)場(chǎng)景,且官方已不建議使用。

參考資料

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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