转-聚合查询变慢-详解Elasticsearch的Global Ordinals与High Cardinality

转自: https://blog.csdn.net/zwgdft/article/details/83215977

 

【README】

 Elasticsearch中的概念很多,本文将从笔者在实践过程中遇到的问题出发,逐步详细介绍 Global OrdinalsHigh Cardinality ,这也是笔者的认知过程。文中的Elasticsearch 版本为5.5。

背景

  故事是这样的,因为业务需要,我们在项目中设计了一种针对Elasticsearch数据的异步去重方法(注:关于Elasticsearch数据去重,笔者会在另一篇博文中更加详细介绍),基本思路是:

  • 为每一条数据计算hash值,作为document的一个字段(keyword类型)插入到Elasticsearch中,数据格式简化如下:
{"timestamp": 1540099182,"msgType": 1210,......"hash": "31a2c683dccb83ef8b8d1ee43290df62"
}
  •  
  • 每隔一段时间,运行一次检测脚本,检查Elasticsearch中的数据是否有重复,相关查询语句如下(这里,terms聚合用于发现给定时间范围内是否有超过2条hash值一样的数据,top_hits聚合用于找出重复数据组中的具体数据信息,然后删除掉重复的数据即可):
{"size": 0,"query": {"bool": {"filter": [{  "range":{  "timestamp":{  "gte": 1540087200,"lt": 1540087500}}}]}},"aggs": {"duplications": {"terms": {"field": "hash","min_doc_count": 2,"size": 500},"aggs": {"top_duplications": {"top_hits": {"size": 3}}}}}
}
  •  

  这样一个方案,因为只是在数据集中增加了一个hash字段,并且去重是异步的,不会影响到原有的设计,所以在通过相关的功能性测试后就上线了。然而,运行一段时间后,出现了严重问题:

  • 随着新数据的写入,上述的查询语句变得越来越慢,从秒级逐步变成要20多秒,并且在数据量超过10亿条后,每次查询都会使内存超过80%;
  • index的存储空间比原先增加了近一倍

  对于类似上述的查询语句,Elasticsearch会先根据Filter条件找出匹配的document,然后再进行聚合运算。在我们的业务中,每次查询2小时内的数据,并且数据的写入是匀速的,这意味着每次匹配出来的document个数基本是固定的,那么为何会出现这个查询越来越慢的问题?而且,我们发现,即使Filter匹配的document个数为0,也同样需要很久才能返回结果

  另一方面,经过对比验证,可以确定是新增加的hash字段导致了数据存储空间比原先增加了近一倍。
  带着这些问题,笔者进行了详细的调研,最终锁定Global OrdinalsHigh Cardinality两个核心概念。其中,github上面的一个issue Terms aggregation speed is proportional to field cardinality 给了很大的启发。

Global Ordinals

什么是Ordinals? 

  假设有10亿条数据,每条数据有一个字段status(keyword类型),其值有三种可能性:status_pending、status_published、status_deleted,那么每条数据至少需要14-16 Bytes,也就是说需要将近15GB内存才能装下所有数据。

Doc     |   Term
-------------------------------
0       |   status_pending
1       |   status_deleted
2       |   status_published
3       |   status_pending
  •  

  为了减少内存使用,考虑将字符串排序后进行编号,形成一张映射表,然后在每条数据中使用相应字符串的序号来表示。通过这样的设计,可以将所需内存从15 GB减少为1 GB左右。
  这里的映射表,或者说映射表中的序号,就是Ordinals。

Ordinal |   Term
-------------------------------
0       |   status_deleted
1       |   status_pending
2       |   status_publishedDoc     |   Ordinal
-------------------------------
0       |   0   # deleted
1       |   2   # published
2       |   1   # pending
3       |   0   # deleted
  •  

什么是Global Ordinals? 

  当我们对status字段做Terms聚合查询时,请求会透过Coordinate Node分散到Shard所在的Node中执行,而针对每个Shard的查询又会分散到多个Segment中去执行。
  上述的Ordinals是per-segment ordinals,是针对每个Segment里面的数据而言,意味着同一个字符串在不同的per-segment ordinals中可能对应的序号是不同的。比如,在Segment 1中只有status_deleted(0)和status_published(1)两个值,而Segment 2中有3个值:status_deleted(0),status_pending(1),status_published(2)。
  这样就面临一个抉择:方案一,在完成per-segment的查询后,将相应的序号转换成字符串,返回到Shard层面进行合并;方案二,构建一个Shard层面的Global Ordinals,实现与per-segment ordinals的映射,就可以在Shard层面完成聚合后再转换成字符串。
  经过权衡,Elasticsearch(Lucene)选择了方案二作为默认方法:构建Global Ordinals。

为何会影响聚合查询?

  构建Global Ordinals的目的是为了减少内存使用、加快聚合统计,在大多数情况下其表现出来的性能都非常好。之所以会影响到查询性能,与其构建时机有关:

  • 由于Global Ordinals是Shard级别的,因此当一个Shard的Segment发生变动时就需要重新构建Global Ordinals,比如有新数据写入导致产生新的Segment、Segment Merge等情况。当然,如果Segment没有变动,那么构建一次后就可以一直利用缓存了(适用于历史数据)。
  • 默认情况下,Global Ordinals是在收到聚合查询请求并且该查询会命中相关字段时构建,而构建动作是在查询最开始做的,即在Filter之前

  这样的构建方式,在遇到某个字段的值种类很多(即下文所述的High Cardinary问题)时会变的非常慢,会严重影响聚合查询速度,即使Filter出来的document很少也需要花费很久,也就是上文笔者遇到的问题,即在High Cardinary情况下,构建Global Ordinals非常慢。因为我们新加的hash字段对于每条数据都不一样,所以当写入越来越多的数据后,聚合查询越来越慢(大概超过5000W条之后)。

有哪些优化方法?

  虽然在Lucene 7.1中,针对global ordinals的构建有些优化(LUCENE-7905),但是仍然不能避免这样的问题。目前有这样几种优化方法(或者说是缓解之法,目前尚未发现完美的方法):

  • 增加Shard个数。因为Global Ordinals是Shard层面的,增加Shard个数也许可以缓解问题,前提是:第一,要能确定有问题的字段的值种类可以通过该方式减少在单个Shard中的量;第二,确保Shard的个数增加不会影响到整体的性能。
  • 延长refresh interval,即减少构建Global Ordinals的次数来缓解其影响,前提是要能接受数据的非实时性。
  • 修改execution_hint的值。在Terms聚合中,可以设置执行方式是map还是global_ordinals,前者的意思是直接使用该字段的字符串值来做聚合,即无需构建Global Ordinals。这样的方式,适用于可以确定匹配文档数据量的场景,并且不会引起内存的暴增,比如在笔者的业务场景中,每次只查询2小时内的数据量。这也是当前我们的优化方法。
GET /_search
{"aggs" : {"tags" : {"terms" : {"field" : "status","execution_hint": "map" }}}
}
  •  

High Cardinality 

  相信看完上文,读者已经知道什么是High Cardinality了。所谓High Cardinality,指的是Large Number of Unique Values,即某个字段的值有很多很多种,比如笔者业务中的那个hash字段。在Elasticsearch,High Cardinality会带来各种问题,百害而无一利,所以应该尽量避免,避免不了也要做到心中有数,在出问题时可以及时调整。

  • High Cardinality会导致构建Global Ordinals过程变慢,从而导致聚合查询变慢、内存使用过高。
  • High Cardinality会导致压缩比率降低,从而导致存储空间增加,特别是像hash值这样完全随机的字符串。
  • 对High Cardinality字段执行Cardinality聚合查询时,会受到精度控制从而导致结果不精确。

 

  本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Global Ordinals和High Cardinality两个核心概念,希望对遇到类似问题的人有所帮助。目前,针对我们的业务场景,相关的调整有:第一,使用"execution_hint": "map"来避免构建Global Ordinals;第二,尝试在数据上传端增加对压缩友好的唯一键来作为去重对象,比如uuid4;第三,减小index的切割时间,比如从weekly index变成daily index,从而降低index中单个shard的数据量。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330170.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot配置Mybatis的两种方式(通过XML配置和通过YML配置文件配置)

一、 通过XML的方式配置Mybatis 在 /src/main/resource下创建Mybatis配置文件 mybatis-config.xml 和 映射文件目录mapper在application.yml指定Mybatis配置文件、映射文件的位置 可选: - mybatis.config-location:配置 mybatis-config.xml 路径mybatis…

cmw500综合测试仪使用_山西优质三相直流电阻测试仪图片-南电合创

感谢您关注我们的产品,本公司除了有此产品介绍以外,还有太阳能接线盒测试仪,绝缘电阻测试仪,回路电阻测试仪,变压器直流电阻测试仪,无线高压核相仪,微机继电保护测试仪,电能质量分析…

内连接,外链接(左连接、右连接、全连接),交叉连接大总结

转载自 内连接,外链接(左连接、右连接、全连接),交叉连接大总结1.什么是连接查询呢?概念:根据两个表或多个表的列之间的关系,从这些表中查询数据。目的:实现多个表查询操作。2.分类&…

redis创建集群报错can‘t connect to node 192.168.163.203

【README】 创建集群报错 cant connect to node 192.168.163.203 [rootcentos201 ~]# /usr/local/redis-cluster/bin/redis-trib.rb create --replicas 0 192.168.163.201:6379 192.168.163.202:6379 192.168.163.203:6379 >>> Creating cluster Connecting to no…

SpringMVC控制器方法获取参数时@RequestParam注解加与不加的区别

问题 在使用SpringMVC时获取参数时,加与不加RequestParam都会自动传入参数值,那加与不加的区别是什么? 测试 GetMapping("test1")public String test1(String s){return "参数:"s;}GetMapping("test2&…

如何设置电脑自动锁屏_这个手机锁屏密码竟可以根据时间而变化!密码每分钟都会发生改变...

如今,手机已经成为了我们生活中不可或缺的一部分。手机里面承载了我们太多的个人信息、生活隐私,财产安全等等。为了防止别人偷窥我们的手机,我们都会将手机设置锁屏密码。相信大家锁屏设置的都是图形或者数字密码。但是这样的密码很容易被人…

我猜,每个程序员对着电梯都想过调度算法吧

转载自 我猜,每个程序员对着电梯都想过调度算法吧 传统电梯调度算法 1.1 先来先服务算法(FCFS) 先来先服务(FCFS-First Come First Serve)算法,是一种随即服务算法,它不仅仅没有对寻找楼层进行…

redis集群添加节点报错Either the node already knows other nodes (check with CLUSTER NODES) or contains some k

【README】 redis集群添加节点报错 [ERR] Node 192.168.163.202:6380 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0. 【1】 原因 导致异常的主要原因是该节点中默认生成的配置或历史存储数据…

SpringMVC 参数校验

SpringMVC 可以通过使用注解的方式对参数进行校验&#xff0c;省去参数校验的代码 引入依赖 Spring项目需引入 <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>6.0.7.F…

linux入门_Linux超详细0基础入门篇(一)

首先要感谢大康老师对我在Linux操作系统上的教导。今天来讲一下用途广泛的Linux的基础入门教程仅仅是做入门使用&#xff0c;如果想更加深入的学习那就需要自己做探索了。本次例子使用的是kali linux(ARM架构)是基于Debian的Linux发行版本&#xff0c;可能会与其他版本操作有所…

教你如何监控 Java 线程池运行状态

转载自 干货 | 教你如何监控 Java 线程池运行状态 之前写过一篇 Java 线程池的使用介绍文章《线程池全面解析》&#xff0c;全面介绍了什么是线程池、线程池核心类、线程池工作流程、线程池分类、拒绝策略、及如何提交与关闭线程池等。 但在实际开发过程中&#xff0c;在线程池…

算法设计与分析:(一)时间复杂度分析

数学基础 设f和g是定义域为自然数集N上的函数 存在正整数c、n&#xff0c;使0<f(n)<cg(n)成立&#xff0c;称f(n)的渐进上界是g(n)&#xff0c;记作f(n)O(g(n))存在正整数c、n&#xff0c;使0<cg(n)<f(n)成立&#xff0c;则称f(n)渐进下界是g(n)&#xff0c;记作…

redis集群3种模式

【README】 转自&#xff1a; https://segmentfault.com/a/1190000022808576 &#xff08;好文章&#xff09; Redis 支持三种集群方案 主从复制模式Sentinel&#xff08;哨兵&#xff09;模式Cluster 模式【1】主从复制模式 主从复制的作用 通过持久化功能&#xff0…

ios签名软件_苹果企业签名常常掉怎样处理【苹果签名吧】

对于使用ios苹果系统的开发者来说&#xff0c;可以在苹果app商店下载现在陈列的APP软件&#xff0c;但是没有陈列的APP软件只能使用苹果签名&#xff0c;通过上传到app签名渠道的方法下载。这种方法的缺点是&#xff0c;如果苹果ios签名无效(即签名丢失)&#xff0c;应用程序无…

算法设计与分析:(二)动态规划

目录设计思想使用动态规划的必要条件适用动态规划算法解决的问题的特征&#xff1a;优化原则&#xff1a;动态规划的一般步骤以背包问题为例动态规划本质上为带备忘录的穷举算法。对动态规划问题&#xff0c;直接套框架即可&#xff1a;问题有什么「状态」&#xff0c;有什么「…

开发app用户协议_APP软件开发如何让用户更开心地付钱?

原标题&#xff1a;APP软件开发如何让用户更开心地付钱&#xff1f;在一些付费产品中&#xff0c;明明花费了很多心思和精力制作的产品却被用户嫌弃&#xff0c;付钱的时候不情不愿。这是什么原因造成的呢&#xff1f;一、如何判断服务和虚拟商品的价值&#xff1f;比如在线的音…

漫画:什么是八皇后问题

转载自 漫画&#xff1a;什么是八皇后问题&#xff1f;题目是什么意思呢&#xff1f;国际象棋中的皇后&#xff0c;可以横向、纵向、斜向移动。如何在一个8X8的棋盘上放置8个皇后&#xff0c;使得任意两个皇后都不在同一条横线、竖线、斜线方向上&#xff1f;让我们来举个栗子&…

redis-java客户端jedis测试

【README】 本文旨在记录 jedis 包连接redis集群的开发方式&#xff0c;并对api做简单测试&#xff0c;api不会深入&#xff1b; maven引入的 jedis依赖 <dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</a…

MySQL优化(二):索引的类型、匹配原则、创建原则

目录索引的优缺点索引类型聚簇索引&#xff08;主键索引&#xff09;非聚簇索引&#xff08;二级索引、辅助索引&#xff09;索引匹配的原则最左匹配原则无法使用索引的场景索引创建的原则使不使用索引的依据到底是什么&#xff1f;参考索引的优缺点 常见的索引结构有: B 树&a…

jedis对redis键加锁+解锁+事务提交

【README】 redis的java客户端对键加锁和解锁&#xff08;Jedis.watch() Jedis.unwatch() &#xff09;&#xff1b; 【1】代码 /*** redis加锁-watch * 【场景描述】&#xff1a; watch命令就是标记一个键&#xff0c;如果标记了一个键&#xff0c;* 在提交事务前如果该键…