首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何加倍提升 Elasticsearch 查询性能

如何加倍提升 Elasticsearch 查询性能

原创
作者头像
技术姐
修改2019-01-07 10:21:59
1.9K2
修改2019-01-07 10:21:59
举报

一、背景

我们在使用Elasticsearch进行查询的过程中发现,如果查询时间跨度大,查询数据集比较庞大,即使只是返回少量的结果,查询耗时仍然比较长。我们通过分析profile和debug跟踪整个查询流程,确认耗时的原因,针对业务特性,提出了相关的优化方案,可以对该类查询提升三到五倍的性能。

二、流程分析

1、Elasticsearch的查询流程

Elasticsearch使用Lucene作为内部引擎。Elasticsearch的查询流程分为两个阶段。第一个阶段为QueryPhase,收到请求的协调节点将查询优化拆分成多个Shard级别的请求发送到对应的数据节点上,数据节点收到请求后调用Lucene进行查询,查询完成后把结果返回给协调节点。第二个阶段为FetchPhase,协调节点将查询结果进行汇总,得到一个文档的id的集合,然后据此依次给数据节点发送查询具体的_source,docvalue等数据信息,最终把丰富好的结果返回给用户。

2、Lucene的查询流程

Lucene的查询发生在Elasticsearch的QueryPhase阶段中数据节点内部。我们知道Lucene中,ES index 底层是分Segment块存储的。当一条带有多个条件的查询进入Lucene后,Lucene会先做一次裁剪,然后对涉及到的segments遍历进行查询。查询流程可以简单分为两个阶段。第一个阶段为评估(Approximation),是对每条子语句单独进行权重计算和匹配,计算出每条子语句的结果集id具体的偏移位置和有效范围。第二个阶段为遍历(Iteration),在这个阶段会选出结果集最少的子语句的结果集作为遍历的Leader,在遍历的过程中,从中筛选符合其他查询条件的数据,得到最终的结果集。

3、查询流程中的四级缓存

Elasticsearch的查询过程中总共有四层缓存,第一层缓存是Elasticsearch的RequestCache,缓存的是整个查询的Shard级别的查询结果,如果数据节点收到重复的查询语句的请求,那么这级缓存就能利用上。第二层缓存是Lucene的LRUQueryCache,缓存的是单条子查询语句的查询结果,如果有类似的查询进来,部分子查询重复,那么LRUQueryCache便能够发挥作用。第三层缓存是程序内存中的MMAP文件映射缓存,Lucene通过DirectByteBuffer将整个文件或者文件的一部分映射到堆外内存,由操作系统负责获取页面请求和写入文件,Lucene就只需要读取内存数据,这样可以实现非常快速的IO操作。第四层缓存是文件系统缓存,是由系统控制的。

理解以上两个流程图后,我们对两个流程图做一个汇总,并标明缓存发生的位置。

三、问题分析

我们已经了解了Elasticsearch以及Lucene的查询流程以及其中的缓存过程,那么耗时究竟在哪呢?接下来我们会分析其中存在的问题。

1、QueryPhase阶段生成LRUQueryCache耗时

Lucene会判断每条子语句是否值得做缓存,如果值得做缓存,便会进入到缓存分支,生成LRUQueryCache,那么下一次带有该子查询语句的查询便可以大大缩短查询时间,所以这次缓存并不需要对size限制,因为每次查询都可能会从缓存中得到。而这恰恰成为了查询耗时长的罪魁祸首。通常情况下,Lucene的缓存策略会对时间(索引有时间字段)的范围查询进行缓存,而如果没有其他维度限制,单单长时间范围的缓存查询会过滤出大量数据。假设我们查询一个月的前200条数据,而Lucene总共会对整一个月的结果id进行缓存。这可能是几亿条,几十亿条,这个耗时可能是真实的查询耗时的几百倍。

LRUQueryCache本身存在的意义是非常重要的,它能将大量重复的批量短查询缓存起来,对于降低整个系统的压力,减少重复的小io操作具有非常重要的作用。但通常情况下,长时间跨度的大查询是由用户手动发起的,并不会频繁或者反复进行,所以LRUQueryCache带来的后续查询体验提升微乎其微,而第一次低效的查询体验已经能让用户崩溃。所以对于长时间跨度的QueryCache对于我们而言没有太大的意义。这块耗时经测试占了该类慢查询总耗时的90%,其中真实的查询耗时与其相比是很小的,所以我们考虑尝试去掉对慢查询的LRUQueryCache。

2、QueryPhase阶段Segment查询耗时

由Lucene的查询流程我们知道,Lucene对涉及到的segments进行遍历。segments越多,查询耗时就越长。segments内部存储的数据越无序,越不利于缓存,查询耗时也越长。所以需要合理的配置Segment的Merge策略,减少过多的Segment存在,但Merge对集群和磁盘io的压力比较大,这点需要考虑。Elasticsearch6.3的新特性中index-sorting支持对数据根据配置的字段进行排序,经测试对查询性能有很大提升。

四、解决方案

1、QueryPhase阶段生成LRUQueryCache 缓存策略优化

为了避免缓存占用过多的查询耗时,我们在Lucene查询的评估阶段,先计算所有可能成为Leading Query 的查询子句的结果集数量 ,并将最小的结果传递给接下来的缓存分支。我们根据我们的业务场景,针对性地设置了一些阈值,确保缓存分支的耗时超出查询耗时一个数量级的时候,就不做单条查询子句的缓存。

2、利用index-sorting优化查询

Index-sorting新特性能够在数据写入时,将数据按照指定的字段的值进行排序。如果查询中包含指定的字段,那查询只需要读取相邻的文件块。我们根据业务的查询场景,对结果集数量比较多的字段进行排序。

五、优化结果

1、QueryPhase阶段生成LRUQueryCache优化结果

我们考虑尝试去掉对慢查询的LRUQueryCache,图1是去掉之前的监控,查询毛刺平均耗时在50ms左右,图2是优化后的监控,查询毛刺平均在18ms左右,带来三倍左右的整体查询耗时的优化。

图1
图1
图2
图2

2、QueryPhase阶段IndexSorting优化结果

IndexSorting对于小查询的优化不明显,我们尝试通过构造大查询来反馈,对于未排序和排序的数据都模拟查询7天的数据,未排序的数据以上查询平均耗时为2s,排序的数据查询平均耗时为400ms,查询性能可提升5倍。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云硬盘
云硬盘(Cloud Block Storage,CBS)为您提供用于 CVM 的持久性数据块级存储服务。云硬盘中的数据自动地在可用区内以多副本冗余方式存储,避免数据的单点故障风险,提供高达99.9999999%的数据可靠性。同时提供多种类型及规格,满足稳定低延迟的存储性能要求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档