一文帶走Elasticsearch之deep paging問題以及解決方案之scroll

一、分頁API

1、api

from:從第幾條開始
size:每頁幾條

2、demo

2.1、查詢前兩條數據

GET /_search?size=2GET /_search?from=0&size=2

2.2、查詢第50~60條數據

GET /_search?from=50&size=10

二、deep paging

1、描述

就是深度分頁,假設有6w條數據平分到了3個shard中,假設需求是分頁,數據太多了,要查第1000頁的數據,假設每頁10條,第1000頁的數據也就是第1w條。由於6w條數據,3個shard,所以大約每個shard上2w條數據。那麼我們客觀的認爲應該是協調節點從三個shard中分別拿出10條數據(第10001到10010這十條),然後再協調節點中將這30條數據根據規則獲取想要的前10條。但是我們的想法很天真,實際ES的做法是將三個shard上的1~10010條數據分別返回給協調節點,也就是協調節點最終的結果是30030條數據,在這30030條數據的基礎上進行按照規則(比如先排序然後再分頁取前10)返回給客戶端,所以造成了性能的浪費和瓶頸所在。

2、圖解

在這裏插入圖片描述

三、scroll解決方案

1、場景

就跟js的懶加載一樣,滾動加載。也可以用於解決深度分頁問題。

2、原理

scroll搜索會在第一次搜索的時候,保存一個當時的試圖快照。也就是保存完快照後之後的更改都不會對這個快照的數據產生影響,你改你的,我查的還是老的。
默認採用基於_doc的創建時間來排序,不使用_score進行排序的方式,性能較高。

3、用法

3.1、數據準備

PUT /product/_doc/1
{
    "name": "xiaomi shouji",
    "desc": "niubi quanwangtong",
    "tags": ["niubi", "quanwangtong", "xiaomi", "shouji"]
}

PUT /product/_doc/2
{
    "name": "huawei shouji",
    "desc": "4G 5G",
    "tags": ["shouji"]
}

PUT /product/_doc/3
{
    "name": "xiaomi shouhuan",
    "desc": "quanzidong",
    "tags": ["shengdian", "xiaomi", "shouji"]
}

3.2、scroll api

GET /product/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "size": 1
}

返回結果

{
  "_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==",
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "xiaomi shouji",
          "desc" : "niubi quanwangtong",
          "tags" : [
            "niubi",
            "quanwangtong",
            "xiaomi",
            "shouji"
          ]
        }
      }
    ]
  }
}

獲取的結果會有一個scroll_id,下一次在發送scroll請求時,必須帶上這個scroll_id,因爲只有這樣,纔會去查詢上一次scroll生成的快照。

GET /_search/scroll
{
    "scroll": "1m", 
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ=="
}

返回結果

{
  "_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==",
  "took" : 1,
  "timed_out" : false,
  "terminated_early" : true,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "product",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "name" : "huawei shouji",
          "desc" : "4G 5G",
          "tags" : [
            "shouji"
          ]
        }
      }
    ]
  }
}

可以發現這個數據是id=2的。以此類推,拿着id=2的scroll_id去查會查到id=3的。但是這期間有新的數據插入進來了,是不會被查到的,因爲查的是快照。

3.3、補充說明

  • 指定的1m代表的是持續滾動時間,如果過了1分鐘,還沒有查詢下一頁,那麼這個scroll_id就會被es清理掉。就無法用這個scroll_id去查數據。還可以指定1s,代表1秒鐘。
  • 刪除scroll_id

DELETE /_search/scroll/DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAQoAWOWV3WnBTMVNSNFc1ZXAzeGxlWGJ1UQ==

  • 刪除所有scroll_id

DELETE /_search/scroll/_all

四、優化

  • 當你的數據分頁超過5000的時候,不用用deep paging

也就是比如1w條數據,假設每頁10條,1000頁。這時候第5000條數據在第500頁,正常人你去百度搜索,你也就點前幾頁就完了,誰會點到幾百頁。但是不能排除這種情況,所以建議前N頁採取from/size,在之後就採取scroll,但是scroll不支持上一頁的操作,看產品需求。

  • 返回結果500以下爲最佳

避免每頁返回幾千條數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章