专栏首页时序数据库专栏如何加倍提升 Elasticsearch 查询性能
原创

如何加倍提升 Elasticsearch 查询性能

一、背景

我们在使用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
图2

2、QueryPhase阶段IndexSorting优化结果

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

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Solr 企业级搜索引擎简介

    Solr 是一个独立的企业级搜索引擎服务器,并提供类似web-service 的API接口。可以通过http协议把文档以xml格式的方式放入索引库。同样通过H...

    田春峰-JCJC错别字检测
  • 关于lucene发展和多语言实现的方向

        多语言lucene的发展无疑是基于java lucene的。一切的功能特性和兼容性的问题都要以java lucene为主。java lucene是其...

    田春峰-JCJC错别字检测
  • Lucene入门实例

    下面的这个例子摘自Lucene in Action (2010版本),上面的示例使用的是Lucene 3.x,现在的Lucene最新版本是4.10.3。由于Lu...

    TheOneGIS
  • Elasticsearch初识

    用户1621453
  • 如何加倍提升 Elasticsearch 查询性能

    我们在使用Elasticsearch进行查询的过程中发现,如果查询时间跨度大,查询数据集比较庞大,即使只是返回少量的结果,查询耗时仍然比较长。我们通过分...

    用户1644123
  • 1.Elasticsearch简介

    本系列文章参考地址: - https://www.elastic.co/guide/en/elasticsearch/reference/current/i...

    IT云清
  • 不选择使用Lucene的6大原因

         Lucene是开放源代码的全文搜索引擎工具包,凭借着其强劲的搜索功能和简单易用的实现,在国内已经很普及,甚至一度出现了言搜索必称Lucene的盛...

    田春峰-JCJC错别字检测
  • 开源中文分词框架分词效果对比smartcn与IKanalyzer

      中文分词一直是自然语言处理的一个痛处,早在08年的时候,就曾经有项目涉及到相关的应用(Lunce构建全文搜索引擎),那时的痛,没想到5年后的今天依然存在,切...

    数据饕餮
  • Confluence 6 查看索引和提示 原

    Confluence 使用被称为 Lucene 的搜索引擎。如果你希望在你的 Confluence站点中查看更多有关索引的细节,你可以下载并且运行 Luke。L...

    HoneyMoose

扫码关注云+社区

领取腾讯云代金券