CPU使用率高,但平均负载低


28

我们遇到了一种奇怪的行为,我们看到较高的CPU利用率,但平均负载却很低。

通过我们的监控系统中的以下图表可以最好地说明该行为。

CPU使用率和负载

在大约11:57时,CPU利用率从25%上升到75%。平均负载没有明显变化。

我们运行的服务器具有12个核心,每个核心具有2个超线程。操作系统将其视为24个CPU。

/usr/bin/mpstat 60 1每分钟运行一次以收集CPU利用率数据。上表显示了all行和%usr列的数据。我确信这确实显示了每个CPU数据的平均值,而不是 “堆栈”利用率。虽然我们在图表中看到75%的利用率,但是我们看到一个进程显示在中使用了大约2000%的“堆叠” CPU top

负载平均值是/proc/loadavg每分钟的数据。

uname -a 给出:

Linux ab04 2.6.32-279.el6.x86_64 #1 SMP Wed Jun 13 18:24:36 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux

Linux dist是 Red Hat Enterprise Linux Server release 6.3 (Santiago)

我们在机器上相当重的负载下运行了两个Java Web应用程序,认为每台机器100个请求/秒。

如果我正确地解释了CPU利用率数据,那么当我们有75%的CPU利用率时,这意味着我们的CPU平均平均有75%的时间在执行一个进程。但是,如果我们的CPU忙碌了75%的时间,我们是否不应该看到更高的平均负载?当我们在运行队列中只有2-4个作业时,CPU怎么会忙75%?

我们是否正确解释了我们的数据?什么会导致这种行为?


监视系统是否显示标准化的CPU负载(负载/ #CPU)?常规Linux CPU负载很难在具有不同核心/ cpu计数的系统之间进行比较,因此某些工具会使用标准化CPU负载来代替。
布赖恩

您的意思是将每个数据点除以CPU数量吗?即在我们的情况下loadavg / 24?如果有帮助,我可以轻松地从数据中创建这样的图表。
K Erlandsson

我建议您的图表可能已经显示了。
布赖恩

啊,很抱歉误会你。这本来是个很好的解释,但不幸的是,显示的是系统范围的平均负载。我只是三重检查。
K Erlandsson

Answers:


51

至少在Linux上,平均负载和CPU利用率实际上是两件事。平均负载是对一段时间内内核运行队列中正在等待多少任务(不仅是CPU时间,还有磁盘活动)的度量。CPU利用率是衡量CPU现在有多忙的指标。一个CPU线程以100%固定一分钟的最大负载可以“贡献”到1分钟的平均负载为1。一个具有超线程的4核CPU(8个虚拟核心)都以100%的功率持续1分钟将为8 1分钟的平均负载。

通常,这两个数字具有相互关联的模式,但您不能认为它们是相同的。您可能具有接近0%的CPU利用率的高负载(例如,当大量IO数据处于等待状态时),并且当您运行单线程进程时,您的负载可能为1%和100%全倾斜。同样在短时间内,您可以看到CPU接近100%,但是负载仍然低于1,因为平均指标还没有“赶上”。

我已经看到一台服务器的负载超过15,000(是的,这确实不是一个错字),并且CPU%接近0%。发生这种情况是因为Samba共享遇到了问题,许多客户开始陷入IO等待状态。如果您看到的是正常的高负载数而没有相应的CPU活动,则可能是某种存储问题。在虚拟机上,这也可能意味着还有其他VM激烈竞争同一VM主机上的存储资源。

高负载也不一定是一件坏事,在大多数情况下,它只是意味着系统已被充分利用,或者超出了其保持能力(如果负载数量大于处理器内核数量)。在我曾经是系统管理员的地方,他们比Nagios更加关注主系统上的平均负载。当负载很高时,他们给我打电话的速度比您说的SMTP快24/7。大多数时候,实际上并没有什么错,但是他们将装载编号与错误相联系,并像鹰一样看着它。经过检查后,我的回答通常是系统只是在做它的工作。当然,这是负载超过15000的同一位置(虽然不是同一台服务器),所以有时确实表示出了问题。您必须考虑系统的用途。如果这是主力,则可以预期负载自然会很高。


您如何表示我可以通过单线程进程负担1%和100%的CPU负载?您在说什么线程?如果我们考虑我们的Java进程,它们有很多线程,但是我假设从OS的角度来看线程被视为进程(毕竟在Linux上它们具有单独的PID)。从负载平均的角度来看,一个多线程Java进程是否仅被视为一项任务?
K Erlandsson

我只是自己进行了一个测试,Java进程中的线程对平均负载有所贡献,就好像它们在单独的进程中一样(即,在繁忙等待循环中运行10个线程的Java类给我的负载接近10)。对于您上面提到的线程化过程的澄清,我将不胜感激。谢谢!
K Erlandsson

我的意思是,如果您有一个非多线程进程(即一次仅使用一个CPU的进程)。例如,如果您只编写一个运行繁忙循环的简单C程序,那么它仅运行一个线程,一次仅使用1个CPU。
deltaray

我发现的所有信息都表明,从内核和计算负载时,线程都被视为独立的进程。因此,我看不到如何在全倾斜的情况下进行多线程处理,从而在多CPU系统上导致1个负载和100%CPU。你能帮我理解你的意思吗?
K Erlandsson

对于需要更多详细信息的人:Brendan Gregg的“ Linux平均负载:解决奥秘”拥有我所需要的所有答案。
Nickolay

24

负载是一个非常具有欺骗性的数字。撒一粒盐。

如果您快速连续地产生许多任务并且很快完成,则运行队列中的进程数太少而无法为其注册负载(内核每五秒钟对负载计数一次)。

考虑这个示例,在我的具有8个逻辑内核的主机上,此python脚本将在顶部最多注册大量CPU使用率(大约85%),但几乎没有负载。

import os, sys

while True:
  for j in range(8):
    parent = os.fork()
    if not parent:
      n = 0
      for i in range(10000):
        n += 1
      sys.exit(0)
  for j in range(8):
    os.wait()

另一种实现方式是避免wait以8组为一组(这会使测试产生偏差)。在这里,父母总是试图将子代的数量保持在活动CPU的数量上,这样它将比第一种方法更忙,而且希望更准确。

/* Compile with flags -O0 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <err.h>
#include <errno.h>

#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define ITERATIONS 50000

int maxchild = 0;
volatile int numspawned = 0;

void childhandle(
    int signal)
{
  int stat;
  /* Handle all exited children, until none are left to handle */
  while (waitpid(-1, &stat, WNOHANG) > 0) {
    numspawned--;
  }
}

/* Stupid task for our children to do */
void do_task(
    void)
{
  int i,j;
  for (i=0; i < ITERATIONS; i++)
    j++;
  exit(0);
}

int main() {
  pid_t pid;

  struct sigaction act;
  sigset_t sigs, old;

  maxchild = sysconf(_SC_NPROCESSORS_ONLN);

  /* Setup child handler */
  memset(&act, 0, sizeof(act));
  act.sa_handler = childhandle;
  if (sigaction(SIGCHLD, &act, NULL) < 0)
    err(EXIT_FAILURE, "sigaction");

  /* Defer the sigchild signal */
  sigemptyset(&sigs);
  sigaddset(&sigs, SIGCHLD);
  if (sigprocmask(SIG_BLOCK, &sigs, &old) < 0)
    err(EXIT_FAILURE, "sigprocmask");

  /* Create processes, where our maxchild value is not met */
  while (1) {
    while (numspawned < maxchild) {
      pid = fork();
      if (pid < 0)
        err(EXIT_FAILURE, "fork");

      else if (pid == 0) /* child process */
        do_task();
      else               /* parent */
        numspawned++;
    }
    /* Atomically unblocks signal, handler then picks it up, reblocks on finish */
    if (sigsuspend(&old) < 0 && errno != EINTR)
      err(EXIT_FAILURE, "sigsuspend");
  }
}

出现这种现象的原因是,与创建实际任务相比,该算法花费更多的时间来创建子进程(计数为10000)。尚未创建的任务不能计入“可运行”状态,但是在生成任务时会占用CPU时间%sys。

因此,答案可能确实是在您的情况下,无论完成什么工作,都会快速连续产生大量任务(线程或进程)。


感谢您的建议。我的问题中的图表显示了%user time(不包括CPU系统时间,我们只会看到系统时间略有增加)。反正很多小任务可以解释吗?如果平均每5秒采样一次,是否更频繁地采样mpstat给出的CPU利用率数据?
K Erlandsson

我不熟悉如何在那里进行CPU采样。永远不要阅读内核源代码。在我的示例中,%usr为70%+,%sys为15%。
马修·伊夫

很好的例子!
哈维尔·卢卡斯

5

如果平均负载没有增加太多,则仅意味着您的硬件规格和要处理的任务的性质可带来良好的总体吞吐量,从而避免将它们堆积在任务队列中一段时间​​。

如果存在争用现象,例如由于平均任务复杂度太高或任务平均处理时间占用了太多CPU周期,那么是的,平均负载将增加。

更新:

我的原始答案可能不清楚,所以我现在要澄清一下:

负荷平均计算的精确公式为:loadvg = tasks running + tasks waiting (for cores) + tasks blocked

您可以肯定具有良好的吞吐量,并接近平均24的负载,但不会影响任务处理时间。另一方面,您也可能有2-4个周期性任务没有足够快地完成,那么您将看到等待任务(针对CPU周期)的数量在增加,最终您将达到较高的平均负载。可能发生的另一件事是让任务运行出色的同步I / O操作,然后阻塞内核,降低吞吐量,并使等待的任务队列增加(在这种情况下,您可能会看到iowait指标发生变化)


据我了解,平均负载还包括当前正在执行的任务。这将意味着我们肯定可以增加平均负载,而无需实际争用CPU。还是我误会/误会你了?
K Erlandsson

@KristofferE您完全正确。实际公式为:loadavg =运行的任务+等待的任务(对于可用内核)+阻止的任务。这意味着您的平均负载为24,没有等待或阻塞的任务,因此仅具有“全部使用率”,即您的硬件容量无任何争用。您似乎对负载平均数,运行的进程数和CPU使用率感到困惑,我的回答主要集中在解释如何在总体上运行的进程数很少的情况下仍然可以提高负载平均数。重新阅读后可能还不清楚。
泽维尔·卢卡斯

2

平均负载包括磁盘IO上被阻止的任务,因此只需让10个任务都试图从非常慢的磁盘读取,您就可以轻松实现cpu利用率为零,平均负载为10。因此,繁忙的服务器通常会开始对磁盘进行颠簸,并且所有查找操作都会导致大量被阻塞的任务,从而提高平均负载,而cpu的使用率却下降了,因为所有任务都被阻塞在磁盘上。


1

尽管Matthew Ife的回答非常有帮助,并引导我们朝了正确的方向发展,但这并不是导致我们案例中行为发生的确切原因。在我们的案例中,我们有一个使用线程池的多线程Java应用程序,为什么创建实际任务没有完成任何工作。

但是,线程的实际工作是短暂的,并且包括IO等待或同步等待。正如马修(Matthew)在回答中提到的那样,平均负载是由OS采样的,因此可以错过短暂的任务。

我制作了一个重现此行为的Java程序。以下Java类在我们的其中一台服务器上产生28%的CPU利用率(堆叠的650%)。这样做时,平均负载约为1.3。这里的关键是线程内部的sleep(),没有它,负载计算就正确了。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MultiThreadLoad {

    private ThreadPoolExecutor e = new ThreadPoolExecutor(200, 200, 0l, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.CallerRunsPolicy());

    public void load() {
        while (true) {
            e.execute(new Runnable() {

                @Override
                public void run() {
                    sleep100Ms();
                    for (long i = 0; i < 5000000l; i++)
                        ;
                }

                private void sleep100Ms() {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }

    public static void main(String[] args) {
        new MultiThreadLoad().load();
    }

}

总而言之,理论是我们的应用程序中的线程会大量闲置,然后执行短暂的工作,这是为什么平均负载计算未正确采样任务的原因。


0

平均负载是CPU队列中的平均进程数。它对于每个系统都是特定的,您不能说一个LA在所有系统上通常都很高,而另一个LA则很低。因此,您有12个核心,并且要使LA显着增加,进程数必须非常高。

另一个问题是“ CPU使用率”图的含义。如果它是从SNMP那里获取的,并且应该像SNMP一样,并且您的SNMP实现是net-snmp,则只需堆叠来自12个CPU中每个CPU的CPU负载。因此,用于net-snmpCPU负载的总量为1200%。

如果我的假设是正确的,则CPU使用率并未显着增加。因此,洛杉矶并没有明显增加。


cpu使用情况取自mpstat(该all行)。我相当确定这是所有CPU的平均值,不是堆叠的。例如,发生问题时,顶部显示一个进程的CPU使用率2000%。那是堆积的用法。
K Erlandsson

0

尽管有点不寻常,但这里的场景并不是特别出乎意料。Xavier涉及但没有发展很多的是,尽管Linux(默认情况下)和大多数Unix版本实现了抢先式多任务处理,但是在一台运行良好的机器上,任务很少被抢占。每个任务都有占用CPU的时间片,只有在超过此时间并且有其他任务等待运行时才被抢占(请注意,负载报告了CPU和等待运行的平均进程数) 。在大多数情况下,一个过程将产生而不是被中断。

(通常,您只需要担心在CPU数量接近时(即调度程序开始抢占任务时)的负载)。

如果我们的CPU在75%的时间里很忙,我们不应该看到更高的平均负载吗?

所有有关活动模式的信息,显然增加了某些任务(很可能是少量的小巧的事情)对CPU的利用率,对其他任务的处理没有不利影响。如果您可以隔离正在处理的事务,那么我希望您会在减速期间看到一个新的组,而现有的任务集不会受到影响。

更新

在没有大量增加负载的情况下可能发生高CPU使用率的一种常见情况是,一个任务触发一个(或一个序列)其他任务,例如,在收到网络请求时,处理程序将请求路由到单独的线程,即单独的线程然后对其他进程进行一些异步调用。...运行队列的采样导致报告的负载低于实际负载-但它不会随CPU使用率线性增加-如果没有运行,触发的任务链就无法运行初始事件,并且由于它们按顺序发生(或多或少),因此运行队列不会膨胀。


OP最初提供的指示是CPU总数合计为“ 2000%”,这表明有很多任务用尽了CPU,而不是仅一个繁忙的进程。如果一分钟内保持一致的2000%,则通常可以预期负载为20-ish。
马修·伊夫

...在评论中,不在问题中,他对此不太确定。如果没有“ ALL”选项,mpstat会报告总的使用百分比而不是平均值。但这并不能改变答案-它与活动的模式有关。
symcbean 2015年

我100%肯定我们在图表中看到的CPU利用率是“每个CPU的平均值”。Mpstat在没有ALL的情况下运行,但是只保留了每个CPU的信息,该all行仍显示每个CPU的平均值。我将澄清这个问题。
K Erlandsson

能否请您详细介绍一下yoru的最后一部分?我无法理解您的意思,而您引用的问题部分是我最难以理解的部分。
K Erlandsson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.