zoukankan      html  css  js  c++  java
  • 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":[]
    
                }
    
            },
    
        }
  • 相关阅读:
    (转载)C++ string中find() ,rfind() 等函数 用法总结及示例
    UVA 230 Borrowers (STL 行读入的处理 重载小于号)
    UVA 12100 打印队列(STL deque)
    uva 12096 The SetStack Computer(STL set的各种库函数 交集 并集 插入迭代器)
    uva 1592 Database (STL)
    HDU 1087 Super Jumping! Jumping! Jumping!
    hdu 1176 免费馅饼
    HDU 1003 Max Sum
    转战HDU
    hust 1227 Join Together
  • 原文地址:https://www.cnblogs.com/johnvwan/p/15544410.html
Copyright © 2011-2022 走看看