ElasticSearch(后续简称为ES)提供了对数据的统计分析服务。在之前的开发中使用Terms Aggregation 对数据进行聚合统计,遇到了一些问题,查阅了ES的官方文档和技术博文了解Terms Aggregation的用法。

一、聚合基本概念

ES 聚合的两个主要概念:

  • 桶(Buckets):满足特定条件的文档的集合
  • 指标(Metrics):对桶内的文档进行统计计算

每个聚合都是一个或者多个桶和另个或者多个指标的组合,用粗略的SQL语句解释就是:

SELECT COUNT(1) FROM table GROUP BY field

其中,COUNT(1) 相当于指标, GROUP BY field 相当于桶。

简单例子:

GET /_search
{"aggs" : {"genres" : {"terms" : { "field" : "genre" }}}
}

response:

{"aggregations" : {"genres" : {"doc_count_error_upper_bound": 0,"sum_other_doc_count": 0,"buckets" : [{"key" : "electronic","doc_count" : 6},{"key" : "rock","doc_count" : 3},{"key" : "jazz","doc_count" : 2}]}}
}

terms aggregation 应该是一个 keyword 类型或者其他适合桶聚合的数据类型字段。如果想要在 text 上使用,需要开启 fielddata。
默认的,terms aggregation 会根据 doc_count 返回前10个 terms 的桶。

二、Size 参数

如果我们想返回所有的聚合桶该怎么办呢?在低版本的 ES 中,如2.x 版本 ,可以设置 “size”:0 来保证全部返回,代表 Global,但是在高版本的ES中,如 ES6+ 版本,size 的值必须设置大于0。
上文提到 terms aggregation 默认返回 doc_count 前10个 terms 的桶。Size 参数可以设置返回的 terms 桶个数。ES 是一个分布式的搜索引擎,每个索引都可以有多个分片,用来将一份大索引的数据切分成多个小的物理索引,解决单个索引数据量过大导致的性能问题,另外每个 shard 还可以配置多个副本,来保证高可靠以及更好的抗并发的能力。将一个索引切分成多个 shard,大多数时候是没有问题的,但是在 ES 里面如果索引被切分成多个 shard,在使用 terms aggregation 进行聚合时,可能会出现问题。

默认情况下,节点在搜索过程中会请求每个 shard 它们自己前几的 term 桶,一旦所有的分片响应,它就会减少结果到最终列表返回给客户端。这就意味着如果特有的 terms 数量大于 Size,那么返回的列表就有可能会稍有偏差不精确。就是说 term 统计的偏差可能会导致本该在前几中返回的桶而未被返回。 官方建议:如果想要在嵌套 terms aggregation 检索所有的 terms 或 terms 组合,应该使用 Composite aggregation ,它可以进行分页,而 terms aggregation 旨在返回前几的 terms。

文档统计是近似的
在 terms aggregation 中文档统计并不总是精确的。这是因为每个 shard 提供各自满足的 terms 有序列表的视图,它们会被组合起来给到最终视图。

考虑以下场景:
从有3个 shard 的索引请求以 product 字段聚合后按照文档数量降序排序的前5 terms。在下面的例子中,每个 shard 需要给出各自的前5 terms。

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

该索引每个 shard 各自的文档数量:

序号 Shard A Shard B Shard C
1 Product A (25) Product A (30) Product A (45)
2 Product B (18) Product B (25) Product C (44)
3 Product C (6) Product F (17) Product Z (36)
4 Product D (3) Product Z (16) Product G (30)
5 Product E (2) Product G (15) Product E (29)
6 Product F (2) Product H (14) Product H (28)
7 Product G (2) Product I (10) Product Q (2)
8 Product H (2) Product Q (6) Product D (1)
9 Product I (1) Product J (6)
10 Product J (1) Product C (4)

每个 shard 返回的前五 terms:

序号 Shard A Shard B Shard C
1 Product A (25) Product A (30) Product A (45)
2 Product B (18) Product B (25) Product C (44)
3 Product C (6) Product F (17) Product Z (36)
4 Product D (3) Product Z (16) Product G (30)
5 Product E (2) Product G (15) Product E (29)

将3个 shard 返回的 terms 列表组合起来返回的前5 terms:

序号 Product
1 Product A (100)
2 Product Z (52)
3 Product C (50)
4 Product G (45)
5 Product B (43)

Product A 从所有 shard 返回,所以它的文档统计是准确的。Product C 由 Shard A 与 Shard C 返回,显示的文档统计时50,但并不是一个准确的统计。Product C 在 Shard B 由于统计值只有4而不够高没有被 shard 放入返回的前5 term 列表中。Product Z 也只由两个 shard 返回,但不存在于第3个 shard 中。因此,在合并结果生成最终的 terms 列表时没有办法知道是 Product C 还是 Product Z 的文档统计错误。 Product H 在三个 shard 中的文档统计和为44,但它没有包含在最终的 terms 列表中,那是因为它在每个 shard 中的文档统计均未排到前5。

三、Shard Size 参数

请求的 Size 越大,结果越准确,但是计算最终结果的成本也越高。原因主要有两个:其一,在 shard 级别对更大优先级队列的管理;其二,在节点与客户端直接有更大的数据传输。shard_size 参数可用于最小化更大的请求 size 所带来的额外工作。它决定了协调节点从每个节点请求的 terms 数量。一旦所有 shard 都响应了,协调节点就会基于 size 参数将它们减少到最终的结果。这样,就提高了返回 terms 的准确性,并且避免将大量存储桶流回到客户端的开销。shard_size 不能小于 size,当其值小于 size,ES 会将它重置到 size 的大小。默认情况下,shard_size =(size * 1.5 + 10)。

四、Aggregation的一些问题

1.DSL聚合时默认展示10个桶的数据,如果桶的数量大于10,需要在分桶的field上加上size指定返回的数量。

"terms":{"field":"product","size": 50}

2.聚合结果的doc_count字段代表文档的数量。

3.cardinality
cardinality 类似SQL里的distnict && count(统计去重后的数量)是近似值
(1)精度可配置,用来控制内存的使用(更精确 = 更多内存),设置阈值越大,越精确,但是容易造成OOM;
(2)阈值precision_threshold 参数的值定义了在何种基数水平下我们希望得到一个近乎精确的结果,基数在阈值以下几乎100%精确;
(3)precision_threshold 接受 0–40,000 之间的数字,更大的值还是会被当作 40,000来处理。

4.“execution_hint”: “map”使用
在计算离散度比较大的字段统计值时,合理修改Terms Aggregation的执行方式,可以节省内存和提高计算速度。“execution_hint”: “map” 在查询结果集小的情况下速度很快,但如果结果集很大,map方式不一定比默认的执行方式快。

"terms":{"field":"product","execution_hint": "map"}

其中的解释引用自elastic中文社区query+aggs查询性能问题kennywu76的回答。

Terms Aggregation默认的计算方式并非直观感觉上的先查询,然后在查询结果上直接做聚合。ES假定用户需要聚合的数据集是海量的,如果将查询结果全部读取回来放到内存里计算,内存消耗会非常大。因此ES利用了一种叫做global ordinals的数据结构来对聚合的字段来做bucket分配,这个ordinals用有序的数值来代表字段里唯一的一个字符串,因此为每个ordinals值分配一个bucket就等同于为每个唯一的term分配了bucket。 之后遍历查询结果的时候,可以将结果映射到各个bucket里,就可以很快的统计出每个bucket里的文档数了。这种计算方式主要开销在构建global ordinals和分配bucket上,如果索引包含的原始文档非常多,查询结果包含的文档也很多,那么默认的这种计算方式是内存消耗最小,速度最快的。如果指定execution_hint:map则会更改聚合执行的方式,这种方式不需要构造global ordinals,而是直接将查询结果拿回来在内存里构造一个map来计算,因此在查询结果集很小的情况下会显著的比global ordinals快。要注意的是这中间有一个平衡点,当结果集大到一定程度的时候,map的内存开销带来的代价可能就抵消了构造global ordinals的开销,从而比global ordinals更慢,所以需要根据实际情况测试对比一下才能找好平衡点。

参考资料

  • ElasticSearch 7.6 官方文档
  • elastic中文社区query+aggs查询性能问题
  • 通过某瓣真实案例看Elasticsearch优化

ElasticSearch Terms Aggregation 聚合相关推荐

  1. Elasticsearch Terms Aggregation计数聚合详解

    文章目录 概述 1. 计算每个IP的访问次数 2. 计算每个IP的访问人数 3. 计算每个IP+每个类型的访问次数 4. 计算每天的访问次数 概述 Terms Aggregation计算的是每一组符合 ...

  2. Es Bucket聚合(桶聚合) Terms Aggregation与Significant Terms Aggregation

    本章将介绍elasticsearch最重要的桶聚合terms aggregation. Terms Aggregation 多值聚合,根据库中的文档动态构建桶.基于词根的聚合,如果聚合字段是text的 ...

  3. Es Bucket聚合(桶聚合) 第二篇-Terms Aggregation与Significant Terms Aggregation

    作者介绍:<RocketMQ技术内幕>作者,中间件兴趣圈微信公众号维护者,文末有对应的二维码,关注后可以与作者更好的互动. 本章将介绍elasticsearch最重要的桶聚合terms a ...

  4. Elasticsearch:significant terms aggregation

    在本文中,我们将重点关注 significant terms 和 significant text 聚合.这些聚合旨在搜索数据集中有趣和/或不寻常的术语,这些术语可以告诉您有关数据的隐藏属性的更多信息 ...

  5. elasticsearch之Terms Aggregation

    引言 Bucket aggregations本文译为桶聚合 桶聚合(bucket aggregation)不像指标聚合(Metric aggregation)那样计算字段的指标,而是创建文档存储桶. ...

  6. Elasticsearch terms聚合不准确的问题

    Elasticsearch terms聚合不准确的问题 默认情况 默认情况下,terms聚合只返回文档数Top10的term统计.如果想返回更多的词项分桶,可以设置"size"参数 ...

  7. ElasticSearch十四--ES-index template 和 aggregation聚合分析

    index template index template index template 可以帮助你设定 Mappings 和 Settins ,并按照一定的规则,自动匹配到新创建的索引上 模板仅在一 ...

  8. java操作es聚合操作并显示其他字段_java使用elasticsearch分组进行聚合查询过程解析...

    这篇文章主要介绍了java使用elasticsearch分组进行聚合查询过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java连接elas ...

  9. java操作es聚合操作并显示其他字段_java使用elasticsearch分组进行聚合查询(group by)-项目中实际应用...

    java连接elasticsearch 进行聚合查询进行相应操作 一:对单个字段进行分组求和 1.表结构图片: 根据任务id分组,分别统计出每个任务id下有多少个文字标题 1.SQL:select i ...

最新文章

  1. c语言怎么输入学号姓名,c语言如何输入编号和姓名
  2. taskset -pc PID 查看线程占用cpu核
  3. JAVA实现链表的反转(《剑指offer》)
  4. C++智能指针及其简单实现
  5. 让浏览器变身代码编辑器
  6. mysqld:表mysql.plugin不存在_99%测试工程师不知道的数据库知识|干货
  7. 单片机集成wifi等_从零制作单片机需要哪些知识?
  8. 【MySQL】MySQL常见的读写分离方法
  9. markdown的学习
  10. 用户控件中复杂属性的设计时支持
  11. Windows下安装elastic search
  12. 使用Bus Hound抓USB转串口数据包,助力问题分析
  13. 存储卡格式化后数据如何恢复呢?
  14. C# Teechart Pareto图实现 折线显示百分比,多坐标轴显示等
  15. 阿里P10、腾讯T4、华为18都是怎样的神级收入?
  16. 给实体机服务器重装Linux系统全记录
  17. 杀人游戏-Tarjan
  18. ambari全攻略流程,安装ambari(二)
  19. 数据库分区:MySQL分区
  20. Spicy Restaurant (暴力多源bfs)

热门文章

  1. FTP服务器—(5分钟快速搭建一个FTP服务器)
  2. 6.模糊推理方法: 提出, 模糊集合与隶属函数, 模糊关系及其合成, 模糊推理与决策, 模糊推理应用
  3. 怎么解决上台发言紧张的问题
  4. android学习之获取联系人原理(姓名和电话)详解
  5. [WebGL入门]三,3D绘图的基础知识
  6. 移动H2-3获取超管密码
  7. Android如何生成图片分享
  8. android 发短信意图,android代码实现打电话和发送短信功能
  9. VMware Tools 文件为空
  10. 小孩都看得懂的贝塔分布