找回密码
 立即注册
Qt开源社区 门户 查看内容

深入理解linux系统的负载

2019-7-17 20:21| 发布者: admin| 查看: 843| 评论: 0

摘要: 什么是系统平均负载(Load Average)?Load Average是CPU的Load,它所包含的信息不是CPU的使用率状况,而是在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是CPU使用队列的长度的统计信息。loa ...

什么是系统平均负载(Load Average)?


Load Average是CPU的Load,它所包含的信息不是CPU的使用率状况,而是在一段时间内CPU正在处理以及等待CPU处理的进程数之和的统计信息,也就是CPU使用队列的长度的统计信息。load average 表示机器一段时间内的平均load。这个值越低越好。负载过高会导致机器无法处理其他请求及操作,甚至导致死机。

系统平均负载被定义为在特定时间间隔内运行队列中的平均进程树。如果一个进程满足以下条件则其就会位于运行队列中:

- 它没有在等待I/O操作的结果

- 它没有主动进入等待状态(也就是没有调用'wait')

- 没有被停止(例如:等待终止)

什么影响系统负载


Linux的负载高,主要是由于CPU使用、内存使用、IO消耗三部分构成。任意一项使用过多,都将导致服务器负载的急剧攀升。

怎么查看机器负载


uptime
[root@init ~]# uptime
10:43:03 up 18 days, 12:22, 2 users, load average: 0.00, 0.01, 0.05

uptime命令能够打印系统总共运行了多长时间和系统的平均负载。uptime命令可以显示的信息显示依次为:现在时间、系统已经运行了多长时间、目前有多少登陆用户、系统在过去的1分钟、5分钟和15分钟内的平均负载。

w命令
[root@init ~]# w
10:45:06 up 18 days, 12:24, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 118.24.213.153 10:03 2.00s 0.11s 0.11s -bash
root pts/1 118.24.213.153 10:25 12:02 0.21s 0.15s vim 2019-07-16-18-24-13.txt

w命令的主要功能其实是显示目前登入系统的用户信息。但是与who不同的是,w命令功能更加强大,w命令还可以显示:当前时间,系统启动到现在的时间,登录用户的数目,系统在最近1分钟、5分钟和15分钟的平均负载。然后是每个用户的各项数据,项目显示顺序如下:登录帐号、终端名称、远 程主机名、登录时间、空闲时间、JCPU、PCPU、当前正在运行进程的命令行。

top命令
[root@init ~]# top
top - 10:46:16 up 18 days, 12:25, 2 users, load average: 0.00, 0.01, 0.05
Tasks: 201 total, 1 running, 200 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.1 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16266396 total, 338700 free, 414376 used, 15513320 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 15366108 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
27209 root 200677088170441988 S 0.7 0.1 122:16.24 barad_agent
16628 root 200571356111683112 S 0.3 0.1 3:28.59 YDService
22994 root 2001984431121840 S 0.3 0.0 9:09.03 haproxy
27207 root 2001574527528992 S 0.3 0.0 0:16.10 barad_agent
288967020018448857564156 S 0.3 0.0 0:11.83 postgres
1 root 20019218047242196 S 0.0 0.0 17:55.27 systemd
2 root 200000 S 0.0 0.0 0:00.82 kthreadd
3 root 200000 S 0.0 0.0 1:47.45 ksoftirqd/0
5 root 0 -20 000 S 0.0 0.0 0:00.00 kworker/0:0H

top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。

top命令详解

第一行:(当前系统时间) (系统以及运行时间) (系统负载 三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。)

第二行:

Tasks: 29 total 进程总数

1 running 正在运行的进程数

28 sleeping 睡眠的进程数

0 stopped 停止的进程数

0 zombie 僵尸进程数

第三行:

Cpu(s):

0.3% us 用户空间占用CPU百分比

1.0% sy 内核空间占用CPU百分比

0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比

98.7% id 空闲CPU百分比

0.0% wa 等待输入输出的CPU时间百分比

0.0% hi 硬中断占用cpu的百分比

0.0% si 软中断占用cpu百分比

%st:被虚拟机偷走的cpu

0.0% wa 的百分比可以大致的体现出当前的磁盘io请求是否频繁。如果 wa的数量比较大,说明等待输入输出的的io比较多。

第四行:

KiB Mem : 1016916 total 内存总量(我这里是1G)

free 内存空闲量

used 内存使用量

buff/cache 缓存的内存量

第五行:

KiB Swap: 0 total 交换区总量

KiB Swap: 0 total 交换区总量

0 used交换区使用量

第六行:

PID 进程号

USER 进程创建者

PR 进程优先级

NI nice值。越小优先级越高,最小-20,最大20(用户设置最大19)

VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA

SHR 共享内存大小,单位kb

S 进程状态。D=不可中断的睡眠状态 R=运行 S=睡眠 T=跟踪/停止 Z=僵尸进程

%CPU 进程占用cpu百分比
%MEM 进程占用内存百分比

TIME+ 进程运行时间

COMMAND 进程名称

PR 越低优先级 越高,PRI(new)=PRI(old)+nice PR中的rt为实时进程优先级即rt_priority,prio=MAX_RT_PRIO - 1- p->rt_priority MAX_RT_PRIO = 99,prio大小决定最终优先级。这样意味着rt_priority值越大,优先级越高而内核提供的修改优先级的函数,是修改rt_priority的值,所以越大,优先级越高。例:改变优先级:进入top后按“r”–>输入进程PID–>输入nice值

top命令(进入后使用)
P:以占据CPU百分比排序
M:以占据内存百分比排序
T:以累积占用CPU时间排序
q:退出命令:按q键退出top查看页面
s:修改刷新时间间隔。按下s键,然后按下数字,即可修改刷新时间间隔为你输入的数字,单位为秒。例如:按下s键,在按数字1键,即可实现每秒刷新一次
k:终止指定的进程。按下k键-->再输入要杀死的进程的pid-->按enter键-->(选择信号类型,以数字标示,默认15为杀死)本步可省略按enter键(常用为-9)

机器的正常负载范围


对一般的系统来说,是根据cpu数量去判断系统是否已经过载(Over Load)的。如果我们认为0.7算是单核机器负载的安全线的话,那么四核机器的负载最好保持在(4*0.7 = 2.8)以下。一般情况下,1分钟系统负荷表示最近的暂时现象。15分钟系统负荷表示是持续现象,并非暂时问题。如果load15较高,而load1较低,可以认为情况有所好转。反之,情况可能在恶化。

如何降低系统负载


导致负载高的原因可能很复杂,有可能是硬件问题也可能是软件问题。

如果是硬件问题,那么说明机器性能确实就不行了,那么解决起来很简单,直接换机器就可以了。

另外,CPU使用、内存使用、IO消耗都可能导致负载高。

如果是软件问题,有可能由于Java中的某些线程被长时间占用、大量内存持续占用等导致。建议从以下几个方面排查代码问题:

1、是否有内存泄露导致频繁GC

2、是否有死锁发生

3、是否有大字段的读写

4、会不会是数据库操作导致的,排查SQL语句问题。

如果发现线上机器Load飙高,可以考虑先把堆栈内存dump下来后,进行重启,暂时解决问题,然后再考虑回滚和排查问题。

到底什么是io


io涉及到的概念

  1. 用户空间与内核空间

操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。

使用内核空间是为了解决IO设备与CPU速度不匹配的问题

  1. 进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换(很耗资源)。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:

保存处理机上下文,包括程序计数器和其他寄存器

更新PCB信息

把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列

选择另一个进程执行,并更新其PCB

更新内存管理的数据结构

恢复处理机上下文

  1. 进程的阻塞

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的。

  1. 文件描述符FD

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

  1. 缓存 I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

io模式

IO模式


  1. 阻塞 I/O(Blocking IO)




当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

Blocking IO的特点就是在IO执行的两个阶段都被block了

  1. 非阻塞 I/O(NonBlocking IO)




当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。

NonBlocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有

  1. I/O 多路复用




当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接

  1. 异步 I/O(Asynchronous IO)




用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

select、poll、epoll详解


select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

  1. select

int select (int n, fd_set readfds, fd_set writefds, fd_set exceptfds, struct timeval timeout);

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。

  1. poll

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

  1. epoll

int epoll_create(int size);

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

查看磁盘io的三种方式


1.用 top 命令中的cpu 信息观察
0.0% wa 的百分比可以大致的体现出当前的磁盘io请求是否频繁。如果 wa的数量比较大,说明等待输入输出的的io比较多。

2.vmstat
[root@init ~]# vmstat 2 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1003168722434841529482800167311329400
00031599624348415294828000189416720010000
0003163842434841529482800006174820010000
00031624424348415294832000685934710010000
00031661624348415294832000147125060010000

vmstat 命令报告关于线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。系统范围内的这些统计信息(所有的处理器中)都计算出以百分比表示的平均值,或者计算其总和。

vmstat参数解释:

Procs

r: 等待运行的进程数 b: 处在非中断睡眠状态的进程数 w: 被交换出去的可运行的进程数。此数由 linux 计算得出,但 linux 并不耗尽交换空间

Memory

swpd: 虚拟内存使用情况,单位:KB

free: 空闲的内存,单位KB

buff: 被用来做为缓存的内存数,单位:KB

Swap

si: 从磁盘交换到内存的交换页数量,单位:KB/秒

so: 从内存交换到磁盘的交换页数量,单位:KB/秒

IO

bi: 发送到块设备的块数,单位:块/秒

bo: 从块设备接收到的块数,单位:块/秒

System

in: 每秒的中断数,包括时钟中断

cs: 每秒的环境(上下文)切换次数

CPU

按 CPU 的总使用百分比来显示

us: CPU 使用时间

sy: CPU 系统使用时间

id: 闲置时间

wa :等待输入输出的CPU时间百分比

St :被虚拟机偷走的cpu

3.iostat

安装:

Iostat 是 sysstat 工具集的一个工具,需要安装。
Centos的安装方式是:
yum install sysstat
Ubuntu的安装方式是:
aptitude install sysstat
[root@init ~]# iostat -dx
Linux 3.10.0-862.el7.x86_64 (init)07/17/2019 _x86_64_ (8 CPU)

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.01 18.89 0.83 37.11 102.06 459.69 29.61 0.41 10.72 38.96 10.09 0.72 2.75
vdb 0.00 0.17 0.02 2.08 0.71 22.21 21.84 0.01 5.47 12.11 5.40 0.99 0.21
vdc 0.19 12.43 1.13 10.12 21.28 98.28 21.26 0.04 3.54 2.09 3.71 0.36 0.40
scd0 0.00 0.00 0.00 0.00 0.02 0.00 64.78 0.00 0.33 0.33 0.00 0.26 0.00

使用:

iostat -dx 显示磁盘扩展信息

r/s 和 w/s 分别是每秒的读操作和写操作,而rKB/s 和wKB/s 列以每秒千字节为单位显示了读和写的数据量

如果这两对数据值都很高的话说明磁盘io操作是很频繁。

4.查看io较高的进程
[root@init ~]# iotop
Total DISK READ : 0.00 B/s | Total DISK WRITE : 0.00 B/s
Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 0.00 B/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % systemd --system --deserialize 16
2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd]
3 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [ksoftirqd/0]
5 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kworker/0:0H]
7 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/0]
8 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_bh]
9 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_sched]
10 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [lru-add-drain]
11 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/0]
12 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [watchdog/1]
13 rt/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [migration/1]

java应用load飙高排查


1、使用uptime查看当前load,发现load飙高。
➜ ~ uptime
13:29 up 23:41, 3 users, load averages: 101010

2、使用top命令,查看占用CPU较高的进程ID。
➜ ~ top

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1893 admin 200 7127m 2.6g 38m S 181.7 32.6 10:20.26 java

发现PID为1893的进程占用CPU 181%。而且是一个Java进程,基本断定是软件问题。

3、使用 top命令,查看具体是哪个线程占用率较高
➜ ~ top -Hp 1893
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4519 admin 200 7127m 2.6g 38m R 18.6 32.6 0:40.11 java

4、使用printf命令查看这个线程的16进制
➜ ~ printf %x 4519
11a7

5、使用jstack命令查看当前线程正在执行的方法。(Java命令学习系列(二)——Jstack)
➜ ~ jstack 1893|grep -A 200 11a7
"thread-5"#500 daemon prio=10 os_prio=0 tid=0x00007f632314a800 nid=0x11a2 runnable [0x000000005442a000]
java.lang.Thread.State: RUNNABLE
at sun.misc.URLClassPath$Loader.findResource(URLClassPath.java:684)
at sun.misc.URLClassPath.findResource(URLClassPath.java:188)
at java.net.URLClassLoader$2.run(URLClassLoader.java:569)
at java.net.URLClassLoader$2.run(URLClassLoader.java:567)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findResource(URLClassLoader.java:566)
at org.hibernate.validator.internal.xml.ValidationXmlParser.getInputStreamForPath(ValidationXmlParser.java:248)
at com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)

从上面的线程的栈日志中,可以发现,当前占用CPU较高的线程正在执行我代码的com.hollis.test.util.BeanValidator.validate(BeanValidator.java:30)类。那么就可以去排查这个类是否用法有问题了


----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:Devops小王子,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了