一文带走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以下为最佳

避免每页返回几千条数据。

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