转-聚合查询变慢-详解Elasticsearch的Global Ordinals与High Cardinality
转自: https://blog.csdn.net/zwgdft/article/details/83215977
【README】
Elasticsearch中的概念很多,本文将从笔者在实践过程中遇到的问题出发,逐步详细介绍 Global Ordinals 和 High Cardinality ,这也是笔者的认知过程。文中的Elasticsearch 版本为5.5。
背景
故事是这样的,因为业务需要,我们在项目中设计了一种针对Elasticsearch数据的异步去重方法(注:关于Elasticsearch数据去重,笔者会在另一篇博文中更加详细介绍),基本思路是:
{"timestamp": 1540099182,"msgType": 1210,......"hash": "31a2c683dccb83ef8b8d1ee43290df62"
}
{"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字段,并且去重是异步的,不会影响到原有的设计,所以在通过相关的功能性测试后就上线了。然而,运行一段时间后,出现了严重问题:
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会导致构建Global Ordinals过程变慢,从而导致聚合查询变慢、内存使用过高。
- High Cardinality会导致压缩比率降低,从而导致存储空间增加,特别是像hash值这样完全随机的字符串。
- 对High Cardinality字段执行Cardinality聚合查询时,会受到精度控制从而导致结果不精确。
转-聚合查询变慢-详解Elasticsearch的Global Ordinals与High Cardinality相关推荐
- 聚合查询越来越慢?——详解Elasticsearch的Global Ordinals与High Cardinality
Elasticsearch中的概念很多,本文将从笔者在实践过程中遇到的问题出发,逐步详细介绍 Global Ordinals 和 High Cardinality ,这也是笔者的认知过程.文中的E ...
- Elasticsearch(es) 查询语句语法详解
Elasticsearch 查询语句采用基于 RESTful 风格的接口封装成 JSON 格式的对象,称之为 Query DSL.Elasticsearch 查询分类大致分为全文查询.词项查询.复合查 ...
- Tomcat 8熵池阻塞变慢详解
Tomcat 8熵池阻塞变慢详解 Tomcat 8启动很慢,且日志上无任何错误,在日志中查看到如下信息: Log4j:[2015-10-29 15:47:11] INFO ReadProperty:1 ...
- 深入mysql慢查询设置的详解
深入mysql慢查询设置的详解 在web开发中,我们经常会写出一些SQL语句,一条糟糕的SQL语句可能让你的整个程序都非常慢,超过10秒一般用户就会选择关闭网页,如何优化SQL语句将那些运行时间 比较 ...
- mysql通过集合查询_MySQL使用集合函数进行查询操作实例详解
本文实例讲述了MySQL使用集合函数进行查询操作.分享给大家供大家参考,具体如下: COUNT函数 SELECT COUNT(*) AS cust_num from customers; SELECT ...
- mysql 嵌套查询性能_MySQL数据库之嵌套查询与连接查询的性能详解
本文主要向大家介绍了MySQL数据库之嵌套查询与连接查询的性能详解 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助. 嵌套查询与连接查询的性能:连接查询一般较快:子查询很难被优化. ...
- mongodb java查询_java操作mongoDB查询的实例详解
java操作mongo查询的实例详解 前言: MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系数据库 ...
- oracle分页查询sql语句通用,oracle分页查询sql语句,oracle分页查询sql语句详解
oracle分页查询sql语句,oracle分页查询sql语句详解,Oracle分页查询sql语句 Oracle中分页和MySql中的分页不同,MySql中的分页使用关键字limit即可,相对简单一点 ...
- mysql resulttype map_Mybatis中的resultType和resultMap查询操作实例详解
resultType和resultMap只能有一个成立,resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,resultMap解决复杂查询是的映射问题.比 ...
最新文章
- android stadio svn 使用技巧
- 《Adobe Illustrator CS6中文版经典教程(彩色版)》—第1课1.8节使用面板菜单
- 知识图谱(知识图谱构建)
- vue-admin项目启动踩过的坑
- [Windows]7种网络编程I/O模型代码实现实例
- EssentialC++
- python 爬电影名网址评分及导演代码和运行结果
- c语言运算优先级口诀简单,C语言运算符优先级口诀
- http协议中的1xx,2xx,3xx,4xx,5xx状态码分别表示什么?列举常见错误码及含义
- 如何免费将office文档转成PDF的几种方法
- 【天池竞赛系列】菜鸟-需求预测与分仓规划初赛冠军解决方案
- postgresql中替换字符串中的换行符和回车符号
- Oracle优化 latch free问题Result Cache:RC Latch引起数据库缓慢
- 2022最新最全的pytest配置文件pytest.ini
- 微信小程序开发系列——注册申请
- 超声波传感器与液晶屏显示实验
- 硅谷AI商业化大会专题讨论:自动驾驶与智能交通的未来
- 读书笔记 - 我读《财务自由之路》 - 博多 舍费尔 / 你真正想要的是什么
- Django用admin开发的幼儿园薪资管理系统-3
- 2007电子杂志的发展必然—企业电子商刊(iebook)!
热门文章
- AtCoder Regular Contest 100 E - Or Plus Max Sos dp
- Fight against involution
- 201409-5 拼图
- [HNOI2012]集合选数(思维构造 + 状压dp)
- CF1612G Max Sum Array
- 【2018.3.31】模拟赛之三-ssl2408 比萨【搜索,dfs】
- 【模拟】【递归】解压字符串(jzoj 1519)
- 2019.01.26【NOIP普及组】模拟赛C组总结
- 读上瘾-让用户养成习惯
- 28、jdbc操作数据库(5)