监控单个节点edit

cluster-health 是对集群的所有信息的高度概述。而 node-stats API 则相反,它提供一个让人眼花缭乱的数组,其中包含了集群中每一个节点的统计信息。

node-stats 提供的统计值如此之多,在完全熟悉它之前,你可能都搞不清楚哪些指标是最值得关注的。我们将会高亮那些最重要的监控指标(但是我们鼓励你记录接口提供的所有指标 —— 或者用 Marvel —— 因为你永远不知道何时需要这个还是那个状态)。

node-stats API 可以通过如下命令执行:

GET _nodes/stats

在输出内容的开头,我们可以看到集群名称和我们的第一个节点:

{
   "cluster_name": "elasticsearch_zach",
   "nodes": {
      "UNr6ZMf5Qk-YCPA_L18BOQ": {
         "timestamp": 1408474151742,
         "name": "Zach",
         "transport_address": "inet[zacharys-air/192.168.1.131:9300]",
         "host": "zacharys-air",
         "ip": [
            "inet[zacharys-air/192.168.1.131:9300]",
            "NONE"
         ],
...

节点排列在一个哈希对象里,以节点的 UUID 作为键名。还显示了节点网络属性的一些信息(比如传输层地址和主机名)。这些值对调试诸如节点未加入集群这类自动发现问题很有用。通常你会发现是端口用错了,或者节点绑定在错误的 IP 地址/网络接口上了。

索引部分edit

索引(indices) 部分列出了这个节点上所有索引的汇总信息:

    "indices": {
        "docs": {
           "count": 6163666,
           "deleted": 0
        },
        "store": {
           "size_in_bytes": 2301398179,
           "throttle_time_in_millis": 122850
        },

返回的信息被归入以下部分:

  • docs 展示节点内有多少文档,以及还没有从段(segment)里彻底清除的标记为已删除的文档的数量。
  • store 部分显示节点耗用了多少物理存储空间。这个指标包括主分片和副本分片在内。如果限流时间(throttle_time_in_millis)很大,那可能表明你的磁盘限流设置得过低(在段与合并里讨论过)。
        "indexing": {
           "index_total": 803441,
           "index_time_in_millis": 367654,
           "index_current": 99,
           "delete_total": 0,
           "delete_time_in_millis": 0,
           "delete_current": 0
        },
        "get": {
           "total": 6,
           "time_in_millis": 2,
           "exists_total": 5,
           "exists_time_in_millis": 2,
           "missing_total": 1,
           "missing_time_in_millis": 0,
           "current": 0
        },
        "search": {
           "open_contexts": 0,
           "query_total": 123,
           "query_time_in_millis": 531,
           "query_current": 0,
           "fetch_total": 3,
           "fetch_time_in_millis": 55,
           "fetch_current": 0
        },
        "merges": {
           "current": 0,
           "current_docs": 0,
           "current_size_in_bytes": 0,
           "total": 1128,
           "total_time_in_millis": 21338523,
           "total_docs": 7241313,
           "total_size_in_bytes": 5724869463
        },
  • indexing 显示已经索引的文档的数量。这个值是一个累加计数器。在文档被删除的时候,数值不会下降。还要注意的是,在发生内部 索引 操作的时候,这个值也会增加,比如说文档更新。

    还列出了索引操作耗费的时间,正在索引的文档数量,以及删除操作的类似的统计信息。

  • get 显示通过 ID 获取文档的统计信息。包括对单个文档的 GETHEAD 请求。
  • search 描述 活跃中的搜索( open_contexts )的数量、查询的总次数、以及自节点启动以来在查询上消耗的总时间。 可以用 query_time_in_millis / query_total 的比值来粗略的评价你的查询有多高效。比值越大,每个查询花费的时间越多,你应该要考虑调优了。

    fetch 统计信息详细列举了查询处理的后一半流程(query-then-fetch 里的 fetch )。如果 fetch 耗时比 query 还多,说明磁盘较慢,或者获取了太多文档,或者可能搜索请求设置了太大的分页(比如, size: 10000 )。

  • merges 包括了 Lucene 段合并相关的信息。它会告诉你目前在运行几个合并,合并涉及的文档数量,正在合并的段的总大小,以及在合并操作上消耗的总时间。

    如果集群写入压力很大,合并统计信息就非常重要。合并要消耗大量的磁盘 I/O 和 CPU 资源。如果你的索引有大量的写入,同时又发现大量的合并数,一定要去阅读索引性能技巧

    注意:文档更新和删除也会导致大量的合并数,因为它们会产生最终需要被合并的段 碎片

        "filter_cache": {
           "memory_size_in_bytes": 48,
           "evictions": 0
        },
        "fielddata": {
           "memory_size_in_bytes": 0,
           "evictions": 0
        },
        "segments": {
           "count": 319,
           "memory_in_bytes": 65812120
        },
        ...
  • filter_cache 展示了已缓存的过滤器位集合所用的内存数量,以及过滤器被淘汰出内存的次数。过多的淘汰数 可能 说明你需要增加过滤器缓存的大小,或者你的过滤器不太适合缓存(比如它们因为基数高而在大量产生,就像是缓存一个 now 时间表达式)。 例如,由于基数较高,比如缓存now的日期表达式(每秒中都会产生大量的数据),它们产生了很大的波动。

    不过,淘汰数是一个很难评定的指标。过滤器是在每个段上缓存的,而从一个小的段里淘汰过滤器,代价比从一个大的段里要小的多。有可能你有很大的淘汰数,但是它们都发生在小段上,这对查询性能的影响很小。

    把淘汰数指标作为一个粗略的参考。如果你看到数字很大,检查一下你的过滤器,确保他们都是正常缓存的。不断淘汰着的过滤器,哪怕都发生在很小的段上,效率也比正常缓存了的过滤器低很多。

  • field_data 显示 fielddata 使用的内存,用以聚合、排序等等。这里也有一个淘汰数。和 filter_cache 不同的是,这里的淘汰数是很有用的:这个数应该或者至少是接近于 0。因为 fielddata 不是缓存,任何淘汰都消耗巨大,应该避免掉。如果你在这里看到淘汰数,你需要重新评估你的内存状况、fielddata 限制、查询语句,或者三者都要。
  • segments 会展示这个节点目前正在服务中的 Lucene 段的数量。这是一个重要的数字。大多数索引应该有大约 50–150 个段,哪怕它们存有 TB 级别的数十亿条文档。段数量过大表明合并出现了问题(比如,合并速度跟不上段的创建)。注意这个统计值是节点上所有索引的总数据,记住这点。

    memory 统计值展示了 Lucene 段自己用掉的内存大小。这里包括底层数据结构,比如倒排表、字典、布隆过滤器等。太大的段数量会增加这些数据结构带来的开销,这个内存使用量就是一个方便用来衡量开销的度量值。

操作系统和进程部分 (OS and Process Sections)edit

OSProcess 部分基本是自描述的,不会在细节中展开讲解。它们列出了基础的资源统计值,比如 CPU 和负载。OS 部分描述了整个操作系统,而 Process 部分只显示 Elasticsearch 的 JVM 进程使用的资源情况。

这些都是非常有用的度量值,但通常在监视堆栈的其他地方进行度量。统计值包括下面这些:

  • CPU
  • 负载 (load)
  • 内存使用率 (memory usage)
  • Swap 使用率
  • 打开的文件描述符 (open file descriptors)

JVM 部分edit

jvm 部分包括了运行 Elasticsearch 的 JVM 进程一些很关键的信息。最重要的,它包括了垃圾回收的细节,这对你的 Elasticsearch 集群的稳定性有着重大影响。

因为垃圾回收对 Elasticsearch 是如此重要,你应该非常熟悉 node-stats API 里的这部分内容:

        "jvm": {
            "timestamp": 1408556438203,
            "uptime_in_millis": 14457,
            "mem": {
               "heap_used_in_bytes": 457252160,
               "heap_used_percent": 44,
               "heap_committed_in_bytes": 1038876672,
               "heap_max_in_bytes": 1038876672,
               "non_heap_used_in_bytes": 38680680,
               "non_heap_committed_in_bytes": 38993920,
  • jvm 部分首先列出一些和堆(heap)内存使用有关的常见统计值。 你可以看到有多少堆(heap)被使用了,多少已提交(committed, 实际分配给进程的),以及堆(heap)被允许分配的最大值。 理想情况下,heap_committed_in_bytes应该等于heap_max_in_bytes。 如果已提交的(committed)大小更小,JVM 最终会被迫调整堆(heap)大小 —— 这是一个成本很高的过程。 如果这两个值不相等,阅读堆内存:大小和交换学习如何正确的配置它。

    heap_used_percent 指标是值得关注的一个数字。Elasticsearch 被配置为当堆(heap)达到 75% 的时候开始 GC。如果节点一直 >= 75%,该节点正处于 内存压力 状态。这是个危险信号,不远的未来可能就有慢 GC 要出现了。

    如果堆(heap)使用率一直 >=85%,你就麻烦了。Heap 在 90–95% 之间,则面临可怕的性能风险,此时最好的情况是长达 10–30s 的 GC,最差的情况就是内存溢出(OOM)异常。

   "pools": {
      "young": {
         "used_in_bytes": 138467752,
         "max_in_bytes": 279183360,
         "peak_used_in_bytes": 279183360,
         "peak_max_in_bytes": 279183360
      },
      "survivor": {
         "used_in_bytes": 34865152,
         "max_in_bytes": 34865152,
         "peak_used_in_bytes": 34865152,
         "peak_max_in_bytes": 34865152
      },
      "old": {
         "used_in_bytes": 283919256,
         "max_in_bytes": 724828160,
         "peak_used_in_bytes": 283919256,
         "peak_max_in_bytes": 724828160
      }
   }
},
  • 新生代(young)幸存区(survivor)老生代(old) 部分分别展示 GC 中每一个代的内存使用情况。这些统计值很方便观察其相对大小,但是在调试问题的时候,通常并不怎么重要。
"gc": {
   "collectors": {
      "young": {
         "collection_count": 13,
         "collection_time_in_millis": 923
      },
      "old": {
         "collection_count": 0,
         "collection_time_in_millis": 0
      }
   }
}
  • gc 部分显示新生代和老生代的垃圾回收次数和累积时间。 大多数时候你可以忽略掉新生代的次数:这个数字通常都很大。这是完全正常的。

    与之相反,老生代的垃圾回收次数应该很小,而且collection_time_in_millis也应该很小。 这些是累积值,所以很难给出一个阈值表示你要开始操心了(比如,一个跑了一整年的节点,即使很健康,也会有一个比较大的计数)。 这就是像 Marvel 这类工具很有用的一个原因。 GC 计数的 时间趋势 是个重要的考虑因素。

    GC 花费的时间也很重要。 比如,在索引文档时,生成了一定数量的垃圾。 这是正常的,每时每刻都会导致 GC。 这些 GC 绝大多数时候都很快,对节点影响很小:新生代一般就花一两毫秒,老生代花几百毫秒。这些跟 10 秒级别的 GC 是很不一样的。

    我们的最佳建议是定期收集 GC 次数和时长(或者使用 Marvel)然后观察 GC 频率。你也可以开启慢 GC 日志记录,在 日志记录 小节已经讨论过。

线程池部分edit

Elasticsearch 在内部维护了线程池。这些线程池相互协作完成任务,有必要的话相互间还会传递任务。通常来说,你不需要配置或者调优线程池,不过查看它们的统计值有时候还是有用的,可以深入了解集群的表现如何。

大约有十几个线程池,但他们格式相同:

  "index": {
     "threads": 1,
     "queue": 0,
     "active": 0,
     "rejected": 0,
     "largest": 1,
     "completed": 1
  }

每个线程池会列出已配置的线程数量( threads ),当前在处理任务的线程数量( active ),以及在队列中等待处理的任务单元数量( queue )。

如果队列中任务单元数达到了极限,新的任务单元会开始被拒绝,你会在 rejected 统计值上看到它反映出来。这通常是你的集群在某些资源上碰到瓶颈的信号。因为队列满了意味着你的节点或集群在用最高速度运行,但依然跟不上任务的蜂拥而入。

有十几个线程池。大多数你可以安全地忽略,但是有一小部分还是值得关注的:

indexing
普通的索引请求的线程池
bulk
批量请求,和单条的索引请求不同的线程池
get
通过文档id获取文档(Get-by-ID) 操作
search
所有的搜索和查询请求
merging
专用于管理 Lucene 合并的线程池

文件系统和网络部分 (FS and Network Sections)edit

继续向下阅读 node-stats API,你会看到一串和你的文件系统相关的统计值:可用空间,数据目录路径,磁盘 I/O 统计值,等等。 如果你没有监控磁盘可用空间的话,可以从这里获取这些统计值。 磁盘 I/O 统计值也很方便,不过通常那些更专门的命令行工具(比如 iostat )会更有用些。

显然,Elasticsearch 在磁盘空间满的时候很难运行 —— 所以请确保不会这样。

还有两个跟网络统计值相关的部分:

        "transport": {
            "server_open": 13,
            "rx_count": 11696,
            "rx_size_in_bytes": 1525774,
            "tx_count": 10282,
            "tx_size_in_bytes": 1440101928
         },
         "http": {
            "current_open": 4,
            "total_opened": 23
         },
  • transport 显示与传输地址(transport address)相关的一些基础统计值。 包括节点间的通信(通常是 9300 端口)以及任意传输客户端或者节点客户端的连接。如果看到这里有很多连接数不要担心;Elasticsearch 在节点之间维护了大量的连接。
  • http 显示 HTTP 端口(通常是 9200)的统计值。 如果你看到 total_opened 数很大而且还在一直上涨,这是一个明确信号,说明你的 HTTP 客户端里有没启用 keep-alive 长连接的。 持续的 keep-alive 长连接对性能很重要,因为连接、断开套接字是很昂贵的(而且浪费文件描述符)。请确认你的客户端都配置正确。

断路器edit

终于到了最后一部分:跟 fielddata 断路器(在 断路器 介绍过)相关的统计值:

         "fielddata_breaker": {
            "maximum_size_in_bytes": 623326003,
            "maximum_size": "594.4mb",
            "estimated_size_in_bytes": 0,
            "estimated_size": "0b",
            "overhead": 1.03,
            "tripped": 0
         }

这里你可以确定断路器的最大值(比如,一个请求试图请求更多内存, 到多大时会触发断路器)。 这个部分还会让你知道断路器被触发了多少次,以及当前配置的开销。 开销用来填充评估,因为有些请求比其他请求更难评估。

主要需要关注的是 tripped 指标。 如果这个数字很大或者持续上涨,这是一个信号,说明你的请求需要优化,或者你需要添加更多内存(单机上添加,或者通过添加新节点的方式)。