高级调优:查找并修复 Elasticsearch 慢查询

原文链接

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

Elasticsearch 是一个非常灵活且功能丰富的应用程序,它提供了许多不同的数据查询方法。但是,您是否遇到过查询速度低于预期的情况?对于像 Elasticsearch 这样的分布式系统,可能会有各种影响查询性能的因素,包括负载平衡器设置、网络延迟(带宽、网卡/驱动程序)等在内的各种外部因素。

在本篇博文中,我将讨论导致慢查询的原因,以及如何在 Elasticsearch 的上下文中识别它们。本文主要源于一些常见的排查方法展开论述,因此,可能需要您对 ElasticSearch 的工作原理非常熟悉。

Elasticsearch 慢查询的常见原因

在我们研究一些更棘手的情况之前,让我们先从一些导致慢查询的最常见原因及其解决方案入手。

症状:非活动状态下资源利用率也很高

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

问题

集群中的分片太多,以至于任何查询的执行速度看起来都很慢。一个好的经验法则是:确保对于每个节点上已配置的每个 GB 堆,将非冻结的分片数量保持在 20 以下。

解决方案

减少分片计数,实施冻结索引和/或添加附加节点来实现负载平衡。考虑结合使用 Elasticsearch 中的热/温架构(非常适合基于时间的索引)以及滚动/收缩功能,以高效管理分片计数。要想顺利完成部署,最好先执行适当的容量计划,以帮助确定适合每个搜索用例的最佳分片数。

症状:线程池存在大量的“rejected”(拒绝)

根据上次集群重新启动的累积值看,搜索线程池显示“rejected”计数在持续增加。

GET /_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
问题

查询面向的分片太多,超过了集群中的核心数。这会在搜索线程池中造成排队任务,从而导致搜索拒绝。另一个常见原因是磁盘 I/O 速度慢,导致搜索排队或在某些情况下 CPU 完全饱和。 

解决方案

创建索引时采用 1 个主分片:1 个副本分片 (1P:1R) 模型。使用索引模板是一个在创建索引时部署此设置的好方法。(Elasticsearch 7.0 或更高版本将默认 1P:1R)。Elasticsearch 5.1 或更高版本支持搜索任务取消,这对于取消任务管理 API 中出现的慢查询任务非常有用。若要改进磁盘 I/O,请查看存储建议,并确保使用推荐的硬件以获得最佳性能。

症状:高 CPU 使用率和索引延迟

指标相关性表明,当集群不堪重负时,CPU 使用率和索引延迟都会很高。

问题

集群索引量大会影响搜索性能。

解决方案

将 index.refresh_ interval 的值(从文档被索引到其变为可见的时间间隔)增加到 30 秒,通常有助于提高索引性能。实际业务情境中可能会有所不同,因此测试是关键。这可以确保分片不必因为每 1 秒默认创建一个新分段而造成工作负载增大。

对于索引量大的用例,请查看索引调优建议,以优化索引和搜索性能。

症状:副本分片增加后延迟增大

在副本分片计数增加(例如,从 1 增加到 2)后,可以观察到查询延迟。如果存在较多的数据,那么缓存的数据将很快被逐出,从而导致操作系统页面错误增加。

问题

文件系统缓存没有足够的内存来缓存索引中经常查询的部分。Elasticsearch 的 查询缓存实现了 LRU 逐出策略:当缓存变满时,将逐出最近使用最少的数据,以便为新数据让路。

解决方案

为文件系统缓存留出至少 50% 的物理 RAM。内存越多,缓存的空间就越大,尤其是当集群遇到 I/O 问题时。 假设堆大小已正确配置,任何剩余的可用于文件系统缓存的物理 RAM 都会大大提高搜索性能。

例如,128GB 的 RAM 服务器留出 30GB 的内存用于堆,剩余内存用于文件系统缓存(有时称为 OS 缓存),假设操作系统缓存了最近访问的 4KB 数据块,这样,如果您一次又一次地读取相同的文件,那么您不必花很长时间到磁盘上读,而是会从内存中直接读取。

除了文件系统缓存,Elasticsearch 还使用查询缓存和请求缓存来提高搜索速度。 所有这些缓存都可以使用搜索请求首选项进行优化,以便每次都将某些搜索请求路由到同一组分片,而不是在不同的可用副本之间进行交替。这将更好地利用请求缓存、节点查询缓存和文件系统缓存。

症状:共享资源时利用率高

操作系统显示出持续的高 CPU/磁盘 I/O 利用率。停止第三方应用程序后,可以看到性能会提高。

问题

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

解决方案

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

症状:聚合多个高度唯一字段时堆利用率高

当查询包含高度唯一值(如 ID、用户名、电子邮件地址等)的聚合字段时,性能不佳。在堆转储分析期间发现:使用“search”、“buckets”、“aggregation”等术语的 Java 对象消耗了大量的堆空间。

问题

聚合在高基数字段上运行,需要大量资源来提取许多存储桶。 此外,还可能存在涉及嵌套字段和/或联接字段的嵌套聚合。

解决方案

若要改进高基数术语聚合的性能,请阅读我的咨询团队同事的这篇博文:Improving the performance of high-cardinality terms aggregations in Elasticsearch | Elastic Blog

有关进一步的调整,请查看我们关于嵌套字段和联接字段的建议,以更好地提高聚合性能。

偶发慢查询

一般来说,可以采用一些索引调优/搜索调优建议来解决偶尔或间歇出现的慢查询问题。偶发的慢查询应该与这些监测指标中的一个或多个密切相关:

  • CPU 负载
  • 索引吞吐量
  • 搜索吞吐量
  • 垃圾收集 (GC) 活动
  • 搜索线程池队列大小

Elasticsearch 还有另一个有用的功能,称为自适应副本选择 (ARS),它允许协调节点了解数据节点上的负载,并允许它选择最佳分片副本来执行搜索,从而提高搜索吞吐量并降低延迟。通过在查询期间更均匀地分配负载,ARS 对于偶发的减速有很大帮助。在 Elasticsearch 7.0 及更高版本中,默认情况下将启用 ARS。

持续性慢查询

对于持续性的慢查询,我们可以尝试逐个删除查询中的功能,并检查查询是否仍然慢。查找最简单查询以再现性能问题,有助于隔离和识别问题:

  • 没有高亮显示,它是否仍然很慢?
  • 没有聚合,它是否仍然很慢?
  • 如果大小设为 0,它是否仍然慢?(当大小设为 0 时,Elasticsearch 会缓存搜索请求的结果,以便更快地进行搜索)

一些“搜索调优”的建议是否有用?

在故障排除期间,执行以下操作通常很有用:

  • 在启用配置文件的情况下,获取查询响应。
  • 通过在 while(true) 循环中运行的查询,收集节点的热点线程输出。这有助于了解 CPU 时间的使用情况。
  • 使用这个用户友好版本的配置文件 API 来配置查询。

如果查询来自 Kibana 可视化,则使用可视化侦查面板(Kibana 版本 6.3 及更高版本)或仪表板检查面板(Kibana 版本 6.4 及更高版本)来查看并导出实际的查询请求,并将其导入到配置文件 API 中以做进一步分析。

捕获慢查询或耗费资源的查询

有时,在像 Elasticsearch 这样的分布式应用程序中,当同时处理不同的请求/线程时,很难捕获慢查询或耗费资源的查询。如果对运行耗费资源查询的用户不加以控制,情况就会变得愈加复杂,这些查询会降低集群性能(例如,较长的垃圾收集 (GC) 周期),甚至更糟糕地会出现内存不足 (OOM) 的情况。

在 Elasticsearch version 7.0 中,我们引入了一种新的内容熔断策略,用于在保留内存时测量实际堆内存的使用情况。这个新策略可提高节点对耗费资源的查询导致集群过载的弹性支持,并且在默认情况下处于启用状态,并可使用新的集群设置 indices.breaker.total.use_real_memory 对其进行控制。

但是,我们应该注意,这些都是尽力而为;对于以上内容未涉及的情况,最好在 OOM 崩溃后收集堆转储,或从运行的 JVM 中收集堆转储,以更好地了解根本原因。

Elasticsearch 还有另一个保护设置(最大存储桶软限制),用于防止集群出现 OOM。当超过存储桶数量(在 7.0 版中默认为 10,000)时(例如,当运行多层聚合时),此最大存储桶聚合设置将停止执行并使搜索请求失败。

为了进一步识别潜在的耗费资源查询,我们可以设置断路器设置 (indices.breaker.request.limit),从一个低阈值开始隔离查询,并逐渐向上移动阈值,以将范围缩小到特定的查询。

slowlogs

此外,还可以通过在 Elasticsearch 中启用 slowlogs 来识别运行缓慢的查询。slowlogs 专门用于分片级别,这意味着仅适用于数据节点。仅协调/客户端节点不具备慢日志分析功能,因为它们不保存数据(索引/分片)。

slowlogs 有助于回答如下问题:

  • 查询花费了多长时间?
  • 查询请求正文的内容是什么?

slowlog 输出示例:

[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 消息拆解:

拆分项 说明
[2019-02-11T16:47:39,882] 查询日期
[TRACE] 日志级别
[index.search.slowlog.query] 搜索 slowlog 的查询阶段
[2g1yKIZ] 节点名称
[logstash-20190211] 索引名称
[4] 查询执行的分片序号
took[10.4s] 在分片 [4] 上花费的处理时间。注意:在查看 slowlog 时,我们需要避免将来自不同分片的所有时间相加,因为每个分片可能并行执行。
took_millis[10459] 花费的时间(毫秒)
total_hits[16160] 总命中数
search_type[QUERY_THEN_FETCH] 搜索类型
total_shards[10] 索引的总分片数
source[] 执行的查询正文

审计日志

黄金级或白金级订阅的客户(包括 Elastic 安全功能),可以启用审计日志来捕获有关查询的更多详细信息。(用户可以开始为期 30 天的试用来测试 Elastic 安全功能。) 审计日志有助于回答以下问题:

  • 查询是何时发生的?
  • 谁执行了查询?
  • 查询的内容是什么?

由于默认的审计设置相当繁琐,我们需要调整设置:

  1. 启用安全审计日志: 在 elasticsearch.yml 中设置 xpack.security.audit.enabled: true
  2. 在安全审计输出中启用日志或索引: 在 elasticsearch.yml 中设置 xpack.security.audit.outputs:[logfile, index]

    备注:
    • xpack.security.audit.outputs 设置仅适用于版本 6.0-6.2 和 5.x。版本 7.0 不再接受此设置,并且当 xpack.security.audit.enabled 设置为 true 时,默认为 json 输出 (_audit.json)。
    • 出于排除故障的目的,我们建议选择 logfile 而不是索引,因为审计日志的冗长可能会对安全索引超出其预期大小的集群性能造成不必要的压力。
    • 审计模式可能非常冗长,因此,请在完成故障排除后将其关闭。
  3. 在事件列表中包含 authentication_success 访问: 在 elasticsearch.yml 中设置 xpack.security.audit.logfile.events.include: authentication_success

    备注:
    • 默认事件中不包括此设置。此设置会覆盖默认设置。
    • 如果需要再添加一个事件(而不是替换),请先写入现有的默认事件列表,然后在最后一个条目后添加上述设置。
    • 在审计事件中输出请求正文: 在 elasticsearch.yml 中设置 xpack.security.audit.logfile.events.emit_request_body: true。

借助这些设置,您就可以像下面这样监测用户查询。

  • 用户:louisong
  • 查询时间: 2019-05-01T19:26:53,554 (UTC)
  • 查询端点:_msearch(通常表示它是从 Kibana 可视化/仪表板发出的查询)
  • 查询主体:从以下日志中的 "request.body": 开始:
     
    {"@timestamp":"2019-05-01T19:26:53,554", "node.id":"Z1z_64sIRcy4dW2eqyqzMw", "event.type":"rest", "event.action":"authentication_success", "user.name":"louisong", "origin.type":"rest", "origin.address":"127.0.0.1:51426", "realm":"default_native", "url.path":"/_msearch", "url.query":"rest_total_hits_as_int=true&ignore_throttled=true", "request.method":"POST", "request.body":"{\"index\":\"*\",\"ignore_unavailable\":true,\"preference\":1556709820160}\n{\"aggs\":{\"2\":{\"terms\":{\"field\":\"actions\",\"size\":5,\"order\":{\"_count\":\"desc\"},\"missing\":\"__missing__\"}}},\"size\":0,\"_source\":{\"excludes\":[]},\"stored_fields\":[\"*\"],\"script_fields\":{},\"docvalue_fields\":[{\"field\":\"access_token.user_token.expiration_time\",\"format\":\"date_time\"},{\"field\":\"canvas-workpad.@created\",\"format\":\"date_time\"},{\"field\":\"canvas-workpad.@timestamp\",\"format\":\"date_time\"},{\"field\":\"creation_time\",\"format\":\"date_time\"},{\"field\":\"expiration_time\",\"format\":\"date_time\"},{\"field\":\"maps-telemetry.timeCaptured\",\"format\":\"date_time\"},{\"field\":\"task.runAt\",\"format\":\"date_time\"},{\"field\":\"task.scheduledAt\",\"format\":\"date_time\"},{\"field\":\"updated_at\",\"format\":\"date_time\"},{\"field\":\"url.accessDate\",\"format\":\"date_time\"},{\"field\":\"url.createDate\",\"format\":\"date_time\"}],\"query\":{\"bool\":{\"must\":[{\"range\":{\"canvas-workpad.@timestamp\":{\"format\":\"strict_date_optional_time\",\"gte\":\"2019-05-01T11:11:53.498Z\",\"lte\":\"2019-05-01T11:26:53.498Z\"}}}],\"filter\":[{\"match_all\":{}},{\"match_all\":{}}],\"should\":[],\"must_not\":[]}},\"timeout\":\"30000ms\"}\n", "request.id":"qrktsPxyST2nVh29GG7tog"}
        

结论

在本文中,我们讨论了慢查询的常见原因以及相应的解决方案。我们还讨论了识别持续性慢查询和偶发慢查询的不同方法。通常会将慢查询视为需要解决的更广泛集群性能问题的典型症状。

在 Elasticsearch,我们一直在努力改进查询时间,致力于在未来版本中提高查询性能。希望本文对处理慢查询的问题有所帮助。请在 Elasticsearch 讨论论坛上自由提问,参与进一步的讨论。祝您搜索愉快!

你可能感兴趣的:(Elasticsearch,ES搜索优化,运维,elasticsearch,es,慢查询调优)