เรากำลังพบกับพฤติกรรมแปลก ๆ ที่เราเห็นการใช้งาน CPU สูง แต่มีค่าเฉลี่ยการโหลดค่อนข้างต่ำ

พฤติกรรมนี้แสดงให้เห็นได้ดีที่สุดด้วยกราฟต่อไปนี้จากระบบการตรวจสอบของเรา

การใช้งาน CPU และโหลด

เมื่อเวลาประมาณ 11:57 น. การใช้งาน CPU จะเพิ่มขึ้นจาก 25% เป็น 75% ค่าเฉลี่ยการโหลดไม่เปลี่ยนแปลงอย่างมีนัยสำคัญ

เรารันเซิร์ฟเวอร์ที่มี 12 คอร์ แต่ละคอร์มีไฮเปอร์เธรด 2 ตัว ระบบปฏิบัติการมองว่าสิ่งนี้เป็นซีพียู 24 ตัว

ข้อมูลการใช้งาน CPU จะถูกรวบรวมโดยการรัน/usr/bin/mpstat 60 1ในแต่ละนาที ข้อมูลสำหรับallแถวและ%usrคอลัมน์จะแสดงในแผนภูมิด้านบน ฉันแน่ใจว่านี่แสดงค่าเฉลี่ยต่อข้อมูล CPU ไม่ใช่การใช้งานแบบ "ซ้อน" ในขณะที่เราเห็นการใช้งาน 75% ในแผนภูมิเราเห็นกระบวนการที่แสดงให้ใช้ CPU "ซ้อนกัน" ประมาณ 2,000% ใน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 สองสามตัวภายใต้ภาระงานที่ค่อนข้างหนักบนเครื่อง คิด 100 คำขอ/วินาทีต่อเครื่อง

หากฉันตีความข้อมูลการใช้ CPU อย่างถูกต้อง เมื่อเรามีการใช้งาน CPU 75% หมายความว่า CPU ของเรากำลังดำเนินการตามกระบวนการโดยเฉลี่ย 75% ของเวลา อย่างไรก็ตาม หาก CPU ของเรามีงานยุ่งถึง 75% ของเวลา เราไม่ควรจะเห็นค่าเฉลี่ยโหลดที่สูงขึ้นหรือไม่ ซีพียูจะยุ่ง 75% ได้อย่างไรในขณะที่เรามีงานเพียง 2-4 งานในคิวการรัน?

เราตีความข้อมูลของเราถูกต้องหรือไม่? อะไรทำให้เกิดพฤติกรรมนี้

answer

อย่างน้อยบน Linux ค่าเฉลี่ยโหลดและการใช้งาน CPU เป็นสองสิ่งที่แตกต่างกัน ค่าเฉลี่ยการโหลดคือการวัดจำนวนงานที่รออยู่ในคิวการรันเคอร์เนล (ไม่ใช่แค่เวลา CPU แต่ยังรวมถึงกิจกรรมของดิสก์ด้วย) ในช่วงเวลาหนึ่ง การใช้งาน CPU เป็นตัววัดว่า CPU ยุ่งแค่ไหนในตอนนี้ โหลดมากที่สุดที่เธรด CPU เดียวตรึงที่ 100% เป็นเวลาหนึ่งนาทีสามารถ "สนับสนุน" ในการโหลดเฉลี่ย 1 นาทีคือ 1 CPU 4 คอร์ที่มีไฮเปอร์เธรดดิ้ง (8 คอร์เสมือน) ทั้งหมดที่ 100% เป็นเวลา 1 นาทีจะส่งผลให้ 8 ค่าเฉลี่ยโหลด 1 นาที

บ่อยครั้งตัวเลขสองตัวนี้มีรูปแบบที่สัมพันธ์กัน แต่คุณไม่สามารถมองว่ามันเหมือนกันได้ คุณสามารถมีภาระงานสูงโดยมีการใช้งาน CPU เกือบ 0% (เช่น เมื่อคุณมีข้อมูล IO จำนวนมากติดอยู่ในสถานะรอ) และคุณสามารถโหลด CPU 1 และ 100% เมื่อคุณมีกระบวนการเธรดเดียวที่ทำงานอยู่ เอียงเต็ม นอกจากนี้ ในช่วงเวลาสั้นๆ คุณจะเห็น CPU ที่เกือบ 100% แต่โหลดยังต่ำกว่า 1 เนื่องจากเมตริกเฉลี่ยยังไม่ "ทัน"

ฉันเคยเห็นเซิร์ฟเวอร์โหลดมากกว่า 15,000 (ใช่แล้ว นั่นไม่ใช่การพิมพ์ผิด) และ CPU % เกือบ 0% เกิดขึ้นเนื่องจากการแชร์ของ Samba มีปัญหา และลูกค้าจำนวนมากเริ่มติดค้างอยู่ในสถานะรอ IO เป็นไปได้มากว่าถ้าคุณเห็นหมายเลขโหลดสูงเป็นประจำโดยไม่มีกิจกรรม CPU ที่ตรงกัน แสดงว่าคุณกำลังมีปัญหาด้านพื้นที่เก็บข้อมูลบางประเภท บนเครื่องเสมือน นี่อาจหมายความว่ามี VM อื่นๆ แข่งขันกันอย่างหนักเพื่อทรัพยากรการจัดเก็บข้อมูลบนโฮสต์ VM เดียวกัน

ภาระที่สูงไม่จำเป็นต้องเป็นสิ่งที่ไม่ดี ส่วนใหญ่ก็หมายความว่าระบบกำลังถูกใช้งานอย่างเต็มความสามารถหรืออาจเกินความสามารถในการติดตาม (หากจำนวนการโหลดสูงกว่าจำนวนคอร์ของโปรเซสเซอร์) ในสถานที่ที่ฉันเคยเป็นผู้ดูแลระบบ พวกเขามีคนที่คอยดูค่าเฉลี่ยโหลดบนระบบหลักของตนที่ใกล้กว่าที่ Nagios เคยทำ เมื่อภาระงานสูง พวกเขาจะโทรหาฉันเร็วกว่าที่คุณพูดได้ตลอด 24 ชั่วโมงทุกวันตลอด 24 ชั่วโมง ส่วนใหญ่แล้วไม่มีอะไรผิดปกติจริง ๆ แต่พวกเขาเชื่อมโยงหมายเลขโหลดกับสิ่งผิดปกติและมองมันเหมือนเหยี่ยว หลังจากตรวจสอบแล้ว คำตอบของฉันมักจะเป็นว่าระบบทำงานตามปกติ แน่นอนว่านี่คือที่เดียวกับที่โหลดเพิ่มขึ้นมากกว่า 15,000 (ไม่ใช่เซิร์ฟเวอร์เดียวกัน) ดังนั้นบางครั้งมันก็หมายความว่ามีบางอย่างผิดปกติคุณต้องพิจารณาถึงวัตถุประสงค์ของระบบของคุณ หากเป็นม้าหมุน ให้คาดหวังให้น้ำหนักบรรทุกสูงโดยธรรมชาติ

โหลดเป็นตัวเลขที่หลอกลวงมาก เอาไปกับเม็ดเกลือ

หากคุณวางไข่งานจำนวนมากอย่างรวดเร็วต่อเนื่องกันซึ่งเสร็จสิ้นอย่างรวดเร็ว จำนวนกระบวนการในคิวการรันจะน้อยเกินไปที่จะลงทะเบียนการโหลดสำหรับพวกเขา (เคอร์เนลจะนับโหลดทุกๆ ห้าวินาที)

ลองพิจารณาตัวอย่างนี้ บนโฮสต์ของฉันซึ่งมีแกนตรรกะ 8 คอร์ สคริปต์หลามนี้จะลงทะเบียนการใช้งาน 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");
  }
}

สาเหตุของพฤติกรรมนี้คืออัลกอริธึมใช้เวลาในการสร้างกระบวนการย่อยมากกว่าที่รันงานจริง (นับถึง 10,000) งานที่ยังไม่ได้สร้างไม่สามารถนับรวมในสถานะ 'รันได้' แต่จะใช้เวลา %sys ของเวลา CPU เมื่อมีการเกิด

ดังนั้น คำตอบอาจเป็นกรณีของคุณจริงๆ ว่างานใดก็ตามที่ทำอยู่ทำให้เกิดงานจำนวนมากติดต่อกันอย่างรวดเร็ว (เธรด หรือกระบวนการ)

หากค่าเฉลี่ยการโหลดไม่เพิ่มขึ้นมาก ก็หมายความว่าข้อกำหนดฮาร์ดแวร์ของคุณและลักษณะของงานที่จะประมวลผลส่งผลให้มีปริมาณงานโดยรวมที่ดี หลีกเลี่ยงการซ้อนในคิวงานในบางครั้ง

หากมีปรากฏการณ์การโต้แย้งเนื่องจากตัวอย่างเช่น ความซับซ้อนของงานโดยเฉลี่ยสูงเกินไปหรือเวลาประมวลผลโดยเฉลี่ยของงานใช้รอบ CPU มากเกินไป ใช่แล้ว ค่าเฉลี่ยของโหลดจะเพิ่มขึ้น

อัปเดต :

อาจไม่ชัดเจนในคำตอบเดิมของฉัน ดังนั้นฉันจึงชี้แจงตอนนี้:

loadvg = tasks running + tasks waiting (for cores) + tasks blockedสูตรที่แน่นอนของการคำนวณค่าเฉลี่ยโหลด:

คุณสามารถมีปริมาณงานที่ดีและใกล้เคียงกับค่าเฉลี่ยของโหลดที่ 24 แต่ไม่ต้องเสียค่าปรับสำหรับเวลาในการประมวลผลงาน ในทางกลับกัน คุณสามารถมีงานเป็นระยะ 2-4 งานที่ไม่เสร็จเร็วพอ จากนั้นคุณจะเห็นจำนวนงานที่รอ (สำหรับรอบ CPU) เพิ่มขึ้น และในที่สุดคุณจะถึงค่าเฉลี่ยโหลดที่สูง อีกสิ่งหนึ่งที่สามารถเกิดขึ้นได้คือการมีงานที่รันการดำเนินการ I/O แบบซิงโครนัสที่โดดเด่น จากนั้นบล็อกคอร์ ลดปริมาณงาน และทำให้คิวงานที่รอเพิ่มขึ้น (ในกรณีนี้ คุณอาจเห็นการiowaitเปลี่ยนแปลงของตัววัด)

แม้ว่าคำตอบของ Matthew Ife จะมีประโยชน์มากและนำเราไปสู่ทิศทางที่ถูกต้อง แต่ก็ไม่ใช่สาเหตุที่ทำให้เกิดพฤติกรรมในกรณีของเราอย่างแน่นอน ในกรณีของเรา เรามีแอปพลิเคชัน Java แบบหลายเธรดที่ใช้การรวมเธรด เหตุใดจึงไม่สร้างงานจริงจนเสร็จ

อย่างไรก็ตาม งานจริงที่เธรดทำนั้นมีอายุสั้นและรวมถึงการรอ IO หรือการรอการซิงโครไนซ์ ตามที่แมทธิวกล่าวถึงในคำตอบของเขา ระบบปฏิบัติการจะสุ่มตัวอย่างค่าเฉลี่ยโหลด ดังนั้นจึงอาจพลาดงานที่มีอายุสั้นได้

ฉันสร้างโปรแกรม Java ที่จำลองพฤติกรรม คลาส Java ต่อไปนี้สร้างการใช้งาน CPU 28% (ซ้อนกัน 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();
    }

}

โดยสรุป ทฤษฎีคือเธรดในแอปพลิเคชันของเราไม่ได้ใช้งานเป็นจำนวนมากแล้วจึงทำงานที่มีอายุสั้น เหตุใดจึงไม่ได้สุ่มตัวอย่างงานอย่างถูกต้องโดยการคำนวณค่าเฉลี่ยโหลด

ค่าเฉลี่ยโหลดประกอบด้วยงานที่ถูกบล็อกบนดิสก์ IO ดังนั้นคุณจึงสามารถใช้ cpu ได้เป็นศูนย์และโหลดเฉลี่ย 10 อย่างง่ายดาย เพียงแค่มี 10 งานทั้งหมดพยายามอ่านจากดิสก์ที่ช้ามาก ดังนั้นจึงเป็นเรื่องปกติที่เซิร์ฟเวอร์ที่มีงานยุ่งจะเริ่มฟาดดิสก์และการค้นหาทั้งหมดทำให้เกิดงานที่ถูกบล็อกจำนวนมาก เพิ่มค่าเฉลี่ยโหลด ในขณะที่การใช้งาน cpu ลดลง เนื่องจากงานทั้งหมดถูกบล็อกบนดิสก์

ค่าเฉลี่ยการโหลดคือจำนวนกระบวนการเฉลี่ยในคิว CPU มีความเฉพาะเจาะจงสำหรับแต่ละระบบ คุณไม่สามารถพูดได้ว่า LA หนึ่งโดยทั่วไปสูงในทุกระบบ และอีกอันหนึ่งอยู่ในระดับต่ำ ดังนั้น คุณมี 12 คอร์ และเพื่อให้ LA เพิ่มขึ้นอย่างมาก จำนวนกระบวนการต้องสูงมาก

อีกคำถามคือความหมายของกราฟ "การใช้งาน CPU" หากนำมาจาก SNMP อย่างที่ควรจะเป็น และการใช้งาน SNMP ของคุณคือnet-snmpให้โหลด CPU กองจาก CPU 12 ตัวแต่ละตัวของคุณ ดังนั้นสำหรับnet-snmpจำนวนโหลด CPU ทั้งหมดคือ 1200%

หากสมมติฐานของฉันถูกต้อง แสดงว่าการใช้งาน CPU ไม่ได้เพิ่มขึ้นอย่างมาก ดังนั้น LA จึงไม่เพิ่มขึ้นอย่างมีนัยสำคัญ

สถานการณ์ที่นี่ไม่ได้คาดไม่ถึงแม้ว่าจะเป็นเรื่องผิดปกติเล็กน้อย สิ่งที่ Xavier สัมผัสแต่ไม่ได้พัฒนามากนักคือแม้ว่า Linux (โดยค่าเริ่มต้น) และ Unix ส่วนใหญ่จะใช้การทำงานหลายอย่างพร้อมกันแบบ pre-emptive บนเครื่องที่มีสุขภาพที่ดี งานต่างๆ มักไม่ค่อยถูกจองไว้ล่วงหน้า แต่ละงานจะได้รับการแบ่งเวลาสำหรับการครอบครอง CPU ซึ่งจะถูกจองไว้ล่วงหน้าก็ต่อเมื่อเกินเวลานี้และมีงานอื่นๆ ที่รอดำเนินการอยู่ (โปรดทราบว่าการโหลดจะรายงานจำนวนเฉลี่ยของกระบวนการทั้งใน CPU และรอดำเนินการ) . โดยส่วนใหญ่ กระบวนการจะยอมให้เกิดขึ้นแทนที่จะถูกขัดจังหวะ

(โดยทั่วไปคุณจะต้องกังวลเกี่ยวกับการโหลดเมื่อใกล้ถึงจำนวนซีพียู - เช่นเมื่อตัวจัดกำหนดการเริ่มงาน pre-empting)

if our CPUs are busy 75% of the time, shouldn't we see higher load average?

ทั้งหมดเกี่ยวกับรูปแบบของกิจกรรม การใช้งาน CPU ที่เพิ่มขึ้นอย่างชัดเจนโดยงานบางอย่าง (น่าจะเป็นกลุ่มย่อยเล็กๆ) ไม่ได้ส่งผลเสียต่อการประมวลผลงานอื่นๆ หากคุณสามารถแยกธุรกรรมที่กำลังดำเนินการได้ ฉันคาดว่าคุณจะเห็นกลุ่มใหม่เกิดขึ้นระหว่างการชะลอตัว ในขณะที่ชุดงานที่ยังหลงเหลืออยู่จะไม่ได้รับผลกระทบ

อัปเดต

สถานการณ์ทั่วไปอย่างหนึ่งที่ CPU สูงสามารถเกิดขึ้นได้โดยไม่มีการโหลดเพิ่มขึ้นมากคือเมื่องานเรียกใช้งานอื่น (หรือเป็นลำดับ) เช่น เมื่อได้รับคำขอเครือข่าย ตัวจัดการจะกำหนดเส้นทางคำขอไปยังเธรดที่แยกจากกัน เธรดที่แยกจากกัน จากนั้นทำการเรียกแบบอะซิงโครนัสไปยังกระบวนการอื่น ๆ .... การสุ่มตัวอย่างของรันคิวทำให้การโหลดรายงานต่ำกว่าที่เป็นอยู่จริง - แต่มันจะไม่เพิ่มขึ้นเชิงเส้นตามการใช้งาน CPU - ห่วงโซ่ของงานที่ทริกเกอร์จะไม่สามารถรันได้หากไม่มี เหตุการณ์เริ่มต้น และเนื่องจากเกิดขึ้น (มากหรือน้อย) ตามลำดับ คิวการรันจึงไม่พอง

ก่อนอื่น คำตอบสั้น ๆ สำหรับคำถาม: เห็นได้ชัดว่าตั้งแต่ 12 ถึง 12:05 น. กระบวนการที่ประมวลผลโดย CPU ใช้เวลาในการประมวลผลนานกว่าที่เคยเกิดขึ้น

ตั้งแต่ 11 ถึง 11:55 น. ทุกกระบวนการของ OS ใช้เวลา CPU 25 มิลลิวินาที (ตัวอย่าง)

ตั้งแต่ 12 ถึง 12:05 น. ทุกกระบวนการของระบบปฏิบัติการใช้เวลา 75 มิลลิวินาที

นั่นเป็นสาเหตุที่ค่าเฉลี่ยการโหลดไม่เปลี่ยนแปลง แต่การใช้งาน CPU เปลี่ยนไปมาก

คำตอบที่ยาวเหยียด: การใช้งาน CPU และค่าเฉลี่ยโหลด อธิบายสถานะของสิ่งมีชีวิตที่แตกต่างกันสองตัว

การใช้ CPU อธิบายความสมบูรณ์ของ CPU

ค่าเฉลี่ยการโหลดไม่มีอะไรเหมือนกับ CPU

ดังนั้นจึงค่อนข้างไม่เหมาะสมเมื่อใช้ค่าเฉลี่ยโหลดเพื่อค้นหาความยุ่งหรือว่างของ CPU

มันเหมือนกับการพยายามค้นหาว่าคนๆ หนึ่งจะได้เงินเท่าไหร่ผ่านการพยากรณ์อากาศ

โหลดเฉลี่ยอธิบายกระบวนการใน Linux OS ไม่ใช่สถานะ CPU

การใช้งาน CPU อธิบายเวลาที่ CPU ทำบางสิ่งแทนที่จะไม่ทำอะไรเลยในช่วงเวลาหนึ่ง เพื่อความง่ายใน 1 วินาที

หากการใช้งาน CPU = 85% แสดงว่า CPU 85ms ไม่ว่างและ 15ms ไม่ได้ใช้งาน แค่นั้นแหละ.

การใช้งาน CPU ค่อนข้างคล้ายกับลักษณะเวลาว่างของ HDD %

โหลดเฉลี่ย = 125 เป็นเวลา 1 วินาที หมายความว่า 125 โปรเซสถูกประมวลผลโดย CPU หรือรอดำเนินการหรือรอระบบ hdd

มันซับซ้อนดังนั้นจึงง่ายต่อการเข้าใจจุดที่คิดว่า 125 กระบวนการถูกประมวลผลโดย CPU ประเด็นคือเราไม่รู้ว่าทุกกระบวนการทำงานบน CPU นานเท่าใด เราเพิ่งรู้ว่าพวกเขากำลังวิ่งในเวลาที่ไม่รู้จัก

ดังนั้นสำหรับความคิดเห็นของฉัน โหลดเฉลี่ย ทำให้เกิดความสับสนและเป็นอันตรายอย่างมากเมื่อเราพยายามทำความเข้าใจประสิทธิภาพมากกว่าที่จะทำสิ่งที่มีประโยชน์

เมื่อเราดูที่กราฟเริ่มต้น เราจะเห็นว่าไม่มีความสัมพันธ์ระหว่างการใช้งาน CPU และค่าเฉลี่ยโหลดตลอดช่วงเวลา มันเหมือนกับการพยายามค้นหาความสัมพันธ์ระหว่างการพยากรณ์อากาศกับสีของถ้วยน้ำชาของคุณ