Elasticsearch 高级调优方法论之——根治慢查询!

1、引言

Elasticsearch 是非常灵活且功能丰富的搜索引擎,它提供了许多不同查询数据的方法。在实战业务场景中,经常会出现远远低于预期查询速度的慢查询。作为分布式系统的 Elasticsearch,可能有各种影响查询性能的因素,包括外部因素,如负载均衡设置,网络延迟(带宽,NIC 卡 / 驱动程序)等。

本文主要讨论可能导致慢查询的原因以及如何在 Elasticsearch 的上下文中识别它们?

本文主要源于常见慢查询故障的排除方法,阅读本文的前提需要你对 Elasticsearch 的原理有大致的了解。

如果不了解 Elastic 相关原理,可以移步:elastic.blog.csdn.net 或 历史文章。

如果不了解慢查询,可以移步:为什么 Elasticsearch 查询变得这么慢了?

本文的目的:根治慢查询。

2、Elasticsearch 慢查询六大症状及解决方案

在我们研究一些棘手的案例之前,让我们从一些最常见的慢查询及其解决方案开始。

2.1 症状 1:非活动(检索 / 写入)状态资源利用率也非常高

症状详情:每个分片都消耗资源(CPU / 内存)。即使没有索引 / 搜索请求,分片的存在也会消耗集群开销。

2.1.1 问题描述

集群中的分片太多,以至于任何查询执行起来都很慢。一个好的经验法则——确保每个节点的非冻结分片数量保持在:20 以下 / 每 GB 堆内存。

2.1.2 解决方案
  • 1、部署之前,设计先行。
    正如 VIVO 搜索技术总监振涛兄所说:“集群规划核心是容量预估,就好比你建个楼,必须规划好容量,不然说用多少就建多高,吃在地基撑不住!!”。

    任何部署的良好开端都是执行适当的容量规划,以帮助确定每个搜索用例的最佳分片数。

  • 2、减少分片数,实施冻结索引或添加其他节点以实现负载均衡。

  • 3、考虑冷热数据分离架构(适用于基于时间的索引)以及 Elasticsearch 中的翻转索引(rollover)/ 压缩索引(shrink)功能,以有效管理分片计数。

推荐阅读:我在 Elasticsearch 集群内应该设置多少个分片?
https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster

Elasticsearch5.x 冷热架构实现
https://www.elastic.co/cn/blog/hot-warm-architecture-in-elasticsearch-5-x

容量规划最佳实践(必读)
https://www.elastic.co/guide/en/elasticsearch/guide/master/capacity-planning.html

2.2 症状 2:线程池存在大量 rejected

搜索线程池显示“拒绝”计数的持续增加,该计数基于上次群集重新启动而累积。

1GET / _cat / thread_pool / search?v&h = node_name,name,active,rejected,completed

响应如下:

node_name             name   active rejected completed  
instance-0000000001   search      0       10         0  
instance-0000000002   search      0       20         0  
instance-0000000003   search      0       30         0  
2.2.1 问题描述

场景 1:查询的目标是太多分片,超过集群中的 CPU 核数。这会在搜索线程池中创建排队任务,从而导致搜索拒绝。

场景 2:磁盘 I/O 速度慢或在某些情况下完全饱和的 CPU 导致搜索排队。

2.2.2 解决方案

1、创建索引时采用 1 主分片 &1 副本模型。
使用索引模板是在创建索引阶段做好设置是个好方法。(7.0 及更高版本默认 1 主 1 副)。

2、 Elasticsearch 5.1 或更高版本支持搜索任务取消,这对于取消显示在任务管理 API 中慢查询任务非常有用。

任务管理:

1GET _tasks?nodes=nodeId1,nodeId2  

取消任务

1POST _tasks/oTUltX4IQMOUUVeiohTt8A:12345/_cancel  

3、要改进磁盘 I / O,请查看我们的存储建议,并确保使用推荐的硬件以获得最佳性能。

存储优化建议:
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/tune-for-indexing-speed.html#_use_faster_hardware

2.3 症状 3:高 CPU 和索引化延迟

当集群不堪重负时,度量标准关联显示 CPU 利用率高和索引化延迟大(如下图)。

null

Metric 指标 Kibana 核查方法:

https://www.elastic.co/guide/en/kibana/7.0/elasticsearch-metrics.html

2.3.1 问题描述

写入数据量大(索引化)会影响搜索性能。

2.3.2 解决方案

1、调大刷新频率

将 index.refresh_ interval(文档被索引到数据搜索可见时间间隔)增加到 30 s,通常有助于提高索引性能。

实战中要结合具体业务场景,可能会有所不同,因此测试是关键。这样避免了缺省一秒生成一个分段的麻烦。

2、对于重型索引用例,请检查我们的索引调整建议,以优化索引和搜索性能。

包含但不限于:
1)数据初始化阶段 refresh 设置 -1、副本设置为 0,以提升写入速度;写入完毕后复原。
2)关闭 swapping。
3)使用文件系统缓存。
4)使用自动生成 ID。

性能调优实践:
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/tune-for-indexing-speed.html#_disable_refresh_and_replicas_for_initial_loads

2.4 症状 4:副本增加后延时增大

在增加副本分片计数(例如,从 1 到 2)之后可以观察到查询等待时间。如果存在更多数据,则缓存的数据将很快被逐出,导致操作系统层面页面错误增加。

2.4.1 问题描述

文件系统缓存没有足够的内存来缓存经常查询的索引部分。

Elasticsearch 的查询缓存实现了 LRU 置换算法:当缓存变满时,最近最少使用的数据被置换以便为新数据腾出空间。

请求缓存:

https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-cache.html

2.4.2 解决方案

1、为文件系统缓存留出至少 50 %的物理 RAM。

内存越多,可以缓存的越多,尤其是在集群遇到 I / O 问题时。假设堆大小已正确配置,剩下的任何可用于文件系统缓存的剩余物理 RAM 都可以大大加快搜索性能。

堆内存大小配置建议:Min(32 GB,物理机器内存 / 2)。

例如,128 GB 内存服务器为堆提供 30GB 空间,为文件系统缓存(有时称为 OS 缓存)留出剩余内存,假设操作系统缓存最近访问的 4KB 数据块,如果你再一次读取相同的文件,不需要花很长时间去磁盘上读,直接在内存上读来的更快。

2、使用 query 缓存和 request 缓存加快检索速度。

节点级别的 query 缓存默认是开启的。对应配置:

index.queries.cache.enabled  

请求缓存默认是开启的,如果被强制关闭了,可以动态设置开启。

PUT /my_index/_settings  
{ "index.requests.cache.enable": true }  

3、使用 preference 优化高速缓存

可以使用搜索请求首选项 preference 来优化所有这些高速缓存。以便每次将某些搜索请求路由到同一组分片,而不是在可用的不同副本之间交替。

这将更好地利用请求缓存、节点查询缓存和文件系统缓存。

2.5 症状 5:共享硬件资源时的高资源利用率。

操作系统显示始终较高的 CPU 、磁盘、I / O 使用率。

停止第三方应用程序后可以看到性能提升。

2.5.1 问题描述

其他进程(例如 Logstash)和 Elasticsearch 本身之间存在资源(CPU、内存、或磁盘 I / O)争用。

2.5.2 解决方案

给 Elasticsearch 隔离的硬件环境或虚拟环境。

避免在共享硬件上与其他资源密集型应用程序一起运行 Elasticsearch。

2.6 症状 6:聚合 N 多唯一值引起的高内存使用率

查询包含唯一值(例如,ID,用户名,电子邮件地址等)的聚合字段时性能不佳。

在堆内存分析时发现:Java 对象使用 "search", “buckets”, "aggregation" 等术语,消耗大量的堆内存。

2.6.1 问题原因

聚合在高基数 (high-cardinality)字段上运行,需要大量资源来获取许多存储桶。

还可以存在涉及 nested 字段和 / 或 join 字段的嵌套聚合。

注解:high-cardinality 中文解读为高基数不好理解。举个例子:

  • 高基数——列中有很多唯一值(),如主键

  • 低基数——与之相反,如性别列(只有男、女)。

2.6.2 解决方案

1、要提高高基数 term 聚合的性能,推荐阅读:

https://www.elastic.co/cn/blog/improving-the-performance-of-high-cardinality-terms-aggregations-in-elasticsearch



核心:使用 eager_global_ordinals: true 提升性能。

2、有关进一步调整,请查看官网 nested 字段类型和 join 字段类型的使用建议,以更好地提高聚合性能。

3 偶发慢查询解决方案

一般而言,偶尔或间歇性慢查询可以从官网的优化索引、优化检索建议中中受益。

3.1 偶发慢查询关联监控指标

偶发慢查询应与这些监控指标中的一个或多个密切相关:

  • 1)CPU 负载

  • 2)索引吞吐量

  • 3)搜索吞吐量

  • 4)垃圾收集(GC)活动

  • 5)搜索线程池队列大小

线程池查看方法:

1GET /_cat/thread_pool  

3.2 ARS 提升检索吞吐率

Elasticsearch 还有另一个有用的功能,称为自适应副本选择(ARS),它允许协调节点了解数据节点上的负载,并允许它选择最佳的分片副本来执行搜索,从而提高搜索吞吐量、降低延迟。

通过在查询时间内更均匀地分散负载,ARS 可以对偶尔的减速有很大帮助。

在 Elasticsearch 7.0 及更高版本中,默认情况下将启用ARS

4 非偶发慢查询解决方案

对于非偶发慢查询的场景,我们可以尝试逐个删除查询中的功能,并检查查询是否仍然很慢。

4.1 “拆解 DSL”排查慢查询根源

查找最简单查询以重现性能问题有助于隔离和识别问题:

  • 1)没有高亮显示它仍然很慢吗?

  • 2)没有聚合,它仍然很慢吗?

  • 3)如果 size 设置为 0,它仍然很慢吗?
    当 size 设置为 0 时,Elasticsearch 会缓存搜索请求的结果,以便更快地进行搜索

4.2 参考官方搜索优化建议,看是否凑效?

https://www.elastic.co/guide/en/elasticsearch/reference/7.0/tune-for-search-speed.html

4.3 慢查询排除实践

  • 1)启用 "profile:true"。
GET /twitter/_search  
{  
  "profile": true,  
  "query" : {  
    "match" : { "message" : "some number" }  
  }  
}  
  • 2)查看节点的热点线程。

这有助于了解 CPU 时间的使用情况。

GET /_nodes/hot_threads  
  • 3)使用 kibana 可视化 profile 分析工具

https://www.elastic.co/guide/en/kibana/7.0/xpack-profiler.html

5 捕获慢查询、耗费资源查询

5.1 慢查询、耗费资源查询难捕获

在 Elasticsearch 中同时处理不同的请求 / 线程时,很难捕获慢查询、耗费资源查询。

实际 N 多人应用的业务场景,当无法定位耗费资源查询的用户时,情况变得更加复杂,这些查询会降低集群性能(例如,长垃圾收集(GC)周期)或更糟糕的是内存不足(OOM)情况。

在 Elasticsearch 7.0 版中,我们引入了一种新的内存熔断策略,用于在保留内存时测量实际堆内存使用情况。

此新策略可提高节点对资源耗费高查询导致集群过载的弹性支持,并在默认情况下处于打开状态,并可使用新的集群设置:

indices.breaker.total.use_real_memory 进行控制。

内存熔断策略推荐:

https://github.com/elastic/elasticsearch/pull/31767

5.2 dump 堆内存分析

以上方案并未覆盖全部业务场景。借助 dump 文件有助于更好地理解根本原因。

在 JVM OOM 后 Dump 操作实战参考:
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/heap-dump-path.html

Linux 捕获 dump 文件的方法

1ps -ef|egrep 'elasticsearch|logstash' | grep -v grep  

5.3 Elasticsearch 的保护设置

Elasticsearch 具有另一个保护设置(最大桶 search.max_buckets 限制),以保护集群出现 OOM。

当超过桶的数量(在版本 7.0 中默认为 10,000)时(例如,当运行多层聚合时),
该最大桶聚合设置停止执行并且使搜索请求失败。

5.4 断路器设置

为了进一步识别潜在的耗费资源的查询,我们可以设置断路器(indices.breaker.request.limit)。

设置方法:逐步缩放查询范围,从低阈值开始隔离查询并逐渐向上移动阈值以缩小到特定的查询。

断路器设置参考:
https://www.elastic.co/guide/en/elasticsearch/reference/7.0/circuit-breaker.html#request-circuit-breaker

5.5 慢日志分析

可以通过启用 Elasticsearch 中的慢速日志来识别运行缓慢的查询。

Slowlogs 专门用于分片级别,这意味着只应用数据节点

仅协调 Coordinating-only/ 客户 client 节点不具备慢日志分析功能,因为它们不保存数据(索引 / 分片)。

Slowlogs 有助于回答以下问题:

  • 1)查询需要多长时间?

  • 2)查询请求正文的内容是什么?

Slowlogs 输出举例:

[2019-02-11T16:47:39,882][TRACE][index.search.slowlog.query] [2g1yKIZ] [logstash-20190211][4] took[10.4s], took_millis[10459], total_hits[16160], types[], stats[],   
search_type[QUERY_THEN_FETCH], total_shards[10], source[{"size":0,"query":{"bool":{"must":[{"range":{"timestamp":{"from":1549266459837,"to":1549871259837,"include_lower":true,  
"include_upper":true,"format":"epoch_millis","boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}},"_source":{"includes":[],"excludes":[]},"stored_fields":"*","docvalue_fields":  
[{"field":"timestamp","format":"date_time"},{"field":"utc_time","format":"date_time"}],"script_fields":{"hour_of_day":{"script":{"source":"doc['timestamp'].value.getHourOfDay()",  
"lang":"painless"},"ignore_failure":false}},"aggregations":{"maxAgg":{"max":{"field":"bytes"}},"minAgg":{"min":{"field":"bytes"}}}}], id[]],  

Slowlog 日志拆解:
image.png

5.6 日志审计(高阶功能,低版本非付费会员建议跳过)

拥有金牌会员或铂金会员订阅的客户(包括 Elastic 安全功能)可以打开审核日志以捕获有关查询的更多详细信息。(用户可以开始为期 30 天的试用版来测试 Elastic 安全功能。)

注意:6.8 和 7.1 之后,基础安全功能已免费。之上的高版本可以使用该功能。低版本的非会员不建议使用。

审计日志记录有助于回答以下问题:

  • 1)查询是什么时候发生的?

  • 2)谁执行了查询?

  • 3)查询的内容是什么?

我们需要调整审核设置,因为默认设置相当繁琐:

  • 1)启用安全审计日志:

    在 elasticsearch.yml 中设置

    xpack.security.audit.enabled:true。

  • 2)在安全审计输出中启用日志或索引:

    在 elasticsearch.yml 中设置

    xpack.security.audit.outputs:[logfile,index]。

出于故障排除的目的,我们建议选择 logfile 而不是索引,因为审计日志记录的详细程度可能会对集群性能造成不必要的压力。

审计模式可能非常冗长,因此请在完成故障排除后将其关闭。

  • 3)在事件列表中包含 authentication_success 访问权限
    在 elasticsearch.yml 中设置

    xpack.security.audit.logfile.events.include: authentication_success

6、小结

本文翻译自 Elastic 官方博文,添加了自己的实践解读和星球内部讨论总结内容。

在本文中,我们讨论了慢查询的常见原因以及解决方案。我们还讨论了偶发慢查询和非偶发慢查询的解决方案。通常会将慢查询视为更广泛集群性能出问题的典型症状。

null

8 月 24 日北京 Meetup 上 medcl 列举了 10 个原因升级 7.X,如果你还是 6.X、5.X 甚至更低版本,推荐升级。Elasticsearch7.X 高级版本在搜索 qps 上提升很大,相同配置下慢查询也还会有较大改善。

Elasticsearch 性能优化非一朝一夕之功,“认准病根才能根除病痛”。


原文地址:

https://www.elastic.co/cn/blog/advanced-tuning-finding-and-fixing-slow-elasticsearch-queries

官方优化:

https://www.elastic.co/guide/en/elasticsearch/reference/7.0/how-to.html


本文地址:http://www.6aiq.com/article/1581600177845
知乎专栏 点击关注
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出