这样优化Elasticsearch,显著提升查询速度

elasticsearch的搜索效率与多方面有关,例如系统资源、数据查询方式、数据索引方式等,本文从各方面讨论如何进行搜索速度的优化,提升查询的性能。

01

预留足够的堆外内存

首先,操作系统普通的文件读写会经过Page Cache,从而加速文件的访问,一旦命中缓存可以现实提升读写速度,这部分使用的就是堆外内存。elasticsearch底层是lucene,涉及到大量的索引文件的读写,所以必须预留足够的堆外内存以供文件系统的访问使用。其次,elasticsearch默认的存储方式index.store.type是hybridfs,它是niofs和mmapfs的组合,目前term、norms、doc values使用的是mmapfs,实际映射到了Lucene MMapDirectory数据结构,而其他的则是使用了niofs的存储方式,实际映射到Lucene NIOFSDirectory。

通过MMapfs方式的,会将文件映射到内存,索引数据会被预加载到操作系统的Page Cache中,而如果能够使用Page Cache,则直接查询内存,避免通过I/O读取,查询速度会更快(这不是绝对的,后续专门针对MMapfs再展开细节)

最后要注意的是,当内存不足时会导致swap的发生,内存的换入换出会导致大量的I/O,查询速度会严重下降,一般情况下建议在生产上禁用swap或者配置swappiness为较小的值,例如1,降低swap的趋势,也可以通过设置elasticsearch的bootstrap.memory_lock: true锁定内存,禁止swap。

02

使用SSD替换普通机械磁盘

当存在大量的查询请求时,磁盘IO压力会显著增加,在SSD资源有限的情况下,可以进行数据的冷热分离,将高频查询的数据放到SSD上。

03

禁用复杂的文档模型

一般情况下,禁止使用nested、partent-child这种文档模型,因为他们会严重影响查询性能。parent-child只适用于一对多的关系,且父子关系文档数据量差别很大的情况,一旦确实使用了这种文档模型,但是查询性能又较差的时候,可以考虑增加主分片的数量,然后进行reindex。

04

定期进行forcemerge

定期对索引进行force merge,特别是对于一些没有写请求的历史索引,减少segments的数量,能够显著提高查询速度,因为索引段很多的情况下,查询时需要遍历所有的索引段,势必导致查询速度缓慢。

这样优化Elasticsearch,显著提升查询速度

05

查询语句优化

  • 不要使用正则表达式查询
  • 尽量不要使用模糊匹配
  • 对于不需要使用评分机制的查询,使用filter替代query,一是可以减少打分的操作损耗,二是filter可以走Node Query Cache,查询速度更快。

06

字段冗余

利用一些富化、冗余字段来支持一些范围聚合查询,类比关系数据库中配置一些冗余字段减少join操作,因为这些操作的成本都比较高。

07

增加协调节点

elasticsearch协调节点负责转发请求到各个数据节点,然后对结果进行汇总整合,如果集群存在大量的查询操作且需要大量的数据聚合,则CPU和内存的压力都会比较大,这种情况下可以根据需要适当增加一些协调节点。

08

预热文件系统cache

如果elasticsearch主机重启,则文件系统缓存为空,此时的搜索会比较慢,可以使用index.store.preload设置,指定将哪些扩展名的文件加载到内存中.可以通过elasticsearch.yml指定全局的,也可以在创建索引时指定索引级别的配置。

  • 全局的配置
  • 索引级别的配置

这样优化Elasticsearch,显著提升查询速度

09

开启ARS自适应副本机制

默认情况下,协调节点将请求简单轮训到每个分片副本上,而不管副本分片所在节点的实际负载情况,这样子就会导致某些情况下某些节点负载比较高,响应速度比较慢,在ES6.1以上的版本,这种情况下可以开启ARS,利用自适应副本选择机制,智能的选择将请求转发给负载较低的分片所在节点。

这样优化Elasticsearch,显著提升查询速度

10

预热全局序号global ordinals

什么是global ordinals呢?要理解global ordinals首先需要理解ordinals, 假设有n条数据,每条数据有一个字段fruit(keyword类型),其值有三种可能性:Orange、Apple、Peach,那么每条数据至少需要存储5-6个字符的长度,当数据量越来越大,则占用的内存会越来越高。

为了减少内存使用,考虑将字符串排序后进行编号,形成一张映射表,然后在每条数据中使用相应字符串的序号来表示,例如1-Orange,2-Apple,3-Peach,这里的映射表中的序列号就是ordinals。

当我们对fruit字段做Terms聚合时,请求会通过协调节点转发到各个数据节点分片上执行,Ordinals是针对segment级别的,针对Segment而言,同一个字符串在不同的segment Ordinals中可能对应的序号是不同的,当查询完所有的segment以后就又涉及到整合合并,于是针对shard级别的global ordinals出现了,能够在shard级别上进行映射,一方面减少内存内存使用,另外一方面提升聚合的效率。

因为global ordinals面向的是shard级别,所以当一个Shard的Segment发生变动时就需要重新构建Global Ordinals,默认情况下global Ordinals是在收到聚合查询请求并且该查询会命中相关字段时构建,而创建或更新的时候需要一定的时间,特别是在数据量巨大的情况下,构建可能会花费比较长的时间,所以可以考虑在设置mapping的时候配置在refresh时告诉ES预先加载全局序号,减少在查询时创建或更新的时间,一定程度上提升查询速度。

这实际上是牺牲写入的效率从而提升查询的效率的一种方法。

这样优化Elasticsearch,显著提升查询速度

11

使用execution hint配置terms聚合的机制

elasticsearch的terms聚合有两种方式:

  • 直接使用字段值进行聚合,也就是map
  • 使用global_ordinals,每个global_ordinals分配一个bucket,对bucket进行聚合

当存在数据量较大时,global_ordinals的方式效果明显,而当查询匹配的文档比较少的时候使用map直接聚合的效果比较好,因为不需要进行global_ordinals的构建。

这样优化Elasticsearch,显著提升查询速度

12

查询语句分析

终极大招,如果如果语句查询很慢,但是不清楚原因,可以在query查询语句请求体上加上"profile": "true",启用profile API,这样子可以看到一个搜索聚合请求,是如何拆分成底层的 Lucene 请求,并且显示每部分的耗时情况,以方便进一步分析,以下是对索引customer使用profile API进行分析的案例。

这样优化Elasticsearch,显著提升查询速度

这样优化Elasticsearch,显著提升查询速度

上一篇:Codeforces Round #404 (Div. 2)(A.水,暴力,B,排序,贪心)


下一篇:SQL单用户实列登陆恢复master[sqlservr.exe]