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":[]
    
                }
    
            },
    
        }
  • 相关阅读:
    六种排序
    洛谷 P1879 [USACO06NOV]玉米田Corn Fields
    [USACO06NOV]玉米田Corn Fields
    c++位运算符 | & ^ ~ && ||,补码,反码
    Blockade(Bzoj1123)
    割点(Tarjan算法)【转载】
    子串
    生命是什么
    怎样说话才打动人
    自控力
  • 原文地址:https://www.cnblogs.com/johnvwan/p/15544410.html
Copyright © 2011-2022 走看看