我们遇到了一种奇怪的行为,在这种行为中,我们看到CPU利用率很高,但平均负载却很低。
我们的监控系统的以下图表最好地说明了这种行为。

在11:57左右,CPU利用率从25%提高到75%。负载平均值没有显著变化。
我们运行的服务器有12个内核,每个线程有2个超级线程。操作系统认为这是24个CPU。
每分钟运行/usr/bin/mpstat 60 1收集CPU利用率数据。上面的图表显示了all行和%usr列的数据。我确信这确实显示了每个CPU数据的平均值,而不是“堆叠”利用率。当我们在图表中看到75%的利用率时,我们看到一个进程显示在top中使用了大约2000%的“堆叠”CPU。
每分钟从/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/LinuxLinux是Red Hat Enterprise Linux Server release 6.3 (Santiago)
我们在机器上负载相当重的情况下运行了几个Java应用程序,假设每台机器有100个请求/S。
如果我正确地解释CPU利用率数据,那么当我们有75%的CPU利用率时,这意味着我们的CPU平均要执行75%的进程。但是,如果我们的CPU在75%的时间里都很忙,难道我们不应该看到更高的平均负载吗?当我们在运行队列中只有2-4个作业时,CPU怎么会有75%的忙碌呢?
我们是否正确地解释我们的数据?是什么导致了这种行为?
发布于 2015-02-17 08:45:02
马太·伊夫的回答很有帮助,引导我们朝着正确的方向前进,但这并不是造成我们这种行为的确切原因。在我们的例子中,我们有一个多线程Java应用程序,它使用线程池,为什么没有完成创建实际任务的工作。
但是,线程所做的实际工作是短暂的,包括IO等待或同步等待。正如Matthew在他的回答中提到的,负载平均是由OS抽样的,因此可以错过短暂的任务。
我制作了一个Java程序来复制这种行为。下面的Java类在我们的服务器上生成CPU利用率为28% (650%堆叠)。在进行此操作时,负载平均约为1.3。这里的关键是线程中的here (),没有它,负载计算是正确的。
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();
}
}总之,理论上是我们的应用程序中的线程经常空闲,然后执行短暂的工作,为什么负载平均计算不能正确地采样任务。
发布于 2015-02-12 13:05:57
负荷是一个非常具有欺骗性的数字。吃点盐吧。
如果您很快地生成了许多任务,并且这些任务很快就完成了,那么运行队列中的进程数量太少,无法为它们注册负载(内核每5秒计算一次负载)。
考虑一下这个例子,在我的主机上,有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()另一个实现,这个实现避免了8组中的wait (这会使测试倾斜)。在这里,父级总是尝试将子级的数量保持在活动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)。尚未创建的任务不能计算为“runnable”状态,但是生成时会占用CPU时间的%sys。
因此,在您的情况下,答案可能是,无论做什么工作,都会快速地产生大量的任务(线程或进程)。
发布于 2015-02-12 13:00:05
如果负载平均值没有增加多少,那么它只是意味着您的硬件规范和要处理的任务的性质会产生良好的总体吞吐量,从而避免将它们堆积在任务队列中一段时间。
如果存在争用现象,例如平均任务复杂度太高或任务平均处理时间需要太多CPU周期,那么是的,负载平均会增加。
在我最初的回答中可能不清楚,所以我现在澄清:
荷载平均计算的精确公式为:loadvg = tasks running + tasks waiting (for cores) + tasks blocked。
您可以有一个很好的吞吐量和接近平均负载24,但在任务处理时间没有损失。另一方面,您也可能有2-4个周期任务没有足够快地完成,然后您将看到等待任务的数量(对于CPU周期)的增长,最终您将达到一个较高的负载平均值。可能发生的另一件事是让任务运行出色的同步I/O操作,然后阻塞一个核心,降低吞吐量,并使等待的任务队列增加(在这种情况下,您可能会看到iowait度量发生变化)。
https://serverfault.com/questions/667078
复制相似问题