Lösung 1:
Zumindest unter Linux sind der Lastdurchschnitt und die CPU-Auslastung eigentlich zwei verschiedene Dinge. Der Lastdurchschnitt ist ein Maß dafür, wie viele Aufgaben in einer Kernel-Ausführungswarteschlange (nicht nur CPU-Zeit, sondern auch Festplattenaktivität) über einen bestimmten Zeitraum warten. Die CPU-Auslastung ist ein Maß dafür, wie ausgelastet die CPU gerade ist. Die größte Last, die ein einzelner CPU-Thread, der eine Minute lang auf 100 % festgelegt ist, zum Lastdurchschnitt von 1 Minute "beitragen" kann, ist 1. Eine 4-Kern-CPU mit Hyperthreading (8 virtuelle Kerne), die alle 1 Minute lang bei 100 % liegen, würde 8 dazu beitragen der Ladedurchschnitt von 1 Minute.
Oft haben diese beiden Zahlen Muster, die miteinander korrelieren, aber Sie können sie nicht als gleich ansehen. Sie können eine hohe Auslastung mit nahezu 0 % CPU-Auslastung haben (z. B. wenn viele E/A-Daten in einem Wartezustand stecken bleiben) und Sie können eine Auslastung von 1 und 100 % CPU haben, wenn Sie einen Single-Thread-Prozess ausführen volle Neigung. Auch für kurze Zeiträume können Sie die CPU bei fast 100 % sehen, aber die Auslastung liegt immer noch unter 1, weil die durchschnittlichen Metriken noch nicht "aufgeholt" haben.
Ich habe einen Server mit einer Auslastung von über 15.000 (ja, das ist wirklich kein Tippfehler) und einem CPU-Prozentsatz von fast 0 % gesehen. Es geschah, weil eine Samba-Freigabe Probleme hatte und viele, viele Clients anfingen, in einem IO-Wartezustand festzustecken. Wenn Sie regelmäßig eine hohe Lastnummer ohne entsprechende CPU-Aktivität sehen, haben Sie wahrscheinlich ein Speicherproblem. Auf virtuellen Maschinen kann dies auch bedeuten, dass andere VMs stark um Speicherressourcen auf demselben VM-Host konkurrieren.
Hohe Last ist auch nicht unbedingt eine schlechte Sache, sondern bedeutet meistens nur, dass das System voll ausgelastet ist oder möglicherweise nicht mehr in der Lage ist, mitzuhalten (wenn die Lastzahl höher ist als die Anzahl der Prozessorkerne). An einem Ort, an dem ich früher Systemadministrator war, hatten sie jemanden, der den Lastdurchschnitt auf ihrem primären System genauer beobachtete als Nagios. Wenn die Last hoch war, riefen sie mich rund um die Uhr schneller an, als man SMTP sagen konnte. Meistens war eigentlich nichts falsch, aber sie verbanden die Ladungsnummer mit etwas, das nicht stimmte, und beobachteten es mit Argusaugen. Nach der Überprüfung war meine Antwort normalerweise, dass das System nur seine Arbeit erledigte. Natürlich war dies derselbe Ort, an dem die Last über 15000 stieg (allerdings nicht derselbe Server), also bedeutet es manchmal, dass etwas nicht stimmt. Sie müssen den Zweck Ihres Systems berücksichtigen. Wenn es sich um ein Arbeitstier handelt, dann erwarten Sie eine natürlich hohe Belastung.
Lösung 2:
Load ist eine sehr irreführende Zahl. Nehmen Sie es mit Vorsicht.
Wenn Sie sehr schnell hintereinander viele Aufgaben erzeugen, die sehr schnell abgeschlossen werden, ist die Anzahl der Prozesse in der Ausführungswarteschlange zu klein, um die Last für sie zu registrieren (der Kernel zählt die Last einmal alle fünf Sekunden).
Betrachten Sie dieses Beispiel:Auf meinem Host mit 8 logischen Kernen registriert dieses Python-Skript eine große CPU-Auslastung (ca. 85 %), aber kaum Last.
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()
Eine weitere Implementierung, diese vermeidet wait
in Gruppen von 8 (was den Test verfälschen würde). Hier versucht der Elternteil immer, die Anzahl der Kinder auf der Zahl der aktiven CPUs zu halten, so dass es viel beschäftigter ist als die erste Methode und hoffentlich genauer.
/* 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");
}
}
Der Grund für dieses Verhalten ist, dass der Algorithmus mehr Zeit damit verbringt, untergeordnete Prozesse zu erstellen, als die eigentliche Aufgabe auszuführen (bis 10000 zu zählen). Noch nicht erstellte Tasks können nicht auf den Status „ausführbar“ angerechnet werden, beanspruchen jedoch %sys CPU-Zeit, wenn sie erzeugt werden.
Die Antwort könnte also in Ihrem Fall wirklich lauten, dass jede Arbeit, die erledigt wird, eine große Anzahl von Aufgaben in schneller Folge hervorbringt (Threads oder Prozesse).
Lösung 3:
Wenn die durchschnittliche Auslastung nicht stark ansteigt, bedeutet dies lediglich, dass Ihre Hardwarespezifikationen und die Art der zu verarbeitenden Aufgaben zu einem guten Gesamtdurchsatz führen, wodurch vermieden wird, dass sie sich für einige Zeit in der Aufgabenwarteschlange ansammeln.
Wenn es ein Konkurrenzphänomen gäbe, weil zum Beispiel die durchschnittliche Aufgabenkomplexität zu hoch ist oder die durchschnittliche Verarbeitungszeit der Aufgabe zu viele CPU-Zyklen benötigt, dann ja, die durchschnittliche Auslastung würde steigen.
AKTUALISIERUNG:
In meiner ursprünglichen Antwort ist dies möglicherweise nicht klar, daher kläre ich es jetzt:
Die genaue Formel zur Berechnung des Lastdurchschnitts lautet:loadvg = tasks running + tasks waiting (for cores) + tasks blocked
.
Sie können definitiv einen guten Durchsatz haben und sich einem Lastdurchschnitt von 24 nähern, aber ohne Einbußen bei der Verarbeitungszeit von Aufgaben. Auf der anderen Seite können auch 2-4 periodische Tasks nicht schnell genug abgeschlossen werden, dann wird die Anzahl der wartenden Tasks (auf CPU-Zyklen) wachsen und Sie werden schließlich einen hohen Lastdurchschnitt erreichen. Eine andere Sache, die passieren kann, ist, dass Aufgaben ausstehende synchrone E/A-Operationen ausführen und dann einen Kern blockieren, den Durchsatz verringern und die Warteschlange für wartende Aufgaben wachsen lassen (in diesem Fall sehen Sie möglicherweise die iowait
metrische Änderung)
Lösung 4:
Obwohl die Antwort von Matthew Ife sehr hilfreich war und uns in die richtige Richtung führte, war sie nicht genau die Ursache für das Verhalten in unserem Fall. In unserem Fall haben wir eine Java-Anwendung mit mehreren Threads, die Thread-Pooling verwendet, weshalb keine Arbeit zum Erstellen der eigentlichen Aufgaben geleistet wird.
Die tatsächliche Arbeit der Threads ist jedoch kurzlebig und umfasst IO-Wartezeiten oder Synchronisierungswartezeiten. Wie Matthew in seiner Antwort erwähnt, wird der Lastdurchschnitt vom Betriebssystem abgetastet, sodass kurzlebige Aufgaben übersehen werden können.
Ich habe ein Java-Programm erstellt, das das Verhalten reproduziert. Die folgende Java-Klasse generiert auf einem unserer Server eine CPU-Auslastung von 28 % (650 % gestapelt). Dabei liegt der Lastdurchschnitt bei etwa 1,3. Der Schlüssel hier ist das sleep() innerhalb des Threads, ohne das die Lastberechnung korrekt ist.
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();
}
}
Zusammenfassend lautet die Theorie, dass die Threads in unseren Anwendungen viel im Leerlauf sind und dann kurzlebige Arbeit verrichten, weshalb die Aufgaben von der Berechnung des Lastdurchschnitts nicht korrekt abgetastet werden.
Lösung 5:
Der Lastdurchschnitt umfasst Aufgaben, die auf Festplatten-E/A blockiert sind, sodass Sie problemlos eine CPU-Auslastung von null und eine durchschnittliche Last von 10 erreichen können, indem Sie 10 Aufgaben haben, die alle versuchen, von einer sehr langsamen Festplatte zu lesen. Daher ist es üblich, dass ein ausgelasteter Server anfängt, die Festplatte zu überlasten, und all das Suchen verursacht viele blockierte Aufgaben, was die durchschnittliche Auslastung erhöht, während die CPU-Auslastung sinkt, da alle Aufgaben auf der Festplatte blockiert sind.