热点编辑

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

检测热点编辑

热点最常表现为 cat nodes 报告的节点子集中的资源利用率(disk.percentheap.percentcpu)显著升高。 个别峰值不一定有问题,但如果利用率反复飙升或持续长时间(例如超过 30 秒)保持高位,则该资源可能正在经历有问题的热点。

例如,让我们使用 cat nodes 展示两个单独的合理问题

response = client.cat.nodes(
  v: true,
  s: 'master,name',
  h: 'name,master,node.role,heap.percent,disk.used_percent,cpu'
)
puts response
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%。 这表明在这两个节点上发生了热点,并且不一定来自相同的根本原因。

原因编辑

历史上,集群经历热点主要是硬件、分片分布和/或任务负载的影响。 我们将按其潜在影响范围的顺序依次审查这些内容。

硬件编辑

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

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

分片分布编辑

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

节点级别编辑

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

例如,让我们使用 cat allocation 展示两个单独的合理问题

response = client.cat.allocation(
  v: true,
  s: 'node',
  h: 'node,shards,disk.percent,disk.indices,disk.used'
)
puts response
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.percent 高于 node_1,即使它们拥有大致相同数量的分片。 当分片大小不均匀(请参阅 目标是每个分片最多 2 亿个文档,或大小在 10GB 到 50GB 之间)或有很多空索引时,就会发生这种情况。

基于期望平衡的集群重新平衡可以很好地防止节点出现热点。 它可能会受到节点达到 水印(请参阅 修复磁盘水印错误)或写入密集型索引的总分片数远低于写入节点数的限制。

您可以通过 节点统计信息 API 确认热点节点,可能会在一段时间内轮询两次,以仅检查它们之间的统计信息差异,而不是轮询一次以获取节点完整 节点正常运行时间 的统计信息。 例如,要检查所有节点索引统计信息

response = client.nodes.stats(
  human: true,
  filter_path: 'nodes.*.name,nodes.*.indices.indexing'
)
puts response
GET _nodes/stats?human&filter_path=nodes.*.name,nodes.*.indices.indexing
索引级别编辑

热点节点经常通过 cat 线程池writesearch 队列备份浮出水面。 例如

response = client.cat.thread_pool(
  thread_pool_patterns: 'write,search',
  v: true,
  s: 'n,nn',
  h: 'n,nn,q,a,r,c'
)
puts response
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 进行进一步的调查

response = client.indices.stats(
  level: 'shards',
  human: true,
  expand_wildcards: 'all',
  filter_path: 'indices.*.total.indexing.index_total'
)
puts response
GET _stats?level=shards&human&expand_wildcards=all&filter_path=indices.*.total.indexing.index_total

为了进行更高级的分析,您可以轮询分片级统计信息,这使您可以比较联合索引级和节点级统计信息。 此分析不会考虑节点重新启动和/或分片重新路由,但可以作为概述

response = client.indices.stats(
  metric: 'indexing,search',
  level: 'shards',
  human: true,
  expand_wildcards: 'all'
)
puts response
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

任务负载编辑

分片分布问题很可能会表现为任务负载,如上面 cat 线程池 示例中所示。 由于个别定性成本高或总体定量流量负载,任务也可能成为节点的热点。

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

或者,假设 cat nodes 显示一个热点主节点,而 cat 线程池 显示节点之间的常规排队。 这表明主节点不堪重负。 要解决此问题,请首先确保 硬件高可用性 设置,然后查找短暂原因。 在此示例中,节点热点线程 API 报告 other 中的多个线程,这表明它们正在等待垃圾收集或 I/O 或被其阻塞。

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

response = client.cat.tasks(
  v: true,
  s: 'time:desc',
  h: 'type,action,running_time,node,cancellable'
)
puts response
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 获得关于它的更多信息,

response = client.tasks.list(
  human: true,
  detailed: true
)
puts response
GET _tasks?human&detailed

它的响应包含一个报告此查询的 description

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 相关 的。 您可以根据需要将其与 审计日志记录 相结合,以跟踪请求来源。