修复常见集群问题

edit

修复常见集群问题

edit

本指南描述了如何修复Elasticsearch集群的常见错误和问题。

Watermark errors
修复当数据节点磁盘空间严重不足并达到洪水位磁盘使用水印时发生的水印错误。
Circuit breaker errors
Elasticsearch 使用断路器来防止节点耗尽 JVM 堆内存。 如果 Elasticsearch 估计某个操作会超过断路器限制,它会停止该操作并返回一个错误。
High CPU usage
高CPU使用率的最常见原因及其解决方案。
High JVM memory pressure
高JVM内存使用率可能会降低集群性能并触发断路器错误。
Red or yellow cluster status
红色或黄色集群状态表示一个或多个分片缺失或未分配。这些未分配的分片增加了数据丢失的风险,并可能降低集群性能。
Rejected requests
当 Elasticsearch 拒绝请求时,它会停止操作并返回一个带有 429 响应码的错误。
Task queue backlog
积压的任务队列可能会阻止任务完成,并将集群置于不健康的状态。
Diagnose unassigned shards
分片可能未分配的原因有多种,从配置错误的分配设置到磁盘空间不足。
Troubleshooting an unstable cluster
一个节点意外离开的集群是不稳定的,可能会引发多个问题。
Mapping explosion
一个索引或索引模式在其中爆炸的集群,具有高数量的映射字段,导致Elasticsearch和Kibana的性能查找问题。
Hot spotting
热点问题可能会在Elasticsearch中发生,当资源利用率在节点之间分布不均匀时。

修复水印错误

edit

当一个数据节点磁盘空间严重不足并达到 洪水阶段磁盘使用水位线时,会记录以下错误:错误:磁盘使用量超过洪水阶段水位线,索引已被设置为只读-允许删除块

为了防止磁盘写满,当一个节点达到这个水位线时,Elasticsearch 阻止对任何在该节点上有分片的索引进行写操作。如果该阻止操作影响到相关的系统索引,Kibana和其他Elastic Stack功能可能会变得不可用。例如,这可能会导致Kibana的Kibana Server is not Ready yet 错误信息

当受影响节点的磁盘使用率低于高磁盘水位线时,Elasticsearch 将自动移除写入块。为了实现这一点,Elasticsearch 尝试将受影响节点的一些分片重新平衡到同一数据层中的其他节点。

监控重新平衡

edit

要验证分片是否正在从受影响的节点移动,直到其低于高水位线,请使用cat shards APIcat recovery API

GET _cat/shards?v=true

GET _cat/recovery?v=true&active_only=true

如果分片仍然保留在节点上,使其保持在高水位线附近,请使用 集群分配解释API 来获取其分配状态的解释。

GET _cluster/allocation/explain
{
  "index": "my-index",
  "shard": 0,
  "primary": false
}

临时缓解

edit

要立即恢复写操作,您可以暂时增加磁盘水位线并移除写块

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "90%",
    "cluster.routing.allocation.disk.watermark.low.max_headroom": "100GB",
    "cluster.routing.allocation.disk.watermark.high": "95%",
    "cluster.routing.allocation.disk.watermark.high.max_headroom": "20GB",
    "cluster.routing.allocation.disk.watermark.flood_stage": "97%",
    "cluster.routing.allocation.disk.watermark.flood_stage.max_headroom": "5GB",
    "cluster.routing.allocation.disk.watermark.flood_stage.frozen": "97%",
    "cluster.routing.allocation.disk.watermark.flood_stage.frozen.max_headroom": "5GB"
  }
}

PUT */_settings?expand_wildcards=all
{
  "index.blocks.read_only_allow_delete": null
}

当长期解决方案到位时,重置或重新配置磁盘水位线:

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": null,
    "cluster.routing.allocation.disk.watermark.low.max_headroom": null,
    "cluster.routing.allocation.disk.watermark.high": null,
    "cluster.routing.allocation.disk.watermark.high.max_headroom": null,
    "cluster.routing.allocation.disk.watermark.flood_stage": null,
    "cluster.routing.allocation.disk.watermark.flood_stage.max_headroom": null,
    "cluster.routing.allocation.disk.watermark.flood_stage.frozen": null,
    "cluster.routing.allocation.disk.watermark.flood_stage.frozen.max_headroom": null
  }
}

解决

edit

作为长期解决方案,我们建议您选择最适合您使用场景的以下任一方案:

断路器错误

edit

Elasticsearch 使用 断路器 来防止节点耗尽 JVM 堆内存。如果 Elasticsearch 估计某个操作会超过断路器,它会停止该操作并返回一个错误。

默认情况下,父断路器在JVM内存使用率达到95%时触发。为了防止错误,我们建议采取措施减少内存压力,如果使用率持续超过85%。

诊断断路器错误

edit

错误信息

如果请求触发了断路器,Elasticsearch 将返回一个带有 429 HTTP 状态码的错误。

{
  'error': {
    'type': 'circuit_breaking_exception',
    'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
    'bytes_wanted': 123848638,
    'bytes_limit': 123273216,
    'durability': 'TRANSIENT'
  },
  'status': 429
}

Elasticsearch 还会将断路器错误写入 elasticsearch.log。这在自动化过程(如分配)触发断路器时非常有用。

Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]

检查JVM内存使用情况

如果您已启用堆栈监控,您可以在 Kibana 中查看 JVM 内存使用情况。在主菜单中,点击 堆栈监控。在堆栈监控 概览 页面,点击 节点JVM 堆 列列出了每个节点的当前内存使用情况。

您还可以使用cat nodes API来获取每个节点的当前heap.percent

GET _cat/nodes?v=true&h=name,node*,heap*

要获取每个断路器的JVM内存使用情况,请使用节点统计信息API

GET _nodes/stats/breaker

防止断路器错误

edit

减少JVM内存压力

高JVM内存压力通常会导致断路器错误。请参阅 高JVM内存压力

避免在text字段上使用fielddata

对于高基数的text字段,fielddata可能会使用大量的JVM内存。为了避免这种情况,Elasticsearch默认禁用了text字段上的fielddata。如果你已经启用了fielddata并触发了fielddata断路器,请考虑禁用它并改用keyword字段。请参阅fielddata映射参数

清除字段数据缓存

如果你触发了fielddata断路器并且无法禁用fielddata, 使用清除缓存API来清除fielddata缓存。 这可能会中断任何使用fielddata的正在进行的搜索。

POST _cache/clear?fielddata=true

高CPU使用率

edit

Elasticsearch 使用 线程池 来管理并发操作的 CPU 资源。高 CPU 使用率通常意味着一个或多个线程池运行不足。

如果线程池耗尽,Elasticsearch将拒绝请求 与线程池相关的请求。例如,如果search线程池耗尽,Elasticsearch将拒绝搜索请求,直到有更多线程可用。

如果一个数据层,以及因此分配给该层的节点,比其他层接收到更多的流量,您可能会遇到高CPU使用率。这种资源利用的不平衡也被称为热点问题

诊断高CPU使用率

edit

检查CPU使用情况

您可以使用cat nodes API检查每个节点的CPU使用情况:

GET _cat/nodes?v=true&s=cpu:desc

响应的 cpu 列包含当前 CPU 使用率的百分比。 name 列包含节点的名称。CPU 使用率升高但短暂是正常的。然而,如果 CPU 使用率长时间升高,则应进行调查。

要跟踪CPU使用情况随时间的变化,我们建议启用监控:

  • (推荐) 启用 日志和指标。当日志和指标启用时,监控信息将在 Kibana 的 Stack Monitoring 页面中可见。

    您还可以启用CPU使用率阈值警报,通过电子邮件接收潜在问题的通知。

  • 从您的部署菜单中,查看性能页面。在此页面上,您可以查看两个关键指标:

    • CPU 使用率: 您的部署的 CPU 使用率,以百分比表示。
    • CPU 信用: 您剩余的 CPU 信用,以 CPU 时间的秒数衡量。

Elasticsearch Service 为每个部署提供 CPU 信用,以便在需要时为较小的集群提供性能提升。高 CPU 使用率可能会耗尽这些信用,这可能导致 性能下降集群响应时间增加

检查热点线程

如果一个节点的CPU使用率很高,请使用nodes hot threads API来检查节点上运行的资源密集型线程。

GET _nodes/hot_threads

此API以纯文本形式返回任何热点线程的详细信息。高CPU使用率通常与长时间运行的任务或任务积压相关。

降低 CPU 使用率

edit

以下提示概述了高CPU使用率的最常见原因及其解决方案。

扩展您的集群

沉重的索引和搜索负载可能会耗尽较小的线程池。为了更好地处理繁重的工作负载,请向集群中添加更多节点或升级现有节点以增加容量。

分散批量请求

虽然比单独的请求更高效,但大型的批量索引多搜索请求仍然需要CPU资源。如果可能,提交较小的请求并在它们之间留出更多时间。

取消长时间运行的搜索

长时间运行的搜索可能会阻塞search线程池中的线程。要检查这些搜索,请使用任务管理 API

GET _tasks?actions=*search&detailed

响应的 description 包含了搜索请求及其查询。 running_time_in_nanos 显示了搜索已经运行了多长时间。

{
  "nodes" : {
    "oTUltX4IQMOUUVeiohTt8A" : {
      "name" : "my-node",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "tasks" : {
        "oTUltX4IQMOUUVeiohTt8A:464" : {
          "node" : "oTUltX4IQMOUUVeiohTt8A",
          "id" : 464,
          "type" : "transport",
          "action" : "indices:data/read/search",
          "description" : "indices[my-index], search_type[QUERY_THEN_FETCH], source[{\"query\":...}]",
          "start_time_in_millis" : 4081771730000,
          "running_time_in_nanos" : 13991383,
          "cancellable" : true
        }
      }
    }
  }
}

要取消搜索并释放资源,请使用API的_cancel端点。

POST _tasks/oTUltX4IQMOUUVeiohTt8A:464/_cancel

有关如何跟踪和避免资源密集型搜索的更多提示,请参阅 避免昂贵的搜索

高JVM内存压力

edit

高JVM内存使用率可能会降低集群性能并触发 断路器错误。为了防止这种情况,我们建议 采取措施减少内存压力,如果节点的JVM内存使用率持续超过85%。

诊断高JVM内存压力

edit

检查JVM内存压力

从您的部署菜单中,点击Elasticsearch。在实例下,每个实例显示一个JVM内存压力指示器。当JVM内存压力达到75%时,指示器变为红色。

您还可以使用节点统计信息 API来计算每个节点的当前 JVM 内存压力。

GET _nodes/stats?filter_path=nodes.*.jvm.mem.pools.old

使用响应来计算内存压力如下:

JVM 内存压力 = used_in_bytes / max_in_bytes

检查垃圾回收日志

随着内存使用量的增加,垃圾回收变得更加频繁且耗时更长。您可以在elasticsearch.log中跟踪垃圾回收事件的频率和持续时间。例如,以下事件表明Elasticsearch在过去40秒内花费了超过50%(21秒)的时间进行垃圾回收。

[timestamp_short_interval_from_last][INFO ][o.e.m.j.JvmGcMonitorService] [node_id] [gc][number] overhead, spent [21s] collecting in the last [40s]

捕获JVM堆转储

要确定JVM内存压力高的确切原因,请在JVM内存使用率高时捕获其堆转储,并捕获覆盖同一时间段的垃圾收集器日志

减少JVM内存压力

edit

本节包含一些减少JVM内存压力的常见建议。

减少分片数量

每个分片都会使用内存。在大多数情况下,少量大分片比许多小分片使用的资源更少。有关减少分片数量的建议,请参阅调整分片大小

避免昂贵的搜索

昂贵的搜索可能会消耗大量内存。为了更好地跟踪集群中的昂贵搜索,请启用慢日志

昂贵的搜索可能会有一个较大的size参数, 使用具有大量桶的聚合,或者包含 昂贵的查询。为了防止昂贵的搜索,请考虑以下设置更改:

PUT _settings
{
  "index.max_result_window": 5000
}

PUT _cluster/settings
{
  "persistent": {
    "search.max_buckets": 20000,
    "search.allow_expensive_queries": false
  }
}

防止映射爆炸

定义过多的字段或过度嵌套字段可能会导致使用大量内存的映射爆炸。 为了防止映射爆炸,请使用映射限制设置来限制字段映射的数量。

分散批量请求

虽然比单独的请求更高效,但大型的批量索引多搜索请求仍然可能产生高JVM内存压力。如果可能,提交较小的请求并在它们之间留出更多时间。

升级节点内存

大量的索引和搜索负载可能会导致高JVM内存压力。为了更好地处理繁重的工作负载,请升级您的节点以增加其内存容量。

红色或黄色集群健康状态

edit

红色或黄色集群健康状态表示一个或多个分片未分配给节点。

  • 红色健康状态: 集群中有一些未分配的主分片,这意味着某些操作(如搜索和索引)可能会失败。
  • 黄色健康状态: 集群中没有未分配的主分片,但有一些未分配的副本分片。这增加了数据丢失的风险,并可能降低集群性能。

当您的集群处于红色或黄色健康状态时,它将继续尽可能地处理搜索和索引,但在集群恢复到绿色健康状态之前,可能会延迟某些管理和清理活动。例如,某些ILM操作要求它们所操作的索引处于绿色健康状态。

在许多情况下,您的集群将自动恢复到绿色健康状态。如果集群没有自动恢复,那么您必须手动解决剩余的问题,以便管理和清理活动可以继续进行。

诊断您的集群状态

edit

检查您的集群状态

使用集群健康状态 API

GET _cluster/health?filter_path=status,*_shards

一个健康的集群有一个绿色的状态和零个未分配的分片。黄色状态意味着只有副本未分配。红色状态意味着一个或多个主分片未分配。

查看未分配的分片

要查看未分配的分片,请使用cat shards API

GET _cat/shards?v=true&h=index,shard,prirep,state,node,unassigned.reason&s=state

未分配的分片具有stateUNASSIGNEDprirep值对于主分片是p,对于副本是r

要了解为什么未分配的分片没有被分配以及您必须采取什么行动才能让 Elasticsearch 分配它,请使用 集群分配解释 API

GET _cluster/allocation/explain?filter_path=index,node_allocation_decisions.node_name,node_allocation_decisions.deciders.*
{
  "index": "my-index",
  "shard": 0,
  "primary": false
}

修复红色或黄色集群状态

edit

分片可能由于多种原因变为未分配状态。以下提示概述了最常见的原因及其解决方案。

重新启用分片分配
edit

通常在重启或其他集群维护期间禁用分配。如果您忘记在之后重新启用分配,Elasticsearch将无法分配分片。要重新启用分配,请重置cluster.routing.allocation.enable集群设置。

PUT _cluster/settings
{
  "persistent" : {
    "cluster.routing.allocation.enable" : null
  }
}
恢复丢失的节点
edit

当数据节点离开集群时,分片通常会变为未分配状态。这可能由多种原因引起,从连接问题到硬件故障。在您解决问题并恢复节点后,它将重新加入集群。Elasticsearch 随后会自动分配任何未分配的分片。

为了避免在临时问题上浪费资源,Elasticsearch 默认会 延迟一分钟进行分配。如果你已经恢复了一个节点并且不想等待延迟期,你可以调用 集群重新路由 API 而不带参数来启动分配过程。该过程会在后台异步运行。

POST _cluster/reroute
修复分配设置
edit

配置错误的分配设置可能导致主分片未分配。 这些设置包括:

要查看您的分配设置,请使用获取索引设置集群获取设置 API。

GET my-index/_settings?flat_settings=true&include_defaults=true

GET _cluster/settings?flat_settings=true&include_defaults=true

您可以使用更新索引设置集群更新设置 API 来更改设置。

分配或减少副本
edit

为了防止硬件故障,Elasticsearch 不会将副本分配到与其主分片相同的节点上。如果没有其他数据节点可以托管副本,它将保持未分配状态。要解决这个问题,您可以:

  • 向同一层级添加一个数据节点以托管副本。
  • 更改index.number_of_replicas索引设置以减少每个主分片的副本数量。我们建议每个主分片至少保留一个副本。
PUT _settings
{
  "index.number_of_replicas": 1
}
释放或增加磁盘空间
edit

Elasticsearch 使用 低磁盘水位线 来确保数据节点有足够的磁盘空间用于接收新的分片。默认情况下,Elasticsearch 不会将分片分配给使用超过 85% 磁盘空间的节点。

要检查节点的当前磁盘空间,请使用cat allocation API

GET _cat/allocation?v=true&h=node,shards,disk.*

如果您的节点磁盘空间不足,您有几个选择:

  • 升级你的节点以增加磁盘空间。
  • 删除不需要的索引以释放空间。如果你使用 ILM,你可以更新你的生命周期策略以使用 可搜索快照 或添加删除阶段。如果你不再需要搜索数据,你可以使用 快照 将其存储在集群外。
  • 如果你不再向索引写入数据,使用强制合并 API或 ILM 的强制合并操作将其段合并为更大的段。

    POST my-index/_forcemerge
  • 如果索引是只读的,请使用收缩索引 API或 ILM 的收缩操作来减少其主分片数量。

    POST my-index/_shrink/my-shrunken-index
  • 如果你的节点具有较大的磁盘容量,你可以提高低磁盘水位线或将其设置为明确的字节值。

    PUT _cluster/settings
    {
      "persistent": {
        "cluster.routing.allocation.disk.watermark.low": "30gb"
      }
    }
减少JVM内存压力
edit

分片分配需要JVM堆内存。高JVM内存压力可能会触发 断路器,停止分配并导致分片未分配。请参阅高JVM内存压力

恢复丢失的主分片的数据
edit

如果包含主分片的节点丢失,Elasticsearch 通常可以使用另一个节点上的副本来替换它。如果你无法恢复该节点,并且副本不存在或无法恢复,Allocation Explain 将报告 no_valid_shard_copy,你需要执行以下操作之一:

  • 快照 恢复丢失的数据
  • 从其原始数据源索引丢失的数据
  • 通过运行 删除索引 接受索引级别的数据丢失
  • 通过执行 集群重新路由 的 allocate_stale_primary 或 allocate_empty_primary 命令并设置 accept_data_loss: true 来接受分片级别的数据丢失

    仅在节点恢复不再可能时使用此选项。此过程分配一个空的主分片。如果节点稍后重新加入集群,Elasticsearch将使用这个较新的空分片覆盖其主分片,从而导致数据丢失。

    POST _cluster/reroute
    {
      "commands": [
        {
          "allocate_empty_primary": {
            "index": "my-index",
            "shard": 0,
            "node": "my-node",
            "accept_data_loss": "true"
          }
        }
      ]
    }

被拒绝的请求

edit

当Elasticsearch拒绝一个请求时,它会停止操作并返回一个带有429响应码的错误。被拒绝的请求通常由以下原因引起:

检查被拒绝的任务

edit

要检查每个线程池的拒绝任务数量,请使用cat thread pool APIrejected任务与completed任务的高比率,特别是在searchwrite线程池中,意味着Elasticsearch经常拒绝请求。

GET /_cat/thread_pool?v=true&h=id,name,queue,active,rejected,completed

write 线程池拒绝频繁出现在错误的API和相关日志中,显示为EsRejectedExecutionException,并带有QueueResizingEsThreadPoolExecutor队列容量

这些错误通常与积压的任务有关。

检查断路器

edit

要检查跳闸的断路器数量,请使用节点统计信息 API

GET /_nodes/stats/breaker

这些统计数据是从节点启动时开始累积的。更多信息,请参阅 断路器错误

检查索引压力

edit

要检查索引压力拒绝的数量,请使用节点统计信息 API

GET _nodes/stats?human&filter_path=nodes.*.indexing_pressure

这些统计数据是从节点启动时开始累积的。

索引压力拒绝显示为 EsRejectedExecutionException,并表明它们因 coordinating_and_primary_bytescoordinatingprimaryreplica 而被拒绝。

这些错误通常与积压的任务批量索引大小或摄取目标的 refresh_interval设置有关。

防止请求被拒绝

edit

修复高CPU和内存使用率

如果 Elasticsearch 经常拒绝请求和其他任务,您的集群可能存在高 CPU 使用率或高 JVM 内存压力。有关提示,请参阅 高 CPU 使用率高 JVM 内存压力

任务队列积压

edit

积压的任务队列可能会阻止任务完成,并将集群置于不健康状态。资源限制、一次性触发大量任务以及长时间运行的任务都可能导致任务队列积压。

诊断任务队列积压

edit

检查线程池状态

一个线程池耗尽可能会导致被拒绝的请求

线程池耗尽可能仅限于特定的数据层。如果热点问题正在发生,一个节点可能会比其他节点更快地耗尽,导致性能问题和不断增长的任务积压。

您可以使用cat thread pool API来查看每个线程池中的活动线程数量,以及有多少任务在队列中,有多少任务被拒绝,以及有多少任务已完成。

GET /_cat/thread_pool?v&s=t,n&h=type,name,node_name,active,queue,rejected,completed

The activequeue 统计数据是即时的,而 rejectedcompleted 统计数据是从节点启动以来的累积数据。

检查每个节点上的热线程

如果某个线程池队列积压,您可以定期轮询 Nodes hot threads API 以确定线程是否拥有足够的资源来推进,并评估其推进的速度。

GET /_nodes/hot_threads

查找长时间运行的节点任务

长时间运行的任务也可能导致积压。您可以使用任务管理 API 来获取有关正在运行的节点任务的信息。检查 running_time_in_nanos 以识别完成时间过长的任务。

GET /_tasks?pretty=true&human=true&detailed=true

如果怀疑某个特定的action,您可以进一步过滤任务。最常见的长时间运行的任务是批量索引或搜索相关的任务。

  • 过滤 批量索引 操作:

    GET /_tasks?human&detailed&actions=indices:data/write/bulk
  • 搜索操作的过滤器:

    GET /_tasks?human&detailed&actions=indices:data/write/search

API响应可能包含额外的任务列,包括descriptionheader,这些列提供了任务参数、目标和请求者。您可以使用此信息进行进一步的诊断。

查找长时间运行的集群任务

任务积压也可能表现为同步集群状态的延迟。您可以使用待处理的集群任务 API来获取有关正在运行的待处理集群状态同步任务的信息。

GET /_cluster/pending_tasks

检查 timeInQueue 以识别那些完成时间过长的任务。

解决任务队列积压

edit

增加可用资源

如果任务进展缓慢且队列积压,您可能需要采取措施来减少CPU使用率

在某些情况下,增加线程池的大小可能会有所帮助。 例如,force_merge 线程池默认使用单个线程。 将其大小增加到 2 可能有助于减少强制合并请求的积压。

取消卡住的任务

如果你发现活跃任务的热线程没有进展并且有积压,考虑取消任务。

映射爆炸

edit

Elasticsearch 的搜索和 Kibana 的发现 JavaScript 渲染依赖于搜索支持索引的总 映射字段 数量,包括所有映射深度。当这个总数过高或呈指数级增长时,我们称之为遭遇映射爆炸。字段数量达到如此高的水平并不常见,通常表明上游文档格式存在问题,如 此博客所示

映射爆炸可能会表现为以下性能症状:

  • CAT nodes 报告主节点和/或托管索引分片的节点上的高堆内存或CPU使用率。这可能会潜在地升级为临时节点无响应和/或主节点过载。
  • CAT tasks 报告仅与此索引或索引相关的长时间搜索持续时间,即使在简单的搜索中也是如此。
  • CAT tasks 报告仅与此索引或索引相关的长时间索引持续时间。这通常与待处理任务相关,报告协调节点正在等待所有其他节点确认它们在映射更新请求上。
  • Discover的通配符字段页面加载API命令或Dev Tools页面刷新自动完成API命令需要很长时间(超过10秒)或在浏览器的开发者工具网络选项卡中超时。更多信息,请参阅我们的Discover故障排除指南
  • Discover的可用字段在浏览器的开发者工具性能选项卡中编译Javascript需要很长时间。这可能会潜在地升级为临时浏览器页面无响应。
  • Kibana的警报安全规则可能会错误内容长度(X)大于允许的最大字符串(Y),其中X是尝试的有效载荷,Y是Kibana的server-maxPayload
  • 长时间Elasticsearch启动持续时间。

预防或准备

edit

映射一旦初始化后,不能进行字段缩减。 Elasticsearch 索引默认使用 动态映射, 这通常不会引起问题,除非与覆盖 index.mapping.total_fields.limit 结合使用。 默认的 1000 限制被认为是慷慨的,尽管将其覆盖为 10000 在不同的使用场景下不会引起明显的性能影响。然而,举一个不好的例子,将其覆盖为 100000 并且达到这个限制 通常会对性能产生强烈的影响。

如果你的索引映射字段期望包含一个大的、任意的键集合,你可以考虑以下方法:

修改为嵌套数据类型并不能解决核心问题。

检查问题

edit

要确认索引的字段总数以检查映射爆炸:

  • 检查Elasticsearch集群日志中的错误 Limit of total fields [X] in index [Y] has been exceeded,其中 Xindex.mapping.total_fields.limit 的值,Y 是你的索引。相关的摄取源日志错误将是 Limit of total fields [X] has been exceeded while adding new fields [Z],其中 Z 是尝试添加的新字段。
  • 对于顶级字段,轮询 字段功能 以获取 fields=*
  • 获取映射 的输出中搜索 "type"
  • 如果你倾向于使用第三方工具JQ,你可以处理获取映射mapping.json输出。

    $ cat mapping.json | jq -c 'to_entries[]| .key as $index| [.value.mappings| to_entries[]|select(.key=="properties") | {(.key):([.value|..|.type?|select(.!=null)]|length)}]| map(to_entries)| flatten| from_entries| ([to_entries[].value]|add)| {index: $index, field_count: .}'

您可以使用分析索引磁盘使用情况来查找从未或很少填充的字段,作为轻松的优化点。

复杂的爆炸

edit

映射爆炸也涵盖了个别索引字段总数在限制范围内但组合索引字段总数非常高的情况。症状通常首先在数据视图上被注意到,并通过解析索引API追溯到单个索引或索引子集。

然而,尽管较为少见,但也有可能只在备份索引的组合上遇到映射爆炸。例如,如果一个数据流的备份索引都达到了字段总数限制,但每个索引包含的独特字段彼此不同。

这种情况最简单的方法是通过添加一个数据视图并检查其字段选项卡中的总字段数来体现。这个统计数据确实告诉您总体字段数,而不仅仅是index:true的字段,但它作为一个良好的基准。

如果你的问题仅通过数据视图出现,如果你没有使用多字段,你可以考虑此菜单的字段过滤器。或者,你可以考虑使用更精确的索引模式,或者使用负模式来过滤掉有问题的索引。例如,如果logs-*由于有问题的后备索引logs-lotsOfFields-*而导致字段数量过多,那么你可以更新为logs-*,-logs-lotsOfFields-*logs-iMeantThisAnyway-*

解析

edit

映射爆炸不容易解决,因此最好通过上述方法预防。遇到这种情况通常表明上游数据发生了意外变化或规划失败。如果遇到这种情况,我们建议审查您的数据架构。以下选项是本页前面讨论的选项之外的补充;它们应根据最佳适用情况应用:

拆分索引 不会解决核心问题。

热点定位

edit

计算机热点 可能在Elasticsearch中发生,当资源利用率在 节点之间分布不均时。临时峰值通常不被认为是问题,但 持续的显著独特利用可能导致集群瓶颈 并应进行审查。

请参阅此视频以了解解决热点问题故障排除的演练。

检测热点

edit

热点问题最常见的表现为某些节点上的资源利用率(如disk.percentheap.percentcpu)显著升高,这些信息可以通过cat nodes报告。个别的高峰并不一定是有问题的,但如果利用率反复出现高峰或长时间(例如超过30秒)持续保持高位,则可能存在资源热点问题。

例如,让我们展示使用cat节点来处理两个独立的可能问题:

GET _cat/nodes?v&s=master,name&h=name,master,node.role,heap.percent,disk.used_percent,cpu

假装在五分钟内拉取了两次相同的输出:

name   master node.role heap.percent disk.used_percent cpu
node_1 *      hirstm              24                20  95
node_2 -      hirstm              23                18  18
node_3 -      hirstmv             25                90  10

这里我们看到两种显著不同的利用方式:主节点在 cpu: 95,而一个热点节点在 disk.used_percent: 90%。这表明在这两个节点上发生了热点定位,但不一定来自相同的根本原因。

原因

edit

历史上,集群主要由于硬件、分片分布和/或任务负载的影响而经历热点问题。我们将按其可能影响的范围顺序逐一审查这些因素。

硬件

edit

以下是一些可能导致热点问题的常见不正确硬件设置:

  • 资源分配不均匀。例如,如果一个热点节点被分配了其同级节点一半的CPU。Elasticsearch期望数据层上的所有节点共享相同的硬件配置文件或规格。
  • 资源被主机上的另一个服务消耗,包括其他Elasticsearch节点。请参阅我们的专用主机建议。
  • 资源经历不同的网络或磁盘吞吐量。例如,如果一个节点的I/O低于其同级节点。有关更多信息,请参阅使用更快的硬件
  • 已配置堆大小超过31GB的JVM。有关更多信息,请参阅设置JVM堆大小
  • 有问题的资源唯一报告内存交换

分片分布

edit

Elasticsearch 索引被划分为一个或多个 分片,这些分片有时可能分布不均。Elasticsearch 通过在数据节点之间平衡分片数量来解决这个问题。正如在8.6 版本中引入的那样,Elasticsearch 默认还启用了期望的平衡来应对摄取负载。一个节点可能仍然会遇到热点问题,这可能是由于写入密集型的索引或其托管的总体分片数量造成的。

节点级别
edit

您可以通过cat allocation检查分片平衡情况,尽管从版本8.6开始,期望的平衡可能不再完全期望平衡分片。请注意,这两种方法在集群稳定性问题期间可能会暂时显示有问题的失衡。

例如,让我们展示两个使用猫分配的独立合理问题:

GET _cat/allocation?v&s=node&h=node,shards,disk.percent,disk.indices,disk.used

返回的结果可能是:

node   shards disk.percent disk.indices disk.used
node_1    446           19      154.8gb   173.1gb
node_2     31           52       44.6gb   372.7gb
node_3    445           43      271.5gb   289.4gb

这里我们看到两种显著不同的情况。node_2 最近重新启动,因此它的分片数量远低于所有其他节点。这也与 disk.indices 远小于 disk.used 有关,因为分片正在恢复,如通过 cat recovery 所见。虽然 node_2 的分片数量较低,但由于正在进行 ILM 滚动,它可能会成为一个写热点。这是下一节中讨论的写热点的常见根本原因。

第二种情况是node_3disk.percentnode_1高,尽管它们持有的分片数量大致相同。这种情况发生在分片大小不均匀(参见目标是分片最多包含200M文档,或者大小在10GB到50GB之间)或者存在大量空索引时。

基于期望平衡的集群重新平衡可以很大程度上避免节点过热。它可以通过节点达到水位线(参考修复磁盘水位线错误)或写入量大的索引的总分片数远低于写入节点数来限制。

您可以通过节点统计信息API确认热点节点, 可能通过时间上的两次轮询来仅检查它们之间的统计信息差异, 而不是一次轮询为您提供节点整个节点运行时间的统计信息。例如,要检查所有节点的索引统计信息:

GET _nodes/stats?human&filter_path=nodes.*.name,nodes.*.indices.indexing
索引级别
edit

热点节点经常通过cat thread poolwritesearch队列备份显现出来。例如:

GET _cat/thread_pool/write,search?v=true&s=n,nn&h=n,nn,q,a,r,c

返回的结果可能是:

n      nn       q a r    c
search node_1   3 1 0 1287
search node_2   0 2 0 1159
search node_3   0 1 0 1302
write  node_1 100 3 0 4259
write  node_2   0 4 0  980
write  node_3   1 5 0 8714

这里你可以看到两种显著不同的情况。首先,node_1 的写队列严重积压,与其他节点相比。其次,node_3 显示的历史完成的写入量是其他节点的两倍。这两种情况可能都是由于写密集型索引分布不均,或者是多个写密集型索引分配到同一个节点。由于主分片和副本分片的写入量在集群工作中大致相同,我们通常建议设置 index.routing.allocation.total_shards_per_node 以在调整索引分片数量与总节点数后强制分散索引。

我们通常建议重写索引具有足够的number_of_shards和副本number_of_replicas,以均匀分布在索引节点上。或者,您可以重新路由分片到更安静的节点,以减轻具有写热点问题的节点。

如果哪些索引存在问题不明显,你可以通过运行以下命令进一步内省:

索引统计 API

GET _stats?level=shards&human&expand_wildcards=all&filter_path=indices.*.total.indexing.index_total

对于更高级的分析,您可以轮询分片级别的统计信息, 这使您可以比较联合索引级别和节点级别的统计信息。这种分析 不会考虑节点重启和/或分片重新路由,但可以作为概览:

GET _stats/indexing,search?level=shards&human&expand_wildcards=all

例如,您可以使用第三方JQ工具, 来处理保存为indices_stats.json的输出:

cat indices_stats.json | jq -rc ['.indices|to_entries[]|.key as $i|.value.shards|to_entries[]|.key as $s|.value[]|{node:.routing.node[:4], index:$i, shard:$s, primary:.routing.primary, size:.store.size, total_indexing:.indexing.index_total, time_indexing:.indexing.index_time_in_millis, total_query:.search.query_total, time_query:.search.query_time_in_millis } | .+{ avg_indexing: (if .total_indexing>0 then (.time_indexing/.total_indexing|round) else 0 end), avg_search: (if .total_search>0 then (.time_search/.total_search|round) else 0 end) }'] > shard_stats.json

# show top written-to shard simplified stats which contain their index and node references
cat shard_stats.json | jq -rc 'sort_by(-.avg_indexing)[]' | head

任务负载

edit

分片分布问题很可能会表现为任务负载,如上文在cat thread pool示例中所见。由于单个任务的昂贵性或整体流量负载,任务也可能使某个节点过热。

例如,如果cat线程池报告了warmer 线程池上的高队列,您将查找受影响节点的热线程。假设它报告了与GlobalOrdinalsBuilder相关的warmer线程在100% cpu上。这将让您知道要检查字段数据的全球序号

或者,假设 cat nodes 显示一个热点主节点, 并且 cat thread pool 显示节点间的普遍排队情况。 这表明主节点已经过载。要解决这个问题,首先确保 硬件高可用性 设置,然后查找临时原因。在这个例子中, 节点热点线程 API 报告了多个线程在 其他 中,这表明它们正在等待或被垃圾收集或 I/O 阻塞。

对于这些示例情况中的任何一种,确认有问题的任务的一个好方法是查看通过cat 任务管理的最长运行非连续(指定为[c])任务。这可以通过检查通过cat 待处理任务的最长运行集群同步任务来补充。以第三个示例为例,

GET _cat/tasks?v&s=time:desc&h=type,action,running_time,node,cancellable

这可能返回:

type   action                running_time  node    cancellable
direct indices:data/read/eql 10m           node_1  true
...

这暴露了一个有问题的EQL查询。我们可以通过任务管理API进一步了解它。

GET _tasks?human&detailed

它的响应包含一个描述,报告了这个查询:

indices[winlogbeat-*,logs-window*], sequence by winlog.computer_name with maxspan=1m\n\n[authentication where host.os.type == "windows" and event.action:"logged-in" and\n event.outcome == "success" and process.name == "svchost.exe" ] by winlog.event_data.TargetLogonId

这让你知道要检查哪些索引(winlogbeat-*,logs-window*),以及EQL搜索请求体。最有可能这是SIEM相关的。你可以根据需要将此与审计日志记录结合使用,以追踪请求的来源。