欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Elasticsearch搜索速度的优化

程序员文章站 2022-07-05 13:08:23
...

为文件系统cache预留足够的内存

在一般情况下,应用程序的读写都会被操作系统“cache”(除了direct方式),cache保存在系统物理内存中(线上应该禁用swap),命中cache可能降低对磁盘的直接访问频率。搜索很依赖对系统cache的命中,如果某个请求需要从磁盘读取数据,则一定会产生相对较高的延迟。应该至少为系统cache预留一半的可用物理内存,更大的内存有更高的cache命中率。

使用更快的硬件

写入性能对CPU的性能更敏感,而搜索性能在一般情况下更多的是在于I/O能力,使用SSD会比旋转类存储介质好得多。尽量避免使用NFS等远程文件系统,如果NFS比本地存储慢3倍,则在搜索场景下响应速度可能会慢10倍左右。这可能是因为搜索请求有更多的随机访问。

如果搜索类型属于计算比较多,则可以考虑使用更快的CPU。

文档模型

为了让搜索时的成本更低,文档应该合理建模。特别是应该避免join操作,嵌套(nested)会使查询慢几倍,父子(parent-child)关系可能使查询慢数百倍,因此,如果可以通过非规范化(denormalizing)文档来回答相同的问题,则可以显著地提高搜索速度。

预索引数据

可以针对某些查询的模式来优化数据的索引方式。例如,如果所有文档都有一个price字段,并且大多数查询在一个固定的范围上运行range聚合,那么可以通过将范围“pre-indexing”到索引中并使用terms聚合来加快聚合速度(在建立索引时对文档进行富化,增加price_range字段,mapping为keyword类型。接下来,搜索请求可以聚合这个新字段,而不是在price字段上运行range聚合。)。

字段映射

有些字段的内容是数值,但并不意味着其总是应该被映射为数值类型,例如,一些标识符,将它们映射为keyword可能会比integer或long更好。

避免使用脚本

一般来说,应该避免使用脚本。如果一定要用,则应该优先考虑painless和expressions。

优化日期搜索

在使用日期范围检索时,使用now的查询通常不能缓存,因为匹配到的范围一直在变化。但是,从用户体验的角度来看,切换到一个完整的日期通常是可以接受的,这样可以更好地利用查询缓存。

为只读索引执行force-merge

为不再更新的只读索引执行force merge,将Lucene索引合并为单个分段,可以提升查询速度。当一个Lucene索引存在多个分段时,每个分段会单独执行搜索再将结果合并,将只读索引强制合并为一个Lucene分段不仅可以优化搜索过程,对索引恢复速度也有好处。

预热全局序号(global ordinals)

全局序号是一种数据结构,用于在keyword字段上运行terms聚合。它用一个数值来代表字段中的字符串值,然后为每一数值分配一个bucket。这需要一个对global ordinals和bucket的构建过程。默认情况下,它们被延迟构建,因为ES不知道哪些字段将用于terms聚合,哪此字段不会。可以通过配置映射在刷新(refresh)时告诉ES预先加载全局序数。

execution hint

terms聚合有两种不同的机制:

  • 通过直接使用字段值来聚合每个桶的数据(map)。
  • 通过使用字段的全局序号并为每个全局序号分配一个bucket(global_ordinals)。

ES使用global_ordinals作为keyword字段的默认选项,它使用全局序号动态地分配bucket,因此内存使用与聚合结果中的字段数量是线性关系。在大部分情况下,这种方式的速度很快。但当查询只会匹配少量文档时,可以考虑使用map。

预热文件系统cache

如果ES主机重启,则文件系统缓存将为空,此时搜索会比较慢。可以使用index.store.preload设置,通过指定文件扩展名,显式地告诉操作系统应该将哪些文件加载到内存中,或者在索引创建时设置。

如果文件系统缓存不够大,则无法保存所有数据,那么为太多文件预加载数据到文件系统缓存中会使搜索速度变慢,应谨慎使用。

转换查询表达式

在组合查询中可以通过bool过滤器进行and、or和not的多个逻辑组合检索,这种组合查询中的表达式在下面的情况下可以做等价转换。

(A|B) & (C|D) ==> (A&C) | (A&D) | (B&C) | (B&D)

调节搜索请求中的batched_reduce_size

该字段是搜索请求中的一个参数。默认情况下,聚合操作在协调节点需要等所有的分片都取回结果后才执行,使用batched_reduce_size参数可以不等待全部分片返回结果,而是在指定数量的分片返回结果之后就可以先处理一部分(reduce)。这样可以避免协调节点在等待全部结果的过程中占用大量内存,避免极端情况下可能导致的OOM。该字段的默认值为512。

使用近似聚合

近似聚合以牺牲少量的精确度为代价,大幅提高了执行效率,降低了内存使用。

深度优先还是广度优先

ES有两种不同的聚合方式:深度优先和广度优先。深度优先是默认设置,先构建完整的树,然后修剪无用节点。如官网的示例

限制搜索请求的分片数

一个搜索请求涉及的分片数量越多,协调节点的CPU和内存压力就越大。默认情况下,ES会拒绝超过1000个分片的搜索请求。我们应该更好地组织数据,让搜索请求的分片数更少。如果想调节这个值,则可以通过action.search.shard_count配置项进行修改。

虽然限制搜索的分片数并不能直接提升单个搜索请求的速度,但协调节点的压力会间接影响搜索速度,例如,占用更多内存会产生更多的GC压力,可能导致更多的stop-the-world时间等,因此间接影响了协调节点的性能。

利用自适应副本选择(ARS)提升ES响应速度

为了充分利用计算资源和负载均衡,协调节点将搜索请求轮询转发到分片的每个副本,轮询策略是负载均衡过程中最简单的策略,任何一个负载均衡器都具备这种基础的策略,缺点是不考虑后端实际系统压力和健康水平。

ES希望将搜索请求转发到副本的过程足够智能,此过程称为“自适应副本选择”。ES的ARS实现基于这样的一个公式:对每个搜索请求,将分片的每个副本进行排序,以确定哪个最可能是转发请求的“最佳”副本。