Java架构直通车——ElasticSearch深度分页解决方案

分页查询

分页查询是这样的:

POST     /shop/_doc/_search
{
    "query": {
        "match_all": {}
    },
    "from": 0,
    "size": 10
}

不过,当查询是这样时,会报错:

POST     /shop/_doc/_search
{
    "query": {
        "match_all": {}
    },
    "from": 9999,
    "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 [10009]. 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."
            }
        ],
...

这种结果就是深度分页。

深度分页

深度分页其实就是搜索的深浅度,比如第1页,第2页,第10页,第20页,是比较浅的;第10000页,第20000页就是很深了。

这其实是ES限制了查询结果的大小,也就是限制了result window的大小。为什么会进行这样的限制呢,首先,我们的ES是分片的。
在这里插入图片描述
由于这个分片的存在,我们想要获得size:10,from:0的数据时,需要在0,1,2三个分片中,分别取出10个数据,然后再做排序。
而如果从size:10,from:9999开始,每个分片都会拿到10009条数据,然后集合在一起,总共是10009*3=30027条数据,针对30027数据再次做排序处理,最终会获取最后10条数据。这样做的代价太高了,会造成性能问题,会耗费内存和占用cpu。而且es为了性能,他不支持超过一万条数据以上的分页查询。

深度分页解决方案

方案1:限制分页最大数
其实我们应该避免深度分页操作(限制分页页数),比如最多只能提供100页的展示,从第101页开始就没了,毕竟用户也不会搜的那么深,我们平时搜索淘宝或者百度,一般也就看个10来页就顶多了。

譬如淘宝搜索限制分页最多100页,如下:
在这里插入图片描述

方案2:修改result_window
有时候,由于业务需要,我们不得不去搜索到10000条数据之后的内容,可以通过修改result_window来实现:

GET     /shop/_settings

PUT     /shop/_settings
{ 
    "index.max_result_window": "20000"
}

方案3:使用scroll搜索
滚动搜索可以先查询出一些数据,然后再紧接着依次往下查询。在第一次查询的时候会有一个滚动id,相当于一个锚标记,随后再次滚动搜索会需要上一次搜索的锚标记,根据这个进行下一次的搜索请求。每次搜索都是基于一个历史的数据快照,查询数据的期间,如果有数据变更,那么和搜索是没有关系的,搜索的内容还是快照中的数据。

scroll=1m,相当于是一个session会话时间,搜索保持的上下文时间为1分钟。

POST    /shop/_search?scroll=1m
{
    "query": { 
    	"match_all": {
    	}
    },  
    "sort" : ["_doc"], 
    "size":  5
}

其返回结果如下:

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAACgFmkxLWVTcGxRUkY2bmNRUnd4RWVXOEEAAAAAAAAAoRZpMS1lU3BsUVJGNm5jUVJ3eEVlVzhBAAAAAAAAAKIWaTEtZVNwbFFSRjZuY1FSd3hFZVc4QQ==",
    "took": 17,
    "timed_out": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
...        

利用返回的结果,我们进行如下查询:

POST    /_search/scroll  这里不能带index
{
    "scroll": "1m", 
    "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAACgFmkxLWVTcGxRUkY2bmNRUnd4RWVXOEEAAAAAAAAAoRZpMS1lU3BsUVJGNm5jUVJ3eEVlVzhBAAAAAAAAAKIWaTEtZVNwbFFSRjZuY1FSd3hFZVc4QQ=="
}

其结果为:

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAACsFmkxLWVTcGxRUkY2bmNRUnd4RWVXOEEAAAAAAAAArhZpMS1lU3BsUVJGNm5jUVJ3eEVlVzhBAAAAAAAAAK0WaTEtZVNwbFFSRjZuY1FSd3hFZVc4QQ==",
    "took": 45,
    "timed_out": false,
    "terminated_early": false,
    "_shards": {
        "total": 3,
        "successful": 3,
        "skipped": 0,
        "failed": 0
    },
    ...
发布了385 篇原创文章 · 获赞 326 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/No_Game_No_Life_/article/details/104376924