linux性能瓶颈分析

常见的几种问题:

  • swap高
  • load高
  • cpu使用率
  • 磁盘报警

几种case可能独立出现,也可能有因果关系的导致先后、同时出现。

说一下一般的排查、处理思路

CPU使用率高

这个查起来很方便,直接使用top命令,查看CPU使用最高的进程,查找该进程中CPU占用最高的线程,再进行具体排查。

对于web应用来说,CPU使用率变高,很大可能是因为程序的问题CPU过高一般是有如下原因造成的:

  1. java代码中存在死循环导致CPU过高

  2. 系统存在不恰当的代码, 尽管没有死循环, 但仍然CPU过高。

  3. JNI中有死循环代码,

  4. 堆内存设置太小造成的频繁GC. (堆内存设置过小, 或者存在内存泄漏)

  5. 32位JDK下, 堆内存设置太大造成的频繁GC.

  6. JDK自身存在死循环的Bug.

CPU过高问题定位的第一步就是要找到CPU高消耗的线程。这个过程可以写成脚本,自动找出web服务中CPU消耗最高的N个线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/sh
#changed MIao

PROG=`basename $0`

JAVA_HOME=你的java路径
WEB_HOME=你的web项目所在路径


jstack="$JAVA_HOME/bin/jstack"

usage() {
cat <<EOF
Usage:
${PROG} <pid|webapp> [<count>]
Find out the highest cpu consumed threads of java, and print the stack of these threads.
Example:
${PROG} twell 50
EOF
exit $1
}

redEcho() {
if [ -c /dev/stdout ] ; then
# if stdout is console, turn on color output.
echo -e "\033[1;31m$@\033[0m"
else
echo "$@"
fi
}


printStackOfThreads() {
while read threadLine ; do

threadId=`echo ${threadLine} | awk '{print $1}'`
threadId0x=`printf %x ${threadId}`
pcpu=`echo ${threadLine} | awk '{print $2}'`

jstackFile=/tmp/${uuid}_${javaid}

[ ! -f "${jstackFile}" ] && {
sudo -u tomcat $jstack ${javaid} > ${jstackFile} || {
redEcho "Fail to jstack java process ${javaid}"
rm ${jstackFile}
continue
}
}

redEcho "The stack of busy(${pcpu}%) thread(${threadId}/0x${threadId0x}) of java pid(${javaid}) all times($3):"
sed "/nid=0x${threadId0x}/,/^$/p" -n ${jstackFile}
done

}

if echo $1 | grep -q "$WEB_HOME"
then
export CATALINA_BASE=$1
else
export CATALINA_BASE=$WEB_HOME/$1
fi

if [ -e $CATALINA_BASE/conf/server.xml ]
then
javaid=`ps aux |grep "java"|grep "Dcatalina.base=$CATALINA_BASE "|grep -v "grep"|awk '{ print $2}'`
else
javaid=`ps -eo pid | grep "^$1$"`
fi

if [ -z "$javaid" ] ; then
redEcho "process ($1) not found !"
usage 1;
fi

if [ -z "$2" ] ; then
count=1000
else
(( count = $2 +7 ))
fi

uuid=`date +%s`_${RANDOM}_$$

top -p $javaid -H -n 1 -b | sed -n "8,${count}p" | awk '{print $1,$9,$11}' | printStackOfThreads

rm /tmp/${uuid}_*

替换上面脚本中的java环境变量以及web项目路径:

1
2
JAVA_HOME=你的java路径
WEB_HOME=你的web项目所在路径

假设你的tomcat进程是用tomcat账户启动的,用法如下:

1
2
sudo chmod 755 ./slow_stack.sh
sudo -utomcat ./slow_stack.sh 进程pid 使用率最高的count个线程

load高

load高与cpu和io的相关性很大。

load值的算法

load 的1分钟、5分钟、14分钟的average值,代表了这几分钟内,运行中+可运行等待调度的进程数量的平均值,也就是CPU使用队列的长度的统计信息

与CPU使用率的区别

CPU使用率是程序在运行期间实时占用的CPU百分比。进程是非CPU密集型、以及CPU上下文切换等情况下,CPU使用率是不高的。因此可见,CPU使用率与CPU负载load并没有必然联系。

但往往CPU使用率的提升是导致load增高的一个主要原因

CPU对load的影响

理论上来说,单核CPU的load如果到了1,就说明cpu的负载能力被充分使用了,大于1的时候,就有进程在等待CPU了。所以N核的CPU,load满载的时候是1×N。可以参考理解Linux系统负荷

由此可以看出,当出现上面的CPU消耗过高的情况,就可能导致等待、堆积的进程变多,导致load增高。

IO对load的影响

根据上面对load和cpu的分析可以看出,若是有过多的IO等待,也会造成CPU使用队列堆积,load增高的现象。

磁盘读写请求过多就会导致大量I/O等待。 cpu的工作效率要高于磁盘,而进程在cpu上面运行需要访问磁盘文件,这个时候cpu会向内核发起调用文件的请求,让内核去磁盘取文件,这个时候会切换到其他进程或者空闲,这个任务就会转换为不可中断睡眠状态。当这种读写请求过多就会导致不可中断睡眠状态的进程过多,从而导致负载高,cpu低的情况。

排查问题的时候可以重点排查IO状况是否正常、不可中断睡眠状态的线程是否过多。

Linux中CPU使用率低负载高

绑核

特定情况下,会发现cpu使用率低,idle比较高、io没有异常,但是load一直很高的情况。这种情况可能是出现了绑核。

当你使用top命令时,cpu的使用状况、idle值是平均值,如果展开看每个cpu的使用的状况,也许会发现很多进程都绑到了同一个核上,其他内核一直空闲,而这一个内核因为任务堆积导致load增高

Load高,CPU idle很高,这情况太诡异了

load异常变高的的场景,排查问题的方向始终来源于load值本身的算法,load增高必然是因为任务过多导致cpu队列堆积,以此为思路就可以展开排查,从cpu使用率、io、绑核等常见原因入手。

swap增高

swap高的原因,直接原因就是内存不够用,导致内存中的数据换出到磁盘上,而这样同时会导致写磁盘的IO增高

swap增高通常的原因可以检查一下进程是否有内存泄露。

可以看:Swap使用情况 内存问题排查思路

磁盘报警

磁盘报警目前常见的基本两类:磁盘被写满、inode用尽

磁盘写满

问题很直接,直接查找一下系统中持续增长的大文件,删减就ok。删除方法:线上大日志文件的删除。不过这背后的原因需要深究。

一般来说,web服务器出现这样的情况,是因为日志文件过大导致的,所以需要思考一下日志输出是否合理、日志文件的压缩时机是否合适。

inode用尽

inode用尽通常是因为小文件过多导致将有限的inode资源用尽,所以可能磁盘使用率并不高

使用df -ih查看使用情况:

1
2
3
4
5
6
7
8
[sage.wang@machine /path]$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda2 640K 41K 600K 7% /
tmpfs 1000K 4 1000K 1% /dev/shm
/dev/vda1 50K 39 50K 1% /boot
/dev/vda6 63K 1.4K 62K 3% /home
/dev/vda7 3.9M 3.8M 20k 99% /home/xxx
/dev/vda5 256K 1.6K 255K 1% /var

可以看到/home/xxx下面的inode使用了99%,接下来就是看一下具体哪个目录里使用的最多了,按照上面的目录一层一层的找下,查找的方法用下面的脚本:

1
2
3
4
5
6
7
8
9
[sage.wang@machine /home/xxx/www/webserver]$ for i in ./*; do echo $i; find $i | wc -l; done
./conf
9
./logs
2365048904
./webapps
2349
./work
9

这里就能看出来,比较多的文件其实是log文件和webapps下面的类文件

PS:上面的文件个数是我随便写的

关键用法:for i in ./*; do echo $i; find $i | wc -l; done 查找当前目录下的目录有多少个文件

坚持原创技术分享,您的支持将鼓励我继续创作!
0%