ES elasticsearch分頁3種方式及優劣勢


1、關於 Elasticsearch 分頁查詢,這幾個問題經常被問到

  • 問題1:想請問下,一次性獲取索引上的某個字段的所有值(100 萬左右),除了把 max_result_window 調大 ,還有沒有啥方法?

  • 問題2:關於 es 的分頁,每次拿 20 條展示在前台,然后點擊下一頁,在查詢后面的20條數據,應該要怎么寫?

  • 問題3:From+size、Scroll、search_after 的本質區別和應用場景分別是什么?

2、 Elasticsearch 支持的三種分頁查詢方式

  • From + Size 查詢
  • Search After 查詢
  • Scroll 查詢

下面我就三種方式的聯系與區別、優缺點、適用場景等展開進行解讀。

2.1 From + size 分頁查詢

2.1.1 From + size 分頁查詢定義與實戰案例

如下基礎查詢:

GET kibana_sample_data_flights/_search

默認返回前10個匹配的匹配項。其中:

  • from:未指定,默認值是 0,注意不是1,代表當前頁返回數據的起始值。
  • size:未指定,默認值是 10,代表當前頁返回數據的條數。

如下指定條件查詢和排序:

GET kibana_sample_data_flights/_search
{
  "from": 0,
  "size":20,
  "query": {
    "match": {
      "DestWeather": "Sunny"
    }
  },
  "sort": [
    {
      "FlightTimeHour": {
        "order": "desc"
      }
    }
  ]
}

共返回 20 條數據。

其中:from + size 兩個參數定義了結果頁面顯示數據的內容。

2.1.2 From + size 查詢優缺點及適用場景

From + size 查詢優點

  • 支持隨機翻頁。

From + size 查詢缺點

  • 受制於 max_result_window 設置,不能無限制翻頁。

  • 存在深度翻頁問題,越往后翻頁越慢。

From + size 查詢適用場景

第一:非常適合小型數據集或者大數據集返回 Top N(N <= 10000)結果集的業務場景。

第二:類似主流 PC 搜索引擎(谷歌、bing、百度、360、sogou等)支持隨機跳轉分頁的業務場景。

 

 

 

2.1.3 深度翻頁不推薦使用 From + size

Elasticsearch 會限制最大分頁數,避免大數據量的召回導致性能低下。

Elasticsearch 的 max_result_window 默認值是:10000。也就意味着:如果每頁有 10 條數據,會最大翻頁至 1000 頁。

實際主流搜索引擎都翻不了那么多頁,舉例:百度搜索“上海”,翻到第 76 頁,就無法再往下翻頁了,提示信息如下截圖所示:

 

 

如下的分頁查詢

GET kibana_sample_data_flights/_search
{
  "from": 0,
  "size":10001
}

GET kibana_sample_data_flights/_search
{
  "from": 10001,
  "size":10
}

報錯如下:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "illegal_argument_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."
      }
    ],

什么原因?超過了最大窗口的限制,index.max_result_window 默認值為10000。

報錯信息還同時給出了兩個解決方案:

  • 方案一:大數據集召回數據使用:scroll api。

后面會詳細講解。

  • 方案二:調大 index.max_result_window 默認值。
PUT kibana_sample_data_flights/_settings
{
    "index.max_result_window":50000
}

官方建議:避免過度使用 from 和 size 來分頁或一次請求太多結果。

不推薦使用 from + size 做深度分頁查詢的核心原因:

  • 搜索請求通常跨越多個分片,每個分片必須將其請求的命中內容以及任何先前頁面的命中內容加載到內存中。

  • 對於翻頁較深的頁面或大量結果,這些操作會顯著增加內存和 CPU 使用率,從而導致性能下降或節點故障。

什么意思呢?

GET kibana_sample_data_flights/_search
{
  "from": 10001,
  "size": 10
}

共 10 條數據加載到內存嗎?不是的!

共:10011 條數據加載到內存,然后經過后台處理后返回了最后 10 條我們想要的數據。

那也就意味着,越往后翻頁(也就是深度翻頁)需要加載的數據量越大,勢必會越耗費 CPU + 內存資源,響應也會越慢!

2.2 search_after 查詢

2.2.1 search_after 查詢定義與實戰案例

search_after 查詢本質:使用前一頁中的一組排序值來檢索匹配的下一頁。

前置條件:使用 search_after 要求后續的多個請求返回與第一次查詢相同的排序結果序列。也就是說,即便在后續翻頁的過程中,可能會有新數據寫入等操作,但這些操作不會對原有結果集構成影響。

如何實現呢?

可以創建一個時間點 Point In Time(PIT)保障搜索過程中保留特定事件點的索引狀態。

Point In Time(PIT)是 Elasticsearch 7.10 版本之后才有的新特性。

PIT的本質:存儲索引數據狀態的輕量級視圖。

如下示例能很好的解讀 PIT 視圖的內涵。

# 創建 PIT
POST kibana_sample_data_logs/_pit?keep_alive=1m

# 獲取數據量 14074
POST kibana_sample_data_logs/_count

# 新增一條數據
POST kibana_sample_data_logs/_doc/14075
{
  "test":"just testing"
}

# 數據總量為 14075
POST kibana_sample_data_logs/_count


# 查詢PIT,數據依然是14074,說明走的是之前時間點的視圖的統計。
POST /_search
{
  "track_total_hits": true, 
  "query": {
    "match_all": {}
  }, 
   "pit": {
    "id": "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEN3RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA"
  }
}

有了 PIT,search_after 的后續查詢都是基於 PIT 視圖進行,能有效保障數據的一致性。

search_after 分頁查詢可以簡單概括為如下幾個步驟。

步驟 1:創建 PIT 視圖,這是前置條件不能省。

# Step 1: 創建 PIT
POST kibana_sample_data_logs/_pit?keep_alive=5m

返回結果如下:

{
  "id" : "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA"
}

keep_alive=5m,類似scroll的參數,代表視圖保留時間是 5 分鍾,超過 5 分鍾執行會報錯如下:

  "type" : "search_context_missing_exception",
  "reason" : "No search context found for id [91600]"

步驟 2:創建基礎查詢語句,這里要設置翻頁的條件。

# Step 2: 創建基礎查詢
GET /_search
{
  "size":10,
  "query": {
    "match" : {
      "host" : "elastic"
    }
  },
  "pit": {
     "id":  "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA", 
     "keep_alive": "1m"
  },
  "sort": [ 
    {"response.keyword": "asc"}
  ]
}
  • 設置了PIT,檢索時候就不需要再指定索引。

  • id 是基於步驟1 返回的 id 值。

  • 排序 sort 指的是:按照哪個關鍵字排序。

在每個返回文檔的最后,會有兩個結果值,如下所示:

 "sort" : [
          "200",
          4
        ]
  • 其中,“200”就是我們指定的排序方式:基於 {"response.keyword": "asc"} 升序排列。

而 4 代表什么含義呢?

  • 4 代表——隱含的排序值,是基於_shard_doc 的升序排序方式。

官方文檔把這種隱含的字段叫做:tiebreaker (決勝字段),tiebreaker 等價於_shard_doc。

tiebreaker 本質含義:每個文檔的唯一值,確保分頁不會丟失或者分頁結果數據出現重復(相同頁重復或跨頁重復)。

步驟3:實現后續翻頁。

# step 3 : 開始翻頁
GET /_search
{
  "size": 10,
  "query": {
    "match" : {
      "host" : "elastic"
    }
  },
  "pit": {
     "id":  "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA", 
     "keep_alive": "1m"
  },
  "sort": [
    {"response.keyword": "asc"}
  ],
  "search_after": [                                
    "200",
    4
  ]
}

后續翻頁都需要借助 search_after 指定前一頁的最后一個文檔的 sort 字段值。

如下代碼所示:

  "search_after": [                                
    "200",
    4
  ]

顯然,search_after 查詢僅支持向后翻頁。

2.2.2 search_after 查詢優缺點及適用場景

search_after 優點

  • 不嚴格受制於 max_result_window,可以無限制往后翻頁。

ps:不嚴格含義:單次請求值不能超過 max_result_window;但總翻頁結果集可以超過。

search_after 缺點
  • 只支持向后翻頁,不支持隨機翻頁。

search_after 適用場景

  • 類似:今日頭條分頁搜索  https://m.toutiao.com/search

不支持隨機翻頁,更適合手機端應用的場景。

圖片

2.3 Scroll 遍歷查詢

2.3.1 Scroll 遍歷查詢定義與實戰案例

相比於 From + size 和 search_after 返回一頁數據,Scroll API 可用於從單個搜索請求中檢索大量結果(甚至所有結果),其方式與傳統數據庫中游標(cursor)類似。

如果把  From + size 和 search_after 兩種請求看做近實時的請求處理方式,那么 scroll 滾動遍歷查詢顯然是非實時的。數據量大的時候,響應時間可能會比較長。

scroll 核心執行步驟如下:

步驟 1:指定檢索語句同時設置 scroll 上下文保留時間。

實際上,scroll 已默認包含了 search_after 的PIT 的視圖或快照功能。

從 Scroll 請求返回的結果反映了發出初始搜索請求時索引的狀態,類似在那一個時刻做了快照。隨后對文檔的更改(寫入、更新或刪除)只會影響以后的搜索請求。

POST kibana_sample_data_logs/_search?scroll=3m
{
  "size": 100,
  "query": {
    "match": {
      "host": "elastic"
    }
  }
}

步驟 2:向后翻頁繼續獲取數據,直到沒有要返回的結果為止。

POST _search/scroll                                   
{
  "scroll" : "3m",
  "scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFkY4UkIwZWtlU2d1OTdTUjRIbzVXdHcAAAAAAAGmkBZ0bVM5YUxMX1R1Nkd1VkNiaGhZSWNn" 
}

scroll_id 值是步驟 1 返回的結果值。

2.3.2 Scroll 遍歷查詢優缺點及適用場景

    scroll 查詢優點

  • 支持全量遍歷。

ps:單次遍歷的 size 值也不能超過 max_result_window 大小。

    scroll 查詢缺點

  • 響應時間非實時。

  • 保留上下文需要足夠的堆內存空間。

scroll 查詢適用場景

  • 全量或數據量很大時遍歷結果數據,而非分頁查詢。

  • 官方文檔強調:不再建議使用scroll API進行深度分頁。如果要分頁檢索超過 Top 10,000+ 結果時,推薦使用:PIT + search_after。

3、小結

  • From+ size:需要隨機跳轉不同分頁(類似主流搜索引擎)、Top 10000 條數據之內分頁顯示場景。

  • search_after:僅需要向后翻頁的場景及超過Top 10000 數據需要分頁場景。

  • Scroll:需要遍歷全量數據場景 。

  • max_result_window:調大治標不治本,不建議調過大。

  • PIT:本質是視圖。

  •  

     

 

 

 

 

轉載自銘毅天下公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM