Elasticsearch大数据下深度分页问题

Elasticsearch是一款非常优秀的实时分布式搜索分析引擎,基于它能够达到实时搜索、稳定、可靠、快速、安装方便等优点,目前已经被广泛使用。在使用过程中不可回避的会遇到大数据的遍历、深度分页等问题,下面基于Easticsearch官网和实践测试经验来小结下Elasticsearch深度分页问题。

深度分页存在的问题

深度分页问题之所以存在,是和Elasticsearch搜索内部执行原理分不开的。如果想查询第5000-5100数据,发送如下查询条件就可以做到:

POST auditlog_operation/operlog/_search

    {

        “from”:5000   //from:定义从哪里开始拿数据

        “size”:100    //size:定义一共拿多少条数据

    }

查询流程如下:

  1. 客户端发送请求到某个node节点。

  2. 此node将请求广播到各分片,各分片各自查询前5100条数据。

  3. 查询结果返回给node节点,node对结果进行合并整合,取出前5100条数据。

  4. 返回给客户端。

这种查询下,如果要深度获取1000000到1000100页的数据,性能问题会非常明显的暴露出来:CPU、内存、IO、网络带宽等等,而且Elasticsearch本身就是个Java应用,若并发上去,Elasticsearch会快就会OOM。查询请求:

POST auditlog_operation/operlog/_search

    {

        “from”:10000

        “size”:100

    }

 如果尝试发送上述from+size请求来获取10000-10100条数据,会返回错误:

{
    "error": {
        "failed_shards": [
            {
                "index": "auditlog_operation",
                "node": "iqu-KVKjTRmT3YcT9XAu_w",
                "reason": {
                    "reason": "Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index level parameter.",
                    "type": "query_phase_execution_exception"
                },
                "shard": 0
            }
        ],
        "grouped": true,
        "phase": "query_fetch",
        "reason": "allshards failed",
        "root_cause": [
            {
                "reason": "Resultwindow is too large, from + size must be less than or equal to: [1000000] butwas [1000100]. See the scroll api for a more efficient way to request largedata sets. This limit can be set by changing the [index.max_result_window]index levelparameter.",
                "type": "query_phase_execution_exception"
            }
        ],
        "type": "search_phase_execution_exception"
    },
    "status": 500
}

注意 from+size不再适用于查询数据超过index.max_result_window设置值,此默认值为10000。查看 Scroll 或 Search After来获取更高效的深层分页(滚动)。由此可以得到两个结论:

  1. 你可以修改index.max_result_window设置值来继续使用from+size做分页查询。当然效率肯定不高。

  2. 如果要找更高效的深度分页方式,请使用Scroll  或者Search After。

Scroll API

Scroll API更适用于检索大量数据(甚至全部数据)。它先做一个初始阶段搜索然后持续批量从Elasticsearch里拉取结果直到返回结果为空。这有点像传统数据库里的cursors(游标)。

Scroll API

   Scroll分两步来完成整个遍历过程:

1、初始化

POST   index/type/_search?scroll=1m

    {
        "query": { "match_all": {}}
    }

参数解析:

  • 和普通查询一样,可以指定index、type、查询条件等。

  • Scroll:初始化请求必须指定Scroll参数,此参数告诉Elasticsearch 他要保存此次搜索的上下文多长时间。

2、遍历

POST /_search?scroll=1m
    {
        "scroll_id":"XXXXXXXXXXXXXXXXXXXXXXX I am scroll id XXXXXXXXXXXXXXX"
    }

参数解析:

  • scroll_id:初始化或上次遍历的返回的scroll_id。

  • Scroll:必须指定上下文搜索的保持时间,超时scroll_id就过期不能再使用。但是设置时间不可过长,只要能让其保持到下一次遍历(两次遍历时间)完成即可。

  • index、type不用指定。

  • 默认每次取10条。若需要修改,添加size参数,size的设置也要考虑性能问题,过大一次取数据过多不是自己应用OOM就是es服务OOM。

  • 重复此请求直到返回数据为空,遍历结束。

清除scroll_id

Scroll为了保证遍历不会因为超时而失败,开发人员可能会把超时时间设置稍长,或者遍历被人为中途停止,这个时候可能需要调用清除scroll_id接口。

DELETE    /_search/scroll

    {  

        "scroll_id" : ["c2NhbjsxOzY0OmRVRUN4TG5TUXYyaXRIT0Q5SUxiR0E7MTt0b3RhbF9oaXRzOjIwNjgxMjg7"]  

    }

DELETE   /_search/scroll/_all

参数解析:

  • scroll_id数组:传入要删除的id数组。

  • 若全部清除,可直接传入all。 

实际分页应用场景

Scroll API只能遍历全部数据,顶多做到向后翻页,不能向前翻页或者直接跳转到某一页。那在实际使用大数据量场景中,需要实现向前向后翻、直接跳转到某页等功能。就目前已知的Elasticsearch查询,无法做到真正意义的分页实际应用。经过测试和分析,打算结合Scroll API和from+size,融合两者的特点来实现真正意义上的分页查询。

举例:数据库有5649条数据,id依次从1到5649。

1、先使用Scroll API进行遍历全部数据(结果按照id排序)。

2、依次遍历,每次取1000条数据(取多少根据内存考虑),缓存起始id。

上例缓存的id数组为:【1,1001,2001,3001,4001,5001】

3、分页查询时依然使用from+size,只是会根据上述取到的id数据先进行一次过滤,再在1000数据内使用from+size来处理。

例如按照每页100条数据,用户想直接查看第32页的数据的话,那就是发送请求以下请求:

POST auditlog_operation/operlog/_search

    {

        “from”:2,

        “size”:100,   //这1000里取第二页的100条

        "query": {

            "bool":{

                "must":[{

                    "range":{

                        "id":{

                            "gte":"3001",

                            "lt":"4001"   //先将此范围限制在3001-4000条之间

                        }

                    }

                }],

                "must_not":[],

                "should":[]

            }

        },

    }
原文地址:https://www.cnblogs.com/johnvwan/p/15544410.html