• elasticserach数据库深度分页查询的原理


    深度分页存在的问题

    https://segmentfault.com/a/1190000019004316?utm_source=tag-newest

    在实际应用中,分页是必不可少的,例如,前端页面展示数据给用户往往都是分页进行展示的。

    1、ES分页搜索
    Elasticsearch分页搜索采用的是from+size。from表示查询结果的起始下标,size表示从起始下标开始返回文档的个数。
    示例:

    什么是深分页(deep paging)?简单来说,就是搜索的特别深,比如总共有60000条数据,三个primary shard,每个shard上分了20000条数据,每页是10条数据,这个时候,你要搜索到第1000页,实际上要拿到的是10001~10010。

    注意这里千万不要理解成每个shard都是返回10条数据。这样理解是错误的!

    下面做一下详细的分析:
    请求首先可能是打到一个不包含这个index的shard的node上去,这个node就是一个协调节点coordinate node,那么这个coordinate node就会将搜索请求转发到index的三个shard所在的node上去。比如说我们之前说的情况下,要搜索60000条数据中的第1000页,实际上每个shard都要将内部的20000条数据中的第10001~10010条数据,拿出来,不是才10条,是10010条数据。3个shard的每个shard都返回10010条数据给协调节点coordinate node,coordinate node会收到总共30030条数据,然后在这些数据中进行排序,根据_score相关度分数,然后取到10001~10010这10条数据,就是我们要的第1000页的10条数据。
    如下图所示:

     deep paging问题就是说from + size分页太深,那么每个shard都要返回大量数据给coordinate node协调节点,会消耗大量的带宽,内存,CPU。

    深度分页问题之所以存在,是和Elasticsearch搜索内部执行原理分不开的。

    如果你想查询第5000-5100数据,查询官网API你很容易就知道,发送如下查询条件就可以做到:

    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

    {
        "query": {
            "match_all": {}
        },
        "from": 9990,
        "size": 10
    }
    
    
    {
        "query": {
            "match_all": {}
        },
        "from": 9999,
        "size": 10
    }

    我们在获取第9999条到10009条数据的时候,其实每个分片都会拿到10009条数据,然后集合在一起,总共是10009*3=30027条数据,针对30027数据再次做排序处理,最终会获取最后10条数据。

    如此一来,搜索得太深,就会造成性能问题,会耗费内存和占用cpu。而且es为了性能,他不支持超过一万条数据以上的分页查询。那么如何解决深度分页带来的性能呢?其实我们应该避免深度分页操作(限制分页页数),比如最多只能提供100页的展示,从第101页开始就没了,毕竟用户也不会搜的那么深,我们平时搜索淘宝或者百度,一般也就看个10来页就顶多了。

    查询请求:

    POST auditlog_operation/operlog/_search

    {

    “from”:10000

    “size”:100

    }

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

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

    {"error":{"root_cause":[{"type":"query_phase_execution_exception","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":"search_phase_execution_exception","reason":"allshards failed","phase":"query_fetch","grouped":true,"failed_shards":[{"shard":0,"index":"auditlog_operation","node":"iqu-KVKjTRmT3YcT9XAu_w","reason":{"type":"query_phase_execution_exception","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."}}]},"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(游标)。

    https://blog.csdn.net/lisongjia123/article/details/79041402

    https://www.sohu.com/a/165387407_465944

    https://segmentfault.com/a/1190000019004316?utm_source=tag-newest

    除了效率上的问题,还有一个无法解决的问题是,es 目前支持最大的 skip 值是 max_result_window ,默认

    为 10000 。也就是当 from + size > max_result_window 时,es 将返回错误

    [root@dnsserver ~]# curl -XGET 127.0.0.1:9200/custm/_settings?pretty 
    {
      "custm" : {
        "settings" : {
          "index" : {
            "max_result_window" : "50000",
             ....
           }
        }
      }
    }
     

    最开始的时候是线上客户的es数据出现问题,当分页到几百页的时候,es 无法返回数据,此时为了恢复正常使用,我们采用了紧急规避方案,就是将 max_result_window 的值调至 50000,其中custm是索引的名称,http请求提交的方式必须是put请求

    [root@dnsserver ~]# curl -XPUT "127.0.0.1:9200/custm/_settings" -d 
    '{ 
        "index" : { 
            "max_result_window" : 50000 
        }
    }'
     

    然后这种方式只能暂时解决问题,当es 的使用越来越多,数据量越来越大,深度分页的场景越来越复杂时,如何解决这种问题呢?

    custm
  • 相关阅读:
    Java并发编程:synchronized
    对一致性Hash算法,Java代码实现的深入研究
    在Xcode中使用Git进行源码版本控制
    这些 Git 技能够你用一年了
    简明 Git 命令速查表
    Git 分支管理是一门艺术
    2016年Web前端面试题目汇总
    iOS 面试基础题目
    Json数据交互格式介绍和比较
    SpringBoot端口和访问路径
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/12987290.html
Copyright © 2020-2023  润新知