Elasticsearch聚合后分页深入详解

1、Elasticsearch支持聚合后分页吗,为什么?

不支持,看看Elasticsearch员工如何解读。
这里写图片描述
这里写图片描述
这个问题,2014年在github上有很长的讨论。究其为什么ES不支持聚合后分页?可概括如下:
1)性能角度——聚合分页会在大量的记录中产生性能问题。
2)正确性角度——聚合的文档计数不准确。
所以奇怪的事情可能会发生,如第二页的第一项具有比第一页的最后一个元素更高的计数。

具体为什么会不正确?
这是因为每个分片都提供了自己对有序列表应该是什么的看法,并将这些列表结合起来给出最终的结果值。
举例如下:
对于如下的聚合:聚合出产品数据量的前5名

GET /_search
{
   "aggs" : {
       "products" : {
           "terms" : {
               "field" : "product",
               "size" : 5
           }
       }
   }
}

步骤1: 三个分片的统计计数如下:
这里写图片描述
步骤2:各分片取前5名。
这里写图片描述
步骤3:依据各分片前5名,聚合得出总前5名。
这里写图片描述
仅以产品C的排名作为举例,产品C(50个)的数据来自分片A(6个)和分片C(44个)之和。
所以,排名第三。
实际产品C在分片B中还存在4个,只不过这四个按照排名处于第10位,取前5的时候,显然取不到。
所以,导致聚合结果不准确。
官网有详细举例解读。

2、Elasticsearch要实现聚合后分页,该怎么办?

方案:需要展示满足条件的全部数据条数,即需要全量聚合,且按照某规则排序。
记住,如果数据基数大(十万、百万甚至千万级),这必然会很慢。

步骤1:全量聚合,size设置为: 2147483647。
ES5.X/6.X版本设置为2147483647 ,它等于2^31-1,
是32位操作系统中最大的符号型整型常量;ES1.X 2.X版本设置为0。

步骤2:将聚合结果存入内存中,可以考虑list或map存储。
这里存入list的_id是基于某种规则排序过的,如:基于插入时间。

步骤3:内存内分页,基于list中存储值结合偏移值进行筛选。
如每页10条数据,取第一页就是:取list中第0到第9个元素,以此类推。

步骤4:基于筛选出的值进行二次查询获取详情。
此处的筛选条件已经能唯一确定一篇document。

3、“聚合后不能分页,但能分区来取”,是什么鬼?

这里写图片描述
貌似,没有起到分页的作用。此处没有深入研究。

4、聚合后分页实战

步骤1:建立索引

PUT book_index
{
  "mappings": {
  "book_type": {
  "properties": {
  "_key": {
  "type": "keyword",
  "ignore_above": 256
  },

  "pt": {
  "type": "date"
  },

  "url": {
  "type": "keyword",
  "ignore_above": 256
  },
  "title": {
  "type": "text",
  "term_vector": "with_positions_offsets",
  "fields": {
  "keyword": {
  "type": "keyword",
  "ignore_above": 256
  }
  },
  "analyzer": "ik_smart"
  },
  "abstr": {
  "type": "text",
  "term_vector": "with_positions_offsets",
  "fields": {
  "keyword": {
  "type": "keyword",
  "ignore_above": 256
  }
  },
  "analyzer": "ik_smart"
  },
  "rplyinfo": {
  "type": "text",
  "term_vector": "with_positions_offsets",
  "fields": {
  "keyword": {
  "type": "keyword",
  "ignore_above": 256
  }
  },
  "analyzer": "ik_smart"
  },
  "author": {
  "type": "keyword",
  "ignore_above": 256
  },
  "booktype": {
  "type": "keyword",
  "ignore_above": 256
  },
  "price": {
  "type": "long"
  }
  }
  }
  }
}

步骤2:导入数据

举例原因,假设后来导入百万甚至千万级别数据。

    POST book_index/book_type/1
    {
     "title":"《Elasticsearch深入理解》",
     "author":"ERicif",
     "abstr":"Elasticsearch实战书籍",
     "relyinfo":"不错,值得推荐",
     "booktype":"技术",
     "price":79,
     "pt":1543611840000
    }
POST book_index/book_type/2
{
  "title":"《大数据之路》",
  "author":"阿里巴巴",
  "abstr":"大数据实现",
  "relyinfo":"不错,值得推荐2",
  "booktype":"技术",
  "price":89,
  "pt":1543011840000
}

POST book_index/book_type/3
{
  "title":"《人性的弱点》",
  "author":"卡耐基",
  "abstr":"直击人性",
  "relyinfo":"不错,值得推荐2",
  "booktype":"励志",
  "price":59,
  "pt":1543101840000
}

POST book_index/book_type/4
{
  "title":"《Flow案例精编》",
  "author":"ERicif",
  "abstr":"Flow案例",
  "relyinfo":"还可以",
  "booktype":"技术",
  "price":57,
  "pt":1543201840000
}

POST book_index/book_type/5
{
  "title":"《kibana案例精编》",
  "author":"ERicif",
  "abstr":"kibana干货",
  "relyinfo":"还可以,不孬",
  "booktype":"技术",
  "price":53,
  "pt":1480539840000
}

步骤3:聚合

要求:按照以下条件聚合
1)相同作者出书量;(聚合)
2)相同作者的书,取时间最大的返回。(聚合后排序)

        POST book_index/_search
        {
          "sort": [
          {
          "pt": "desc"
          }
          ],
          "aggs": {
          "count_over_sim": {
          "terms": {
          "field": "author",
          "size": 2147483647,
          "order": {
          "pt_order": "desc"
          }
          },
          "aggs": {
          "pt_order": {
          "max": {
          "field": "pt"
          }
          }
          }
          }
          },
          "query": {
          "bool": {
          "must": [
          {
          "bool": {
          "should": [
          {
          "match": {
          "booktype": "技术"
          }
          }
          ]
          }
          },
          {
          "range": {
          "pt": {
          "gte": 1451595840000,
          "lte": 1603201840000
          }
          }
          }
          ]
          }
          },
          "_source": {
          "includes": [
          "title",
          "abstr",
          "pt",
          "booktype",
          "author"
          ]
          },
          "from": 0,
          "size": 10,
          "highlight": {
          "pre_tags": [
          "<span style=\"color:red\">"
          ],
          "post_tags": [
          "</span>"
          ],
          "fields": {
          "title": {}
          }
          }
        }

步骤4:获取关键信息存入list。

步骤5:二次遍历+偏移截取分页实现。

5、Elasticsearch聚合+分页速度慢,该如何优化?

优化方案:改为广度搜索方式。
“collect_mode” : “breadth_first”,
[ES官网]如果数据量越大,那么默认的使用深度优先的聚合模式生成的总分组数就会非常多,但是预估二级的聚合字段分组后的数据量相比总的分组数会小很多所以这种情况下使用广度优先的模式能大大节省内存,从而通过优化聚合模式来大大提高了在某些特定场景下聚合查询的成功率。

6、小结

待聚合的大小size取值越大,结果就越精确,而且计算最终结果的代价也越高;
耗时主要体现在:
第一:分片级别巨大的优先级队列的管理成本;
第二:集群节点和客户端之间的数据传输成本。

7、认知升级

思路优化:
1、聚合后分页的实现通过topHits Agg方式,而非term Agg。
好处:
1)支持自定义返回字段:_source 方式定义。
2)聚合后返回结果,而非二次再检索。

具体实现:

步骤1:结合原来query实现topHits聚合;并指定待返回字段。

步骤2:考虑分页实现,采取from+size的实现方式。

假定每页限定10条数据,可自定义。
1)取第1-10条,page=1, size=10;top_hits聚合传入的参数是10(10=page*size);

2)取第11—20条,page=2,size=10; top_hits聚合传入的参数是20(20=page*size);
此时,注意:会聚合生成top20的数据,返回给前端的时候,返回最后size条数据;
…..

3)取最后一页,最后几条数据,不一定是10(<=10)条。
举例:假定共187条数据,共应该分成18+1=19页。
最后一页,请求:page=19,size=187-(page-1)*size=7页。
top_hits聚合传入的参数是:190(190=page*size)。

步骤3:组合数据json串,返回给前端。

参考:

[1]Git解读:http://t.cn/RQpTzSH
[2]广度优先遍历:http://t.cn/RHndSgY
[3]分区聚合:http://t.cn/RQpTbdO

——————————————————————————————————
更多ES相关实战干货经验分享,请扫描下方【铭毅天下】微信公众号二维码关注。
(每周至少更新一篇!)

这里写图片描述
和你一起,死磕Elasticsearch
——————————————————————————————————

2018-1-20
作者:铭毅天下
转载请标明出处,原文地址:
http://blog.csdn.net/laoyang360/article/details/79112946
如果感觉本文对您有帮助,请点击‘顶’支持一下,您的支持是我坚持写作最大的动力,谢谢!

Elasticsearch 简介 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。 elasticSearch 的使用场景 1、在海量数据前提下,对数据进行检索。比如:京东,淘宝等电商项目 课程目标: 1. 了解企业级搜索引擎 2. 安装elasticsearch {linux 系统} 3. 安装kibana并利用kibana对Elasticsearch 索引中的数据进行搜索、查看、交互操作 4. 项目实战{ELK} 课程目录: 01 课程介绍 02 elasticsearch 简介 03 elasticsearch 使用场景 04 安装elasticsearch 之前先安装jdk 05 安装elasticsearch 06 测试elasticsearch是否安装成功  07 安装kibana 08 elasticsearch 基本认识 以及添加索引和删除索引 09 elasticsearch 添加查询数据 10 elasticsearch 修改删除数据 11 elasticsearch 有条件的查询 12 分词子属性fuzzy查询 13 elasticsearch 过滤使用 14 elasticsearch 排序与分页 15 elasticsearch 如何查询指定的字段 16 elasticsearch 高亮显示 17 elasticsearch 聚合 18 elasticsearch mapping 概念 19 elasticsearch 的中文词库 20 elasticsearch 中文词库安装测试 21 elasticsearch 中文词库的使用案例 22 elasticsearch 自定义词库配置 23 安装nginx 配置中文词库 24 测试elasticsearch 自定义中文词库 25 搭建项目父工程 26 搭建项目bean-interface-common 27 搭建search 的service web 项目 28 测试项目是否能与elasticsearch联通 29 创建数据库并搭建首页 30 数据上传功能的实现类完成 31 数据上传控制器完成 32 dubbo 介绍以及安装zookeeper 33 将数据从mysql 上传到elasticsearch 中 34 elasticsearch查询功能分析 35 编写业务需求的dsl 语句 36 编写输入参数返回结果集的实体类 37 实现类编写 38 编写实现类中dsl 语句 39 返回集结果转换 40 结果测试 41 测试通过输入查询条件并将数据显示到页面
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页