乐趣区

关于java:Elasticsearch-聚合性能优化六种方式

本文章转自:乐字节

文章次要解说:Elasticsearch 聚合性能优化

获取更多 Java 相干材料能够关注公众号《乐字节》发送:999

问题引出

默认状况下,Elasticsearch 已针对大多数用例进行了优化,确保在写入性能和查问性能之间获得均衡。咱们将介绍一些聚合性能优化的可配置参数,其中局部改良是以就义写入性能为代价的。指标是将聚合优化招数汇总到一个易于消化的短文中,为大家的 Elasticsearch 集群聚合性能优化提供一些领导。

聚合实战问题

问题 1:1 天的数据 70W,聚合 2 次分桶失常查问工夫是 200ms 左右,减少了一个去重条件,就 10-13 秒了,有优化的中央不?

问题 2:请问在很多 terms 聚合的状况下,怎么优化检索?我的场景在无聚合时,吞吐量有 300,在退出 12 个聚合字段后,吞吐量不到 20。

问题 3:哪位兄弟帮忙发一个聚合优化的链接,我这个聚合几千万就好几秒了?

认知前提

3.1 Elasticsearch 聚合是不严格精准的

起因在于:数据扩散到多个分片,聚合是每个分片的取 Top X,导致后果不精准。

3.2 从业务层面躲避全量聚合

聚合后果的精准性和响应速度之间是绝对矛盾的。

失常业务开发,产品经理往往要求:

第一:疾速秒级或者毫秒级聚合响应。

第二:聚合后果精准。

殊不知,二者不可兼得。

遇到相似两者都要兼得的需要,倡议从架构选型和业务层面做躲避解决。

3.3 刷新频率

如下图所示,Elasticsearch 中的 1 个索引由一个或多个分片组成,每个分片蕴含多个 segment(段),每一个段都是一个倒排索引。

在 lucene 中,为了实现高索引速度,应用了 segment 分段架构存储。一批写入数据保留在一个段中,其中每个段最终落地为磁盘中的单个文件。

如下图所示,将文档插入 Elasticsearch 时,它们会被写入缓冲区中,而后在刷新时定期从该缓冲区刷新到段中。刷新频率由 refresh_interval 参数管制,默认每 1 秒产生一次。也就是说,新插入的文档在刷新到段(内存中)之前,是不能被搜寻到的。

刷新的实质是:写入数据由内存 buffer 写入到内存段中,以保障搜寻可见。
来看个例子,加深对 refresh_inteval 的了解,正文局部就是解读。

对于是否须要实时刷新:

如果新插入的数据须要近乎实时的搜寻性能,则须要频繁刷新。

如果对最新数据的检索响应没有实时性要求,则应减少刷新距离,以进步数据写入的效率,从而应开释资源辅助进步查问性能。

对于刷新频率对查问性能的影响:

因为每刷新一次都会生成一个 Lucene 段,刷新频率越小就意味着同样工夫距离,生成的段越多。
每个段都要耗费句柄和内存。

每次查问申请都须要轮询每个段,轮询结束后再对后果进行合并。

也就意味着:refresh_interval 越小,产生的段越多,搜寻反而会越慢;反过来说,加大 refresh_interval,会绝对晋升搜寻性能。

聚合性能优化猛招

4.1 启用 eager global ordinals 晋升高基数聚合性能

实用场景:高基数聚合。

高基数聚合场景中的高基数含意:一个字段蕴含很大比例的惟一值。

global ordinals 中文翻译成全局序号,是一种数据结构,利用场景如下:

基于 keyword,ip 等字段的分桶聚合,蕴含:terms 聚合、composite 聚合等。

基于 text 字段的分桶聚合(前提条件是:fielddata 开启)。

基于父子文档 Join 类型的 has_child 查问和 父聚合。

global ordinals 应用一个数值代表字段中的字符串值,而后为每一个数值调配一个 bucket(分桶)。

global ordinals 的实质是:启用 eager_global_ordinals 时,会在刷新(refresh)分片时构建全局序号。这将构建全局序号的老本从搜寻阶段转移到了数据索引化(写入)阶段。

创立索引的同时开启:eager_global_ordinals。

留神:开启 eager_global_ordinals 会影响写入性能,因为每次刷新时都会创立新的全局序号。为了最大水平地缩小因为频繁刷新建设全局序号而导致的额定开销,请调大刷新距离 refresh_interval。

动静调整刷新频率的办法如下:

该招数的实质是:以空间换工夫。

4.2 插入数据时对索引进行预排序

Index sorting(索引排序)可用于在插入时对索引进行预排序,而不是在查问时再对索引进行排序,这将进步范畴查问(range query)和排序操作的性能。

在 Elasticsearch 中创立新索引时,能够配置如何对每个分片内的段进行排序。

这是 Elasticsearch 6.X 之后版本才有的个性。

Index sorting 实战举例:

如上示例是在:创立索引的设置局部设置待排序的字段:cur_time 以及排序形式:desc 降序。

留神:预排序将减少 Elasticsearch 写入的老本。在某些用户特定场景下,开启索引预排序会导致大概 40%-50% 的写性能降落。

也就是说,如果用户场景更关注写性能的业务,开启索引预排序不是一个很好的抉择。

4.3 应用节点查问缓存

节点查问缓存(Node query cache)可用于无效缓存过滤器(filter)操作的后果。如果屡次执行同一 filter 操作,这将很无效,然而即使更改过滤器中的某一个值,也将意味着须要计算新的过滤器后果。

例如,因为“now”值始终在变动,因而无奈缓存在过滤器上下文中应用“now”的查问。

那怎么应用缓存呢?通过在 now 字段上利用 datemath 格局将其四舍五入到最靠近的分钟 / 小时等,能够使此类申请更具可缓存性,以便能够对筛选后果进行缓存。

对于 datemath 格局及用法,举个例子来阐明:

以下的示例,无奈应用缓存。

然而,上面的示例就能够应用节点查问缓存。

上述示例中的“now-1h/m”就是 datemath 的格局。

更细化点说,如果以后工夫 now 是:16:31:29,那么 range query 将匹配 my_date 介于:15:31:00 和 15:31:59 之间的工夫数据。

同理,聚合的前半部分 query 中如果有基于工夫查问,或者后半局部 aggs 局部中有基于工夫聚合的,倡议都应用 datemath 形式做缓存解决以优化性能。

4.4 应用分片申请缓存

聚合语句中,设置:size:0,就会应用分片申请缓存缓存后果。

size = 0 的含意是:只返回聚合后果,不返回查问后果。

4.5 拆分聚合,使聚合并行化

这里有个认知前提:Elasticsearch 查问条件中同时有多个条件聚合,这个时候的多个聚合不是并行运行的。

这里就有疑难:是不是能够通过 msearch 拆解多个聚合为单个子语句来改善响应工夫?

什么意思呢,给个 Demo,toy_demo_003 数据起源:基于儿童积木玩具图解 Elasticsearch 聚合
示例一:惯例的多条件聚合实现

如下响应工夫:15 ms。

示例二:msearch 拆分多个语句的聚合实现

如下响应工夫:9 ms。

来个比照验证吧:

蓝色:相似示例一,单个 query 中蕴含多个聚合,聚合数别离是:1,2,5,10。

红色:相似示例二,multi_search 拆解多个聚合,拆分子句个数别离为:1,2,5,10。

横轴:蓝色对应聚合个数;红色对应子句个数;

纵轴:响应工夫,响应工夫越短、性能越好。

初步论断是:

默认状况下聚合不是并行运行。

当为每个聚合提供本人的查问并执行 msearch 时,性能会有显著晋升。

尤其在 10 个聚合的场景下,性能晋升了靠近 2 倍。

因而,在 CPU 资源不是瓶颈的前提下,如果想缩短响应工夫,能够将多个聚合拆分为多个查问,借助:msearch 实现并行聚合。

4.6 将聚合中的查问条件挪动到 query 子句局部

示例一:

示例二:

示例一和示例二的本质区别:

第二个查问已将此过滤器提取到较高级别,这应使聚合共享后果。

如下比照试验表明,因为 Elasticsearch 本身做了优化,示例一(蓝色)和示例二(红色)响应工夫基本一致。

更多验证须要联合业务场景做一下比照验证,精简起见,举荐应用第二种。

更多优化参考

官网对于检索性能优化同样实用于聚合

https://www.elastic.co/guide/…

分片数设置多少正当?

https://www.elastic.co/cn/blo…

堆内存大小设置?

https://www.elastic.co/cn/blo…

禁用 swapping

https://www.elastic.co/guide/…

小结

本文的六大猛招出自:Elastic 原厂征询架构师 Alexander 以及 Coolblue 公司的软件开发工程师 Raoul Meyer。

六大猛招中的 msearch 并行聚合形式,令人眼前一亮,相比我在业务实战中用的多线程形式实现并行,要“高级”了许多。

我联合本人的聚合优化实际做了翻译和扩大,心愿对大家的聚合性能优化有所帮忙。
欢送留言写下您的聚合优化实际和思考。

感激大家的认同与反对,小编会继续转发《乐字节》优质文章

退出移动版