【README】
Elasticsearch中的概念很多,本文将从笔者在实践过程中遇到的问题出发,逐步详细介绍 Global Ordinals 和 High Cardinality ,这也是笔者的认知过程。文中的Elasticsearch 版本为5.5。
故事是这样的,因为业务需要,我们在项目中设计了一种针对Elasticsearch数据的异步去重方法(注:关于Elasticsearch数据去重,笔者会在另一篇博文中更加详细介绍),基本思路是:
这样一个方案,因为只是在数据集中增加了一个hash字段,并且去重是异步的,不会影响到原有的设计,所以在通过相关的功能性测试后就上线了。然而,运行一段时间后,出现了严重问题:
对于类似上述的查询语句,Elasticsearch会先根据Filter条件找出匹配的document,然后再进行聚合运算。在我们的业务中,每次查询2小时内的数据,并且数据的写入是匀速的,这意味着每次匹配出来的document个数基本是固定的,那么为何会出现这个查询越来越慢的问题?而且,我们发现, 即使Filter匹配的document个数为0,也同样需要很久才能返回结果 。
另一方面,经过对比验证,可以确定是新增加的hash字段导致了数据存储空间比原先增加了近一倍。 带着这些问题,笔者进行了详细的调研,最终锁定 Global Ordinals 与 High Cardinality 两个核心概念。其中,github上面的一个issue Terms aggregation speed is proportional to field cardinality 给了很大的启发。
什么是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_published Doc | 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的数据量。 创建一个新模型 您应该覆盖index和type属性以确定文档路径。 use Isswp101 \ Persimmon \ Models \ BaseElasticsearchModel ; use Isswp101 \ Persimmon \ Persistence \ Persistence ; use Isswp101 \ Persimmon \ Contracts \ PersistenceContract ; class 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.htm spring-boot-starter-elasticsearchHighLevelClient 封装elasticsearch-rest-high-level-client,添加了连接池功能(官方包中没有提供连接池);同时对接spring-boot-starter 作者:古晋 电子邮件: springboot版本:2.0.2.RELEASE 为Maven安装项目 git pull git@github.com:guzhandong/spring-boot-starter-elasticsearchHighLevelClient.git cd spring-boot-starter-elasticsearchHighLevelClient mvn clean install 在pom.xml中添加依赖属性 <dependency> <groupId>com.guzhandong.springframework.boot</groupId> <artifactId>spring-boot-starter-elasticsearchRestHighLeave 在可获得较新的版本,用于Elasticsearch和elasticsearch-js的较新版本。 Amazon ES的连接处理程序 使与Amazon ES兼容。 它使用aws-sdk向Amazon ES端点发出已签名的请求。 # Install the connector, elasticsearch client and aws-sdk npm install --save http-aws-es aws-sdk elasticsearch // create an elasticsearch client for your Amazon ES let es = require ( 'elasticsearch' ) . Client ( { hosts : [ 'https://amazon-es-host.us-east-1.es.amazonaws.c 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵 es在使用cardinality实现count(distinct)时会在准确性和及时性上做一定的取舍。可以在使用cardinality时,配置下面的参数来增加准确性,牺牲的是时间和内存。Elasticsearch 去重统计 按照deviceId 去重统计总数。 1、聚合为什么慢? 大多数时候对单个字段的聚合查询还是非常快的, 但是当需要同时聚合多个字段时,就可能会产生大量的分组,最终结果就是占用 es 大量内存,从而导致 OOM 的情况发生。 实践应用发现,以下情况都会比较慢: 1)待聚合文档数比较多(千万、亿、十亿甚至更多); 2)聚合条件比较复杂(多重条件聚合); 3)全量聚合(翻页的场景用)。 2、聚合优化方案探讨 优化方案一 1、客户反馈es集群存在很多慢查询,检查发现都是term查询,而且进行了sort排序,但是size是top 15,这样的查询不至于一直报慢查询。 2、继续检查日志,发现所有慢查询都是一个索引报的,也就是其他的索引的查询都是正常的 3、定位到索引后,_cat/shards/in 本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Elasticsearch中两个核心概念:Global Ordinals和High Cardinality。 1、问题引出默认情况下,Elasticsearch 已针对大多数用例进行了优化,确保在写入性能和查询性能之间取得平衡。我们将介绍一些聚合性能优化的可配置参数,其中部分改进是以牺牲写入性能... Elasticsearch 是一个非常灵活且功能丰富的应用程序,它提供了许多不同的方式来查询你的数据。 但是你是否曾经遇到过低于你希望的查询速度? 对于像 Elasticsearch 这样的分布式系统,可能有各种影响查询性能的因素,包括外部因素,例如负载均衡器设置、网络延迟(带宽、NIC 卡/驱动程序)等。 在本博客中,我将讨论导致查询缓慢的原因以及如何在 Elasticsearch 的上下文中识别它们。 本文源于我们一些常见的故障排除方法,这些方法可能需要人们对 Elasticsearch 的工作原理相 Elasticsearch查询慢问题排查思路 Elasticsearch的查询慢的问题往往是由多种因素造成的,同时我们也需要遵循Elasticsearch的查询准则:ES适合top N的查询,不适合大数据量返回的查询。 场景1 内存参数配置不合...
Doc | Term ------------------------------- 0 | status_pending 1 | status_deleted 2 | status_published 3 | status_pending 为了减少内存使用,考虑将字符串排序后进行编号,形成一张映射表,然后在每条数据中使用相应字符串的序号来表示。通过这样的设计,可以将所需内存从15 GB减少为1 GB左右。 这里的映射表,或者说映射表中的序号,就是Ordinals。
为了减少内存使用,考虑将字符串排序后进行编号,形成一张映射表,然后在每条数据中使用相应字符串的序号来表示。通过这样的设计,可以将所需内存从15 GB减少为1 GB左右。 这里的映射表,或者说映射表中的序号,就是Ordinals。
Ordinal | Term ------------------------------- 0 | status_deleted 1 | status_pending 2 | status_published Doc | 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的数据量。 创建一个新模型 您应该覆盖index和type属性以确定文档路径。 use Isswp101 \ Persimmon \ Models \ BaseElasticsearchModel ; use Isswp101 \ Persimmon \ Persistence \ Persistence ; use Isswp101 \ Persimmon \ Contracts \ PersistenceContract ; class 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.htm spring-boot-starter-elasticsearchHighLevelClient 封装elasticsearch-rest-high-level-client,添加了连接池功能(官方包中没有提供连接池);同时对接spring-boot-starter 作者:古晋 电子邮件: springboot版本:2.0.2.RELEASE 为Maven安装项目 git pull git@github.com:guzhandong/spring-boot-starter-elasticsearchHighLevelClient.git cd spring-boot-starter-elasticsearchHighLevelClient mvn clean install 在pom.xml中添加依赖属性 <dependency> <groupId>com.guzhandong.springframework.boot</groupId> <artifactId>spring-boot-starter-elasticsearchRestHighLeave 在可获得较新的版本,用于Elasticsearch和elasticsearch-js的较新版本。 Amazon ES的连接处理程序 使与Amazon ES兼容。 它使用aws-sdk向Amazon ES端点发出已签名的请求。 # Install the connector, elasticsearch client and aws-sdk npm install --save http-aws-es aws-sdk elasticsearch // create an elasticsearch client for your Amazon ES let es = require ( 'elasticsearch' ) . Client ( { hosts : [ 'https://amazon-es-host.us-east-1.es.amazonaws.c 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵 es在使用cardinality实现count(distinct)时会在准确性和及时性上做一定的取舍。可以在使用cardinality时,配置下面的参数来增加准确性,牺牲的是时间和内存。Elasticsearch 去重统计 按照deviceId 去重统计总数。 1、聚合为什么慢? 大多数时候对单个字段的聚合查询还是非常快的, 但是当需要同时聚合多个字段时,就可能会产生大量的分组,最终结果就是占用 es 大量内存,从而导致 OOM 的情况发生。 实践应用发现,以下情况都会比较慢: 1)待聚合文档数比较多(千万、亿、十亿甚至更多); 2)聚合条件比较复杂(多重条件聚合); 3)全量聚合(翻页的场景用)。 2、聚合优化方案探讨 优化方案一 1、客户反馈es集群存在很多慢查询,检查发现都是term查询,而且进行了sort排序,但是size是top 15,这样的查询不至于一直报慢查询。 2、继续检查日志,发现所有慢查询都是一个索引报的,也就是其他的索引的查询都是正常的 3、定位到索引后,_cat/shards/in 本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Elasticsearch中两个核心概念:Global Ordinals和High Cardinality。 1、问题引出默认情况下,Elasticsearch 已针对大多数用例进行了优化,确保在写入性能和查询性能之间取得平衡。我们将介绍一些聚合性能优化的可配置参数,其中部分改进是以牺牲写入性能... Elasticsearch 是一个非常灵活且功能丰富的应用程序,它提供了许多不同的方式来查询你的数据。 但是你是否曾经遇到过低于你希望的查询速度? 对于像 Elasticsearch 这样的分布式系统,可能有各种影响查询性能的因素,包括外部因素,例如负载均衡器设置、网络延迟(带宽、NIC 卡/驱动程序)等。 在本博客中,我将讨论导致查询缓慢的原因以及如何在 Elasticsearch 的上下文中识别它们。 本文源于我们一些常见的故障排除方法,这些方法可能需要人们对 Elasticsearch 的工作原理相 Elasticsearch查询慢问题排查思路 Elasticsearch的查询慢的问题往往是由多种因素造成的,同时我们也需要遵循Elasticsearch的查询准则:ES适合top N的查询,不适合大数据量返回的查询。 场景1 内存参数配置不合...
Ordinal | Term ------------------------------- 0 | status_deleted 1 | status_pending 2 | status_published Doc | 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的数据量。 创建一个新模型 您应该覆盖index和type属性以确定文档路径。 use Isswp101 \ Persimmon \ Models \ BaseElasticsearchModel ; use Isswp101 \ Persimmon \ Persistence \ Persistence ; use Isswp101 \ Persimmon \ Contracts \ PersistenceContract ; class 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.htm spring-boot-starter-elasticsearchHighLevelClient 封装elasticsearch-rest-high-level-client,添加了连接池功能(官方包中没有提供连接池);同时对接spring-boot-starter 作者:古晋 电子邮件: springboot版本:2.0.2.RELEASE 为Maven安装项目 git pull git@github.com:guzhandong/spring-boot-starter-elasticsearchHighLevelClient.git cd spring-boot-starter-elasticsearchHighLevelClient mvn clean install 在pom.xml中添加依赖属性 <dependency> <groupId>com.guzhandong.springframework.boot</groupId> <artifactId>spring-boot-starter-elasticsearchRestHighLeave 在可获得较新的版本,用于Elasticsearch和elasticsearch-js的较新版本。 Amazon ES的连接处理程序 使与Amazon ES兼容。 它使用aws-sdk向Amazon ES端点发出已签名的请求。 # Install the connector, elasticsearch client and aws-sdk npm install --save http-aws-es aws-sdk elasticsearch // create an elasticsearch client for your Amazon ES let es = require ( 'elasticsearch' ) . Client ( { hosts : [ 'https://amazon-es-host.us-east-1.es.amazonaws.c 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵 es在使用cardinality实现count(distinct)时会在准确性和及时性上做一定的取舍。可以在使用cardinality时,配置下面的参数来增加准确性,牺牲的是时间和内存。Elasticsearch 去重统计 按照deviceId 去重统计总数。 1、聚合为什么慢? 大多数时候对单个字段的聚合查询还是非常快的, 但是当需要同时聚合多个字段时,就可能会产生大量的分组,最终结果就是占用 es 大量内存,从而导致 OOM 的情况发生。 实践应用发现,以下情况都会比较慢: 1)待聚合文档数比较多(千万、亿、十亿甚至更多); 2)聚合条件比较复杂(多重条件聚合); 3)全量聚合(翻页的场景用)。 2、聚合优化方案探讨 优化方案一 1、客户反馈es集群存在很多慢查询,检查发现都是term查询,而且进行了sort排序,但是size是top 15,这样的查询不至于一直报慢查询。 2、继续检查日志,发现所有慢查询都是一个索引报的,也就是其他的索引的查询都是正常的 3、定位到索引后,_cat/shards/in 本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Elasticsearch中两个核心概念:Global Ordinals和High Cardinality。 1、问题引出默认情况下,Elasticsearch 已针对大多数用例进行了优化,确保在写入性能和查询性能之间取得平衡。我们将介绍一些聚合性能优化的可配置参数,其中部分改进是以牺牲写入性能... Elasticsearch 是一个非常灵活且功能丰富的应用程序,它提供了许多不同的方式来查询你的数据。 但是你是否曾经遇到过低于你希望的查询速度? 对于像 Elasticsearch 这样的分布式系统,可能有各种影响查询性能的因素,包括外部因素,例如负载均衡器设置、网络延迟(带宽、NIC 卡/驱动程序)等。 在本博客中,我将讨论导致查询缓慢的原因以及如何在 Elasticsearch 的上下文中识别它们。 本文源于我们一些常见的故障排除方法,这些方法可能需要人们对 Elasticsearch 的工作原理相 Elasticsearch查询慢问题排查思路 Elasticsearch的查询慢的问题往往是由多种因素造成的,同时我们也需要遵循Elasticsearch的查询准则:ES适合top N的查询,不适合大数据量返回的查询。 场景1 内存参数配置不合...
什么是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的目的是为了减少内存使用、加快聚合统计,在大多数情况下其表现出来的性能都非常好。之所以会影响到查询性能,与其构建时机有关:
这样的构建方式,在遇到某个字段的值种类很多(即下文所述的High Cardinary问题)时会变的非常慢,会严重影响聚合查询速度,即使Filter出来的document很少也需要花费很久,也就是上文笔者遇到的问题,即在High Cardinary情况下,构建Global Ordinals非常慢。因为我们新加的hash字段对于每条数据都不一样,所以当写入越来越多的数据后,聚合查询越来越慢(大概超过5000W条之后)。
有哪些优化方法?
虽然在Lucene 7.1中,针对global ordinals的构建有些优化(LUCENE-7905),但是仍然不能避免这样的问题。目前有这样几种优化方法(或者说是缓解之法,目前尚未发现完美的方法):
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的数据量。 创建一个新模型 您应该覆盖index和type属性以确定文档路径。 use Isswp101 \ Persimmon \ Models \ BaseElasticsearchModel ; use Isswp101 \ Persimmon \ Persistence \ Persistence ; use Isswp101 \ Persimmon \ Contracts \ PersistenceContract ; class 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)-英语-对照版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中英对照文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.htm spring-boot-starter-elasticsearchHighLevelClient 封装elasticsearch-rest-high-level-client,添加了连接池功能(官方包中没有提供连接池);同时对接spring-boot-starter 作者:古晋 电子邮件: springboot版本:2.0.2.RELEASE 为Maven安装项目 git pull git@github.com:guzhandong/spring-boot-starter-elasticsearchHighLevelClient.git cd spring-boot-starter-elasticsearchHighLevelClient mvn clean install 在pom.xml中添加依赖属性 <dependency> <groupId>com.guzhandong.springframework.boot</groupId> <artifactId>spring-boot-starter-elasticsearchRestHighLeave 在可获得较新的版本,用于Elasticsearch和elasticsearch-js的较新版本。 Amazon ES的连接处理程序 使与Amazon ES兼容。 它使用aws-sdk向Amazon ES端点发出已签名的请求。 # Install the connector, elasticsearch client and aws-sdk npm install --save http-aws-es aws-sdk elasticsearch // create an elasticsearch client for your Amazon ES let es = require ( 'elasticsearch' ) . Client ( { hosts : [ 'https://amazon-es-host.us-east-1.es.amazonaws.c 赠送jar包:elasticsearch-rest-high-level-client-6.8.3.jar; 赠送原API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc.jar; 赠送源代码:elasticsearch-rest-high-level-client-6.8.3-sources.jar; 赠送Maven依赖信息文件:elasticsearch-rest-high-level-client-6.8.3.pom; 包含翻译后的API文档:elasticsearch-rest-high-level-client-6.8.3-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.3; 标签:elasticsearch、client、rest、high、level、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵 es在使用cardinality实现count(distinct)时会在准确性和及时性上做一定的取舍。可以在使用cardinality时,配置下面的参数来增加准确性,牺牲的是时间和内存。Elasticsearch 去重统计 按照deviceId 去重统计总数。 1、聚合为什么慢? 大多数时候对单个字段的聚合查询还是非常快的, 但是当需要同时聚合多个字段时,就可能会产生大量的分组,最终结果就是占用 es 大量内存,从而导致 OOM 的情况发生。 实践应用发现,以下情况都会比较慢: 1)待聚合文档数比较多(千万、亿、十亿甚至更多); 2)聚合条件比较复杂(多重条件聚合); 3)全量聚合(翻页的场景用)。 2、聚合优化方案探讨 优化方案一 1、客户反馈es集群存在很多慢查询,检查发现都是term查询,而且进行了sort排序,但是size是top 15,这样的查询不至于一直报慢查询。 2、继续检查日志,发现所有慢查询都是一个索引报的,也就是其他的索引的查询都是正常的 3、定位到索引后,_cat/shards/in 本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Elasticsearch中两个核心概念:Global Ordinals和High Cardinality。 1、问题引出默认情况下,Elasticsearch 已针对大多数用例进行了优化,确保在写入性能和查询性能之间取得平衡。我们将介绍一些聚合性能优化的可配置参数,其中部分改进是以牺牲写入性能... Elasticsearch 是一个非常灵活且功能丰富的应用程序,它提供了许多不同的方式来查询你的数据。 但是你是否曾经遇到过低于你希望的查询速度? 对于像 Elasticsearch 这样的分布式系统,可能有各种影响查询性能的因素,包括外部因素,例如负载均衡器设置、网络延迟(带宽、NIC 卡/驱动程序)等。 在本博客中,我将讨论导致查询缓慢的原因以及如何在 Elasticsearch 的上下文中识别它们。 本文源于我们一些常见的故障排除方法,这些方法可能需要人们对 Elasticsearch 的工作原理相 Elasticsearch查询慢问题排查思路 Elasticsearch的查询慢的问题往往是由多种因素造成的,同时我们也需要遵循Elasticsearch的查询准则:ES适合top N的查询,不适合大数据量返回的查询。 场景1 内存参数配置不合...
相信看完上文,读者已经知道什么是High Cardinality了。所谓High Cardinality,指的是Large Number of Unique Values,即某个字段的值有很多很多种,比如笔者业务中的那个hash字段。在Elasticsearch,High Cardinality会带来各种问题,百害而无一利,所以应该尽量避免,避免不了也要做到心中有数,在出问题时可以及时调整。
本文结合笔者在实践过程中遇到的由High Cardinality引起Global Ordinals构建过慢,从而导致聚合查询变慢的问题,阐述了Global Ordinals和High Cardinality两个核心概念,希望对遇到类似问题的人有所帮助。目前,针对我们的业务场景,相关的调整有:第一,使用"execution_hint": "map"来避免构建Global Ordinals;第二,尝试在数据上传端增加对压缩友好的唯一键来作为去重对象,比如uuid4;第三,减小index的切割时间,比如从weekly index变成daily index,从而降低index中单个shard的数据量。