CPU使用率が高いが、平均負荷がかなり低いという奇妙な動作が発生しています。
動作は、監視システムの次のグラフで最もよくわかります。
11:57頃に、CPU使用率は25%から75%になります。負荷平均は大幅に変更されていません。
それぞれ2つのハイパースレッドを持つ12コアのサーバーを実行します。 OSはこれを24 CPUと見なします。
CPU使用率データは、毎分_/usr/bin/mpstat 60 1
_を実行することによって収集されます。 all
行と_%usr
_列のデータを上のグラフに示します。これはCPUデータごとの平均を示していると確信していますnot「スタックされた」使用率。グラフには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/Linux
_
Linux distはRed Hat Enterprise Linux Server release 6.3 (Santiago)
です
いくつかのJava Webアプリケーションをマシンにかなり重い負荷をかけて実行します。マシンあたり100リクエスト/秒と考えます。
CPU使用率データを正しく解釈すると、CPU使用率が75%の場合、CPUが平均で75%の時間プロセスを実行していることを意味します。ただし、CPUが75%の時間ビジーである場合、負荷平均が高くなるのではないでしょうか。実行キューに2〜4個のジョブしかない場合、CPUはどのように75%ビジーになりますか?
データを正しく解釈していますか?この動作の原因は何ですか?
Matthew Ifeの回答は非常に役に立ち、正しい方向に導いてくれましたが、私たちのケースでの行動の原因はそれだけではありませんでした。今回のケースでは、マルチスレッド化されたJavaスレッドプーリングを使用するアプリケーションがあるため、実際のタスクを作成する作業が行われません。
ただし、スレッドが行う実際の作業は短命であり、IO待機または同期ホーン化の待機が含まれます。マシューが彼の回答で言及しているように、負荷平均はOSによってサンプリングされるため、短命のタスクは逃した。
動作を再現するJavaプログラムを作成しました。次のJavaクラスは、サーバーの1つで28%(650%スタック))のCPU使用率を生成します。これを実行している間、負荷平均は約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();
}
}
要約すると、理論では、アプリケーションのスレッドがアイドル状態になり、短時間の作業を実行するため、負荷平均の計算でタスクが正しくサンプリングされないのです。
少なくともLinuxでは、負荷平均とCPU使用率は実際には2つの異なるものです。負荷平均は、一定の期間にわたってカーネル実行キュー(CPU時間だけでなくディスクアクティビティも)で待機しているタスクの数の測定値です。 CPU使用率は、CPUが現在どれだけビジーであるかの尺度です。 1分間100%でペグされた単一のCPUスレッドが1分間の負荷平均に「寄与」できる最大の負荷は1です。ハイパースレッディング(4つの仮想コア)を備えた4コアCPUがすべて1分間100%の場合、 1分の負荷平均。
多くの場合、これらの2つの数値には互いに相関するパターンがありますが、それらを同じと考えることはできません。 CPU使用率がほぼ0%の高負荷になる可能性があり(IOデータが待機状態でスタックしている場合など)、1および100%CPUの負荷がかかる可能性があります、シングルスレッドプロセスがフルティルトで実行されている場合。また、短時間の間、CPUが100%に近いことを確認できますが、平均メトリックがまだ「追いついていない」ため、負荷はまだ1未満です。
サーバーの負荷が15,000を超え(実際にはタイプミスではありません)、CPU使用率が0%に近いことを確認しました。これは、Samba共有に問題があり、多くのクライアントがIO待ち状態)でスタックし始めたために発生しました。対応するCPUアクティビティがない通常の高負荷数が表示されている可能性があります。仮想マシンでは、同じVM Host。上のストレージリソースをめぐって激しく競合している他のVMがあることも意味します。
高負荷は必ずしも悪いことではありません。ほとんどの場合、システムが最大限の容量で使用されているか、おそらくシステムの負荷を超えていることを意味します(負荷数がプロセッサコアの数よりも大きい場合)。私が以前システム管理者だった場所に、プライマリシステムの平均負荷をNagiosよりも近く見ている人がいました。負荷が高いと、SMTPと言うよりも24時間年中早く電話がかかってきます。ほとんどの場合、実際には何も問題はありませんでしたが、彼らは負荷数を何か問題に関連付け、それを鷹のように見ていました。チェックした後、私の反応は通常、システムがちょうどそれをやっているということでした。もちろん、これは負荷が15000を超えたのと同じ場所でした(ただし、同じサーバーではありません)。システムの目的を考慮する必要があります。それが主力の場合は、負荷が自然に高くなることを期待してください。
負荷は非常に不正な数値です。一粒の塩と一緒に服用してください。
非常に迅速に連続して多くのタスクを生成すると、実行キュー内のプロセスの数が少なすぎて、それらの負荷を登録できません(カーネルは5秒ごとに1回負荷をカウントします)。
この例を考えてみてください。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までカウント)を実行するよりも多くの時間を子プロセスの作成に費やしているためです。まだ作成されていないタスクは、「実行可能」状態にカウントできませんが、スポーンされるときにCPU時間で%sysを占有します。
したがって、答えは、実際には、実行されている作業が多数のタスク(スレッド、またはプロセス)を連続して大量に発生させるというケースにある可能性があります。
負荷平均がそれほど増加しない場合は、ハードウェアの仕様と処理されるタスクの性質により、全体的なスループットが良好になり、しばらくの間タスクキューに蓄積されないようにします。
たとえば、平均的なタスクの複雑さが高すぎる、またはタスクの平均処理時間が非常に多くのCPUサイクルを必要とするために競合現象が発生した場合、はい、負荷平均が増加します。
更新:
それは私の元の答えでは明確ではないかもしれないので、私は今明確にしています:
負荷平均計算の正確な式は次のとおりです:loadvg = tasks running + tasks waiting (for cores) + tasks blocked
。
確かに優れたスループットが得られ、24の負荷平均に近づくことができますが、タスクの処理時間を犠牲にすることはありません。一方、2〜4個の定期的なタスクが十分に速く完了しない場合も、待機中のタスク(CPUサイクル)の数が増え、最終的に高い負荷平均に達します。発生する可能性のあるもう1つのことは、未処理の同期I/O操作を実行しているタスクでコアをブロックし、スループットを低下させ、待機中のタスクキューを拡大することです(その場合、iowait
メトリックが変化することがあります)。
負荷平均には、ディスクIOでブロックされるタスクが含まれるため、非常に遅いディスクからすべてのタスクを読み取ろうとするだけで、CPU使用率がゼロになり、負荷平均が10になります。したがって、ビジー状態のサーバーがディスクのスラッシングを開始することは一般的であり、すべてのシークにより多くのブロックされたタスクが発生し、負荷平均が上昇しますが、CPUの使用率は低下します。
負荷平均は、CPUキュー内の平均プロセス数です。これはシステムごとに固有であり、1つのLAがすべてのシステムで一般的に高く、別のLAが低いとは言えません。したがって、コアは12個あり、LAが大幅に増加するには、プロセス数が非常に多くなる必要があります。
もう1つの質問は、「CPU使用率」グラフの意味です。 SNMPから取得した場合のように、SNMP実装はnet-snmp
、12の各CPUからのCPU負荷をスタックするだけです。 net-snmp
CPU負荷の総量は1200%です。
私の仮定が正しければ、CPU使用率は大幅に増加していません。したがって、LAは大幅に増加しませんでした。
ここでのシナリオは、少し変わっていますが、特に予期せぬものではありません。 Xavierが触れているがあまり開発されていないのは、Linux(デフォルト)およびほとんどの種類のUnixがプリエンプティブマルチタスクを実装しているにもかかわらず、正常なマシンではタスクがプリエンプトされることはほとんどないということです。各タスクには、CPUを占有するためのタイムスライスが割り当てられます。この時間を超え、実行を待機している他のタスクがある場合にのみ、タスクが横取りされます(負荷は、CPUと実行を待機しているプロセスの両方の平均数を報告することに注意してください)。 。ほとんどの場合、プロセスは中断されるのではなく生成されます。
(一般的に、CPUの数が近くなったとき、つまりスケジューラがタスクを横取りするときに、負荷について心配する必要があるだけです)。
私たちのCPUが75%の時間ビジーである場合、負荷平均が高くなるのではないでしょうか?
アクティビティのパターンについてのすべては、明らかにいくつかのタスク(おそらく小さなミント)によるCPUの使用率の増加は、他のタスクの処理に悪影響を与えていませんでした。処理中のトランザクションを分離できれば、減速中に新しいグループが出現し、既存のタスクセットには影響がなかったと思います。
更新
負荷を大幅に増加せずにCPU使用率が高くなる一般的なシナリオの1つは、タスクが他のタスクの1つ(またはシーケンス)をトリガーする場合です。ネットワークリクエストを受信すると、ハンドラーがリクエストを別のスレッドにルーティングし、別のスレッドが他のプロセスに非同期呼び出しをいくつか行います...実行キューのサンプリングにより、負荷が実際よりも低く報告されます-しかし、それはCPU使用率に比例して上昇しません-トリガーされたタスクのチェーンは、初期イベントなしでは実行できませんでした。これらのタスクは(多かれ少なかれ)連続して発生するため、実行キューは拡張されません。