原因总结原因总结一句话:等待磁盘I/O完成的进程太多,导致进程队列过长,但cpu上运行的进程却很少,反映负载过大,和cpu使用率低。以下内容为具体原理分析:在分析为什么负载高之前,先介绍一下什么是负载、多任务操作系统、进程调度等相关概念。什么是负载?什么是负载:负载是一段时间内CPU正在处理和等待CPU处理的进程数之和的统计信息,即CPU使用时长的统计信息队列。数字越小越好(如果超过CPU核心*0.7就不正常了)负载分为两部分:CPU负载和IO负载。例如,假设有一个用于大规模科学计算的程序。程序虽然不频繁地从磁盘输入输出,但完成处理需要很长时间。时间。因为程序主要用于计算、逻辑判断等处理,所以程序的处理速度主要取决于CPU的计算速度。这种cpu加载的程序被称为“计算密集型程序”。还有一类程序可以从存储在磁盘上的大量数据中搜索任意文件。这个搜索程序的处理速度不依赖于cpu,而是依赖于磁盘的读取速度,即输入/输出(I/O)。磁盘越快,检索时间越短。具有这种I/O负载的程序称为“I/O密集型程序”。什么是多任务操作系统Linux操作系统能够同时处理多个不同名称的任务。但是在同时运行多个任务的过程中,这些任务程序需要共享有限的cpu、磁盘等硬件资源。即使在很短的时间间隔内,也需要在这些任务之间切换并同时处理它们。这是多任务处理。在运行任务较少的情况下,系统不会等待这种切换动作的发生。但是当任务增加的时候,比如任务A在CPU上进行计算,那么如果任务B和C也想进行计算,那么就需要等待CPU空闲。换句话说,即使它正在运行处理某个任务,也不能在轮到他之前运行。这种等待状态表现为程序运行延迟。uptime[root@localhost~]#uptime11:16:38up2:06,4users,loadaverage:0.00,0.02,0.05输出中包含“loadaverage”的数字为过去1分钟、5分钟、15分钟左边,单位时间内等待的任务数,即平均有多少个任务处于等待状态。当loadaverage高的时候,意味着有很多任务等待运行,所以任务运行的等待时间会有很大的延迟,体现了此时的高负载。进程调度什么是进程调度:进程调度也有人称之为cpu上下文切换,意思是:CPU切换到另一个进程需要保存当前进程的状态,恢复另一个进程的状态:当前运行的任务变为就绪状态(或暂停)、中断)状态,另一个被选中的就绪任务成为当前任务。进程调度包括保存当前任务的运行环境和恢复待运行任务的运行环境。在Linux内核中,每个进程都有一个名为“进程描述符”的管理表。进程描述符会被调整为优先级降序排列,进程(任务)已经按照合理的顺序运行。这种调整是进程调度程序的工作。调度器对进程的状态进行划分和管理,如:等待分配cpu资源的状态。等待磁盘输入输出完成的状态。下面说说进程的状态差异:下面举例说明进程的状态转换:有A、B、C三个进程同时运行。首先,每个进程在生成后都处于runnable状态,这是running的开始状态,而不是当前的running状态。由于在linux内核中无法区分running状态和runnable等待状态,所以下面将runnable状态和runnable状态统称为running状态。进程A:正在运行进程B:正在运行进程C:正在运行正在运行的三个进程立即成为调度对象。此时,假设调度器给进程A分配了CPU执行权限。进程A:正在运行进程B:正在运行进程C:正在运行进程A被分配了CPU,于是进程A开始处理。进程B和C在这里等待进程A移出CPU。假设进程A在执行一些计算后需要从磁盘读取数据。那么A发出读取磁盘数据的请求后,直到请求的数据到达,才会进行任何工作。这种状态称为“阻塞等待I/O操作完成”。在I/O处理之前,进程A一直在等待,它会转入不可中断的休眠状态(uninterruptible),不占用CPU。于是调度器检查进程B和进程C的优先级计算结果,将CPU执行权限交给优先级高的一方。这里假设进程B的优先级高于进程C进程A:不可中断(等待磁盘输入输出/不可中断状态)进程B:正在运行(running)进程C:正在运行进程B刚刚开始运行,需要等待用户的键盘输入。于是B进入等待用户键盘输入的状态,也被阻塞。结果变成A进程和B进程都在等待输出,运行C进程。此时A进程和B进程都处于等待状态,但是等待磁盘输入输出和等待键盘输入是不同的状态.等待键盘输入是一个无限等待的事件,而读取磁盘是一个必须在短时间内完成的事件等待。这是两种不同的等待状态。各个进程的状态如下:进程A:不可中断(等待磁盘输入输出/不可中断状态)进程B:可中断(等待键盘输入输出/可中断状态)进程C:正在运行(running)这次是假设进程C在运行过程中,进程A请求的数据从磁盘到达缓冲设备。紧接着,硬盘向内核发出中断信号,内核知道磁盘读取完成,将进程A恢复到可运行状态。进程A:正在运行(running)进程B:可中断(等待键盘输入输出/可中断状态)进程C:正在运行(running)之后,进程C也会变成某种等待状态。比如CPU使用时间超过上限,任务结束,I/O等待。一旦满足这些条件,调度器就可以完成从进程C到进程A的进程状态切换。负载的含义负载代表“平均等待进程数”。在上面的进程状态变化过程中,除了运行状态外,其他状态都是等待状态,那么加载等待过程中会不会加入其他状态呢?事实证明,只有当进程处于运行状态(running)和不可中断状态(interruptible)时,才会被加入负载等待进程,即以下两种情况下的进程才会显示负载值.即使你需要立即使用CPU,你仍然需要等待其他进程用完CPU。即使需要继续处理,也必须等待磁盘输入输出完成后才能继续。下面描述一个直观的场景来解释为什么只有运行状态和可中断状态(interruptible)才会被添加到负载中。例如:在CPU资源消耗比较大的处理中,比如在动画编码的过程中,虽然想进行其他同类型的处理,但是结果系统响应变的很慢,而且当读取量很大的时候来自磁盘的数据,系统的响应也会变得很慢。但另一方面,无论有多少进程在等待键盘输入输出操作,都不会减慢系统响应。什么场景会导致CPU低但负载真的很高?通过上面的具体分析,负载的含义就很明显了。负载可以用一句话来概括:需要运行处理但必须等待队列前的进程完成处理的进程数。具体是以下两种情况:进程等待授权运行CPU的进程和等待磁盘I/O完成CPU低,负载高,意味着等待的进程太多磁盘I/O来完成,这会导致队列长度过大,也就是负载过大,但实际上此时cpu被分配去执行其他任务或者空闲了。具体场景如下。场景一:磁盘读写请求过多,会导致大量I/O等待。上面说了CPU的工作效率高于磁盘,运行在CPU上的进程需要访问磁盘文件。这时CPU会向内核发起文件调用。请求,让内核去磁盘取文件。这时候就会切换到其他进程或者空闲,这个任务就会切换到不可中断的休眠状态。当这样的读写请求过多时,处于不可中断睡眠状态的进程就会过多,导致负载高,cpu低。场景二:MySQL中存在没有索引或者死锁的语句。我们都知道MySQL的数据是存储在硬盘上的。如果需要执行SQL查询,需要先将数据从磁盘加载到内存中。当数据特别大的时候,如果执行的SQL语句没有索引,会因为扫描的表行数太多,造成I/O阻塞,或者语句死锁也会造成I/O阻塞,导致有不可中断休眠进程过多,造成负载过大。作为具体的解决方案,可以在MySQL中使用showfullprocesslist命令查看线程等待情况,将其中的语句取出来进行优化。场景三:外置硬盘故障,通常是连接NFS,但NFS服务器故障。比如我们的系统挂载了NFS共享存储这样的外置硬盘,经常会出现大量的读写请求访问存储在NFS中的文件。如果服务器出现故障,进程的读写请求将无法获取资源,进程将一直处于不可中断状态,导致负载过高。结论:大概就是这个内容。如果大家遇到其他场景,欢迎留言补充。
