Elasticsearch:高级调优 - 查找和修复慢速 Elasticsearch 查询

Posted Elastic 中国社区官方博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch:高级调优 - 查找和修复慢速 Elasticsearch 查询相关的知识,希望对你有一定的参考价值。

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

在本博客中,我将讨论导致查询缓慢的原因以及如何在 Elasticsearch 的上下文中识别它们。 本文源于我们一些常见的故障排除方法,这些方法可能需要人们对 Elasticsearch 的工作原理相当熟悉。

Elasticsearch 查询缓慢的常见原因

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

症状:不活动时资源利用率高

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

问题

集群有太多的分片,以至于任何查询执行起来似乎都很慢。一个好的经验法则是确保将每个节点的非冻结(non-frozen)分片数量保持在每 GB 堆(heap)配置 20 以下。

解决方案

减少分片数量、冻结索引和/或添加额外的节点来应对负载。考虑热/温架构(非常适用于基于时间的索引)以及 Elasticsearch 中的翻转(rollover)/收缩(shrink)功能,以有效管理分片数量。任何部署的良好开端都是执行适当的容量规划,以帮助确定每个搜索用例的最佳分片数量。

症状:高线程池 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

问题

查询针对的分片过多,超过了集群中的 core 的数量。 这会在搜索线程池中创建排队任务,从而导致搜索拒绝。 另一个常见原因是磁盘 I/O 缓慢导致搜索排队或在某些情况下 CPU 完全饱和。

解决方案

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

症状:高 CPU 和索引延迟


当集群不堪重负时,指标相关性显示高 CPU 利用率和摄入数据延迟。

问题

集群在繁重地摄入数据,这会影响搜索性能。

解决方案

index.refresh_interval(文档被编入索引和它变为可见之间的时间量)增加到 30s 之类的值通常有助于提高索引性能。 实际的数值可能会有所不同,因此测试是关键。 这样确保每个分片不必过于频繁在每一秒创建一个新的 Segment(默认情况)。

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

症状:更多副本分片导致延迟增加

在增加副本分片数量(例如,从 1 到 2)后,可以观察到查询延迟。如果存在更多数据,则缓存的数据将更快地被逐出,从而导致操作系统 page faults 的增加。

问题

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

解决方案

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

例如,一台 128GB RAM 的服务器有 30GB 留作堆,剩余内存留作文件系统缓存(有时称为 OS 缓存),这是操作系统缓存最近访问的 4KB 数据块的一种方式,以便如果你读取相同的文件一次又一次,大多数时候你不需要去磁盘,读取请求将直接从内存中提供。

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

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

操作系统始终显示高 CPU/磁盘 I/O 使用率。 停止第三方应用程序后可以看到性能提升。

问题

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

解决方案

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

症状:高堆使用率聚合了高度独特的字段

查询包含高度唯一值(例如 ID、用户名、电子邮件地址等)的聚合字段时性能不佳。 在 heap dump analysis 期间,你应该会看到带有 “search”、“buckets”、“aggregation” 等术语的 Java 对象,它们占用了大量的堆。

问题

聚合在高基数字段上运行,需要大量资源来获取许多存储桶。 还可以存在涉及嵌套(nested)字段和/或连接(join)字段的嵌套聚合。

解决方案

要提高高基数术语聚合的性能,请阅读我的咨询团队同事撰写的这篇博文:https://www.elastic.co/blog/improving-the-performance-of-high-cardinality-terms -aggregations-in-elasticsearch

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

偶尔的慢查询

一般来说,偶尔或间歇性的慢查询可以从索引调优/搜索调优建议的中受益。 偶尔的慢查询应该与这些监控指标中的一个或多个密切相关:

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

Elasticsearch 有另一个有用的功能,称为自适应副本选择 (ARS),它允许 coordinating node了解数据节点上的负载,并允许它选择最佳分片副本来执行搜索,从而提高搜索吞吐量和延迟。 通过在查询期间更均匀地分布负载,ARS 可以为偶尔的减速提供很大帮助。 在 Elasticsearch 7.0 及更高版本中,ARS 将默认开启。

持续的慢查询

对于持续的慢查询,我们可以尝试将查询中的特征一一去除,并检查查询是否仍然很慢。查找重现性能问题的最简单查询有助于隔离和识别问题:

  • 不高亮(highligting)还是慢吗?
  • 没有聚合它仍然很慢吗?
  • 如果 size 设置为 0,它仍然很慢吗? (当 size 设置为0时,Elasticsearch 会缓存搜索请求的结果,使搜索速度更快)

它可以从一些 “搜索优化” 建议中受益吗?

在故障排除期间,通常有助于:

  • profile 打开的情况下获取查询响应。
  • 使用在 while(true) 循环中运行的查询收集节点热线程输出。这将有助于了解 CPU 时间花在哪里。
  • 使用这个 user-friendlier version 的配置文件 API 来配置查询。

如果查询来自 Kibana 可视化,请使用 visualziation spy panel(Kibana 6.3 及更早版本)或 dashboard inspect panel(Kibana 6.4 及更高版本)查看并导出实际查询请求并将其导入配置文件 API 以进行进一步分析.

捕获缓慢或昂贵的查询

有时,在分布式应用程序(如 Elasticsearch)中并发处理不同请求/线程时,可能很难捕获缓慢或昂贵的查询。当无法控制运行昂贵查询的用户会降低集群性能(例如,长垃圾收集 (GC) 周期)或更糟糕的内存不足 (OOM) 情况时,事情会变得更加复杂。

在 Elasticsearch 7.0 版中,我们引入了一种新的断路(circuit-breaking)策略,可以在内存预留时测量实际堆内存使用情况。这种新策略提高了节点对导致集群过载的昂贵查询的可靠性,默认情况下是打开的,并且可以通过新的集群设置 index.breaker.total.use_real_memory 进行控制。

然而,我们应该注意到,这些都是最好的努力;对于这些未涵盖的场景,在 OOM 崩溃后收集堆转储或从正在运行的 JVM 收集堆转储以更好地了解根本原因会很有用。

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

为了进一步识别潜在的昂贵查询,我们可以设置断路器设置(indices.breaker.request.limit),从低阈值开始以隔离查询,并逐渐提高阈值以缩小到特定的查询。

慢日志

还可以通过在 Elasticsearch 中打开慢日志来识别运行缓慢的查询。 Slowlogs 专门用于分片级别,这意味着仅适用于数据节点。Coordinating-only/cient 节点被排除在外,因为它们不保存数据(索引/分片)。

慢日志有助于回答以下问题:

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

示例慢日志输出:

[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[]],

慢日志消息分解:

 

审计日志

拥有包括 Elastic 安全功能的 Gold 或 Platinum 订阅的客户可以打开审计日志以捕获有关查询的更多详细信息。 (用户可以开始为期 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。当 xpack.security.audit.enabled 设置为 true 时,7.0 版不再接受此设置并默认为 json 输出 (<clustername>_audit.json)。
  • 出于故障排除的目的,我们建议选择 logfile 而不是 index,因为审记日志的冗长可能会给集群性能带来不必要的压力,其中安全索引增长超出其预期大小。
  • 审记模式可能非常冗长,因此一旦完成故障排除,请考虑将其关闭。

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 Visualization/Dashboard 发出的查询)
  • 查询正文:从 “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:高级调优 - 查找和修复慢速 Elasticsearch 查询的主要内容,如果未能解决你的问题,请参考以下文章

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

ElasticsearcheBay上的Elasticsearch性能调优实践

Elasticsearch调优篇 06 - Elasticsearch 业务层面最全优化

数据库的索引原理

30 个 ElasticSearch 调优知识点,都给你整理好了!

ElasticSearch还能性能调优,涨见识涨见识了!!!