诊断
context switch太大
dstat显示系统的context
switch和interrupts很高,有可能是启动的线程数量太多。一般进程的数量会比较明显的展示出来,查看线程的数量,可用下面的办法:
1、top -H 手册中说:-H : Threads toggle
加上这个选项启动top,top一行显示一个线程。否则,它一行显示一个进程。
2、ps xH 手册中说:H Show threads as if they were processes
这样可以查看所有存在的线程。
3、ps -mp \
linux后端诊断与调试技术
<http://www.2cto.com/os/201412/364987.html> 主要包括:- 网络IO与流量
- 系统内存
- 系统负载与CPU
- 磁盘IO与占用空间
- 线程占用与运行
- 文件描述符占用
评估标准
| 影响性能因素 | 好 | 坏 | 糟糕 |
|--------------|---------------------------|---------------------------|--------------------------------|
| CPU | user%+sys%\<70% | user%+sys%=85% | user%+sys%\>=90% |
| 内存 | Swap In(si) or Out(so)=0 | Per CPU \<= 10 paging/s | 大量paging \> 10 |
| 磁盘 | iowait%\<20% | iowait%=35% | iowait%\>=50% |
| 负载 | load average \< cpu核个数 | load average \> cpu核个数 | load average \> 1.5倍cpu核个数 |
性能分析和诊断工具
用vmstat、sar、iostat检测是否是CPU、磁盘,内存瓶颈
用free、vmstat检测是否是内存、IO瓶颈 用iostat检测是否是磁盘I/O瓶颈
用dstat检测是否是网络带宽、磁盘io、内存、负载等综合瓶颈分析
用mpstat检测是否cpu调用不均衡,也可以使用top替代
用pidstat检测相应进程cpu消耗情况 用netstat检测socket
buffer有未发送或处理的数据,从而判断程序处理能力下降或出现问题.
用lsof检测打开文件描述(网络文件和磁盘文件,管道等)符过多,导致资源不足
用df和du组合检测挂载磁盘或目录占用空间巨大或inode节点消耗殆尽。
用iftop检查2台主机间是否存在流量瓶颈.
下面命令中参数名词解释:
buffer是用于存放(缓存)要输出到disk(块设备)的数据的,
cache是存放从disk上读出的数据。这buffer和cache是为了提高IO性能的,并由OS管理。
swap: linux内核读写虚拟内存是以 “页”
为单位操作的,把内存转移到硬盘交换空间(SWAP)和从交换空间读取到内存
的时候都是按页来读写的。
Paging:内存和SWAP的这种交换过程称为页面交换(Paging)
检测工具
top
shift + h 按线程查看cpu消耗情况 查看每个核消耗情况 us
过高说明应用程序消耗了大部分cpu sy 过高表示系统线程切换频繁 wa
表示为在执行过程中等待io所占的百分比 hi 硬件中断(ex:网卡接收数据频发)
top -p pid 多列信息列表中直显示对应进程信息.
netstat
`` example
netstat –an | grep 端口
Recv-Q 网络接收队列
一般情况为0,如果持续为非0表示收到的数据已经在本地接收缓冲,应用程序还没处理,可能是应用程序处理性能下降。
Send-Q 发送队列
一般情况为0,如果持续为非0可能是应用向外发送数据包过快,或者是
对方接收数据包不够快。 netstat –an \| grep 端口
(查看应用程序端口是否正常监听)
以前遇到过分布式缓存memcached在高峰访问中会出现这种现象,由于高峰时期极端情况下多线程lock内存池原因,导致服务端性能急剧下降。
<h3 id="vmstat">vmstat</h3>
CPU使用率,内存使用,虚拟内存交换情况,IO读写情况 一般使用格式为:vmstat
interval count //表示输出频率1秒,连续输出10次 使用实例: vmstat 1 10
如果CPU的sy和us值相加的百分比接近100%,或者运行队列(r)中等待
的进程数总是不等于0,且经常大于4,同时id也经常小于40,则该系
统受限于CPU;如果bi、bo的值总是不等于0,则该系统受限于内存。
swpd值过高一般情况由于物理内存不够用.
free列表示当前空闲的物理内存数量(以k为单位) buff列表示buffers
cache的内存数量,一般对块设备的读写才需要缓冲。 memory cache列表示page
cached的内存数量,一般作为文件系统cached,频繁访问的文件都会被cached,如果cache值较大,说明cached的文件数较多,如果此时IO中bi比较小,说明文件系统效率比较好。
swap si列表示由磁盘调入内存,也就是内存进入内存交换区的数量。
so列表示由内存调入磁盘,也就是内存交换区进入内存的数量。
一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足。需要增加系统内存。
in 每秒CPU的中断次数 cs 每秒上下文切换次数
//如果上下文切换过多(远高于平常数值),则可能是线程创建过多 us
用户CPU时间占百分比 sy 系统CPU时间占百分比 id 空闲 CPU时间占百分比 wt
等待IO
<h3 id="sar">sar</h3>
格式如下:sar -d interval count 需要关注几个参数:
await表示平均每次设备I/O操作的等待时间(以毫秒为单位)。
svctm表示平均每次设备I/O操作的服务时间(以毫秒为单位)。
%util表示一秒中有百分之几的时间用于I/O操作。
利用sar作性能评估,磁盘IO性能,一般有如下评判标准:
正常情况下svctm应该是小于await值的,而svctm的大小和磁盘性能有关,CPU、内存的负荷也会对svctm值造成影响,过多的请求也会间接的导致svctm值的增加。await值的大小一般取决与svctm的值和I/O队列长度以及I/O请求模式,如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好,如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢,此时可以通过更换更快的硬盘来解决问题。
%util项的值也是衡量磁盘I/O的一个重要指标,如果%util接近100%,表示磁盘产生的I/O请求太多,I/O系统已经满负荷的在工作,该磁盘可能存在瓶颈。长期下去,势必影响系统的性能,可以通过优化程序或者通过更换更高、更快的磁盘来解决此问题。
<h3 id="iostat">iostat</h3>
可以通过Blk<sub>read</sub>/s和Blk<sub>wrtn</sub>/s的值对磁盘的读写性能有一个基本的了解,如果Blk<sub>wrtn</sub>/s值很大,表示磁盘的写操作很频繁,可以考虑优化磁盘或者优化程序,如果Blk<sub>read</sub>/s值很大,表示磁盘直接读取操作很多,可以将读取的数据放入内存中进行操作。对于这两个选项的值没有一个固定的大小,根据系统应用的不同,会有不同的值,但是有一个规则还是可以遵循的:长期的、超大的数据读写,肯定是不正常的,这种情况一定会影响系统性能。
如果 %util 接近
100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈.
idle小于70% IO压力就较大了,一般读取速度有较多的wait.
同时可以结合vmstat查看查看b参数(等待资源的进程数)和wa参数(IO等待所占用的CPU时间的百分比,高过30%时IO压力高)
另外 await 的参数也要多和 svctm 来参考。差的过高就一定有 IO 的问题.
avgqu-sz 也是个做 IO
调优时需要注意的地方,这个就是直接每次操作的数据的大小,如果次数多,但数据拿的小的话,其实
IO 也会很小.如果数据大,IO的数据才会高.也可以通过 avgqu-sz × ( r/s or w/s
) = rsec/s or wsec/s.也就是讲,读定速度是这个来决定的. svctm 一般要小于
await (因为同时等待的请求的等待时间被重复计算了),svctm
的大小一般和磁盘性能有关,CPU/内存的负荷也会对其有影响,请求过多也会间接导致
svctm 的增加.await 的大小一般取决于服务时间(svctm) 以及 I/O 队列的长度和
I/O 请求的发出模式.如果 svctm 比较接近 await,说明 I/O
几乎没有等待时间;如果 await 远大于 svctm,说明 I/O
队列太长,应用得到的响应时间变慢,如果响应时间超过了用户可以容许的范围,这时可以考虑更换更快的磁盘,调整内核
elevator 算法,优化应用,或者升级 CPU. 队列长度(avgqu-sz)也可作为衡量系统
I/O 负荷的指标,但由于 avgqu-sz 是按照单位时间的平均值,所以不能反映瞬间的
I/O 洪水.
<h3 id="mpstat">mpstat</h3>
example
mpstat -P ALL interval count
<h3 id="dstat">dstat</h3>
dstat是多功能系统资源统计工具
<h3 id="iftop">iftop</h3>
iftop显示了系统上所有源主机或者目的主机网络带宽使用的列表。
<h3 id="du命令查看文件和目录磁盘使用的空间">du命令查看文件和目录磁盘使用的空间</h3>
输出当前目录下各个子目录所使用的空间:
example
du -h --max-depth=1
按照子目录大小排序(降序):
example
du -s * | sort -rn | cut -f2- | xargs -d "\n" du -sh
<h3 id="lsof">lsof</h3>
存储图片或小文件的分布式文件系统,很容易发生打开文件量过大,或打开网络连接(网络文件描述符)过大的问题。
递归查找某个目录中所有打开的文件
example
lsof +D /DIR/
lsof的列表中Type为REG和DIR分别表示打开磁盘文件和目录
example
lsof -p [pid] | grep "REG" //列举该进程所有打开的磁盘文件.
然后通过计算和分析就能知道是否有大量磁盘文件没有释放,然后定位出问题的代码块.
本机监听端口来自客户端连接数统计排序(降序)
example
lsof -i:9092 | awk '{print $9}' | cut -d ">" -f2 | awk '{ cidx=index($1,":");print substr($1,0,cidx-1)}' | sort | uniq -c | sort -rn
<h3 id="iodump">iodump</h3>
iodump
是一个统计每一个进程(线程)所消耗的磁盘I/O工具。这个一个perl脚本,其原理时打开有关I/O的内核记录消息开关,而后读取消息然后分析输出。简单使用步骤如下:
首先下载这个工具:
example
wget http://aspersa.googlecode.com/svn/trunk/iodump
然后打开有关I/O内核消息的开关:
example
echo 1 >/proc/sys/vm/block_dump
上述开关打开后,内核会记录下每一个I/O操作的消息。我们只需要定时获取并分析就好了,比如下面这样:
example
while true; do sleep 1; dmesg -c ; done | perl iodump
等待一段时间,然后通过ctrl+c来结束上述脚本,你将获得下面类似的信息:
TASK PID TOTAL READ WRITE DIRTY DEVICES postgres 5799 1919 1919 0 0 sda7
jbd2/sda7-8 1572 35 0 35 0 sda7 jbd2/sda2-8 250 32 0 32 0 sda2 flush-8:0
2229 31 0 31 0 sda2, sda7 postgres 4308 2 0 2 0 sda7 bash 5804 1 0 1 0
sda2
上述输出的单位为块(block),每块的大小取决于创建文件系统时指定的块大小。比如我这个里的sda7的block大小是1KB。
<h3 id="iotop">iotop</h3>
iotop是一个Python编写的工具,有类似top工具的UI,包括一些参数也和top类似。
<h3 id="sysdig">sysdig</h3>
<http://os.51cto.com/art/201412/458595_all.htm>
是一个强大的开源工具,用于系统级别的勘察和排障,它的创建者在介绍它时称之为“strace+tcpdump+lsof+上面点缀着lua樱桃的绝妙酱汁”。抛开幽默不说,sysdig的最棒特性之一在于,它不仅能分析Linux系统的“现场”状态,也能将该状态保存为转储文件以供离线检查。更重要的是,你可以自定义sysdig的行为,或者甚至通过内建的(你也可以自己编写)名为凿子(chisel)的小脚本增强其功能。单独的凿子可以以脚本指定的各种风格分析sysdig捕获的事件流。
<h2 id="磁盘">磁盘</h2>
<h3 id="hdparm检测磁盘读写速度">hdparm检测磁盘读写速度</h3>
example
hdparm -t /dev/sda
<h3 id="用dd测试磁盘读写速度">用dd测试磁盘读写速度</h3>
问: 以下几种方式测试磁盘读写速度有什么区别? dd bs=1M count=128
if=/dev/zero of=test dd bs=1M count=128 if=/dev/zero of=test conv=sync
dd bs=1M count=128 if=/dev/zero of=test conv=fdatasync dd bs=1M
count=128 if=/dev/zero of=test oflag=dsync
答:区别在于内存中写缓存的处理方式。 dd bs=1M count=128 if=/dev/zero
of=test
没有加任何参数,dd默认的方式不包括“同步(sync)”命令。也就是说,dd命令完成前并没有让系统真正把文件写到磁盘上。所以以上命令只是单纯地把这128MB的数据读到内存缓冲当中(写缓存\[write
cache\])。所以你得到的将是一个超级快的速度。因为其实dd给你的只是读取速度,直到dd完成后系统才开始真正往磁盘上写数据,但这个速度你是看不到了。所以如果这个速度很快,先不要偷着乐。呵呵
dd bs=1M count=128 if=/dev/zero of=test conv=sync
和前面1中的完全一样。分号隔开的只是先后两个独立的命令。当sync命令准备开始往磁盘上真正写入数据的时候,前面dd命令已经把错误的“写入速度”值显示在屏幕上了。所以你还是得不到真正的写入速度。
dd bs=1M count=128 if=/dev/zero of=test conv=fdatasync
加入这个参数后,dd命令执行到最后会真正执行一次“同步(sync)”操作,所以这时候你得到的是读取这128M数据到内存并写入到磁盘上所需的时间,这样算出来的时间才是比较符合实际的。
dd bs=1M count=128 if=/dev/zero of=test oflag=dsync
加入这个参数后,dd在执行时每次都会进行同步写入操作。也就是说,这条命令每次读取1M后就要先把这1M写入磁盘,然后再读取下面这1M,一共重复128次。这可能是最慢的一种方式了,因为基本上没有用到写缓存(write
cache)。
问:那应该用哪一种呢? 答:建议使用 dd bs=1M count=128 if=/dev/zero
of=test conv=fdatasync
因为这种方式最接近计算机实际操作,所以测出来的数据最有参考价值。
<h3 id="查看哪个进程占用磁盘IO">查看哪个进程占用磁盘IO</h3>
- 方法一
example
$ iotop -oP
命令的含义:只显示有I/O行为的进程
- 方法二
example
$ pidstat -d 1
命令的含义:展示I/O统计,每秒更新一次
<h2 id="案例">案例</h2>
<h3 id="创建大量网络连接数导致服务进程挂死或假死">创建大量网络连接数导致服务进程挂死或假死</h3>
当某台或几台机器作为基础服务组件或中间件节点(不一定为java,或python,go,c/c++等等)集群部署时,会有大量客户端会依赖此服务而连接此目标集群。打开网络连接过大的问题,可结合netstat和lsof来解决问题。
example
netstat -an | grep 9094
lsof -i:9094 | awk '{print $9}' | cut -d ">" -f2 | awk '{ cidx=index($1,":");print substr($1,0,cidx-1)}' | sort | uniq -c | sort -rn
<h3 id="线上java服务进程突然cpu占用很高">线上java服务进程突然cpu占用很高</h3>
CPU消耗很高的进程:
example
ps p 31476 -L -o pcpu,pid,tid,time,tname,stat,psr | gawk '{printf("%s %d %d %s %s %s %x\n",$1,$2,$3,$4,$5,$6,$3) }' | sort -n -k1 -r
假如进程的cpu消耗很高且大约80%cpu,通过ps命令找到对应线程号nid=31510转换为十六进制0x7b16。
example
jstack 31476 | grep "7b16"
通过上面的命令可找出Java的线程号,再通过jstack,缩小代码排查范围,顺藤摸瓜找到对应的调用堆栈信息和代码,然后对相关代码逻辑进行分析。
<h3 id="某个进程启动时无法创建文件">某个进程启动时无法创建文件</h3>
如果遇到上述错误,可以先检测一下硬盘状态信息(一般是这类问题概率不大,除非是数据库或做存储用的服务器)
请参考硬盘检测smartctl:Linux系统下检测硬盘的健康状态 Linux硬盘的检测
如果磁盘状态一切正常,则分析可能为挂载磁盘空间占满。 执行 :df -h
查看磁盘空间占用情况
然后到相应的应用程序软件部署目录或数据存储目录下执行:
example
du -s * | sort -rn | cut -f2- | xargs -d "\n" du -sh
就可以顺利找到占用空间比较大的目录,然后做相应处理。
<h2 id="资源">资源</h2>
<http://os.51cto.com/art/201208/354609.htm> Linux下的一些I/O统计工具
<http://www.centoscn.com/CentOS/Intermediate/2013/0905/1564.html>
Centos下的IO监控与分析
<h1 id="配置">配置</h1>
<h2 id="swap和OOM">swap和OOM</h2>
swap:在linux里面,当物理内存不够用了,而又有新的程序请求分配内存,那么linux就会选择将其他程序暂时不用的数据交换到物理磁盘上(swap
out),等程序要用的时候再读进来(swap in)。这样做的坏处显而易见,swap
in/swap
out这里的代价比较大,相比数据一直放在内存里面,多了读磁盘的操作,而磁盘IO代价。。大家都懂的。
OOM:out of
memory,指在linux里面,由于系统内存压力,系统会选择保护一些系统进程,而将一些其他的进程kill掉,释放内存。
一、禁止使用swap
- 可以设置/proc/sys/vm/swappiness=0,不过这就禁用了所有进程使用swap
- 给进程分配内存的时候,让它使用hugepage内存。设置方法: vi
/etc/sysctl.conf ; 添加
vm.nr<sub>hugepages</sub>=hugepage<sub>num</sub>(多少页大页内存,这个必须大于进程需要的最大内存,比如mysqld
buffer pool=5G,那么hugepage<sub>num</sub>\*page<sub>size</sub>\>5G)
; sysctl -p 生效。
这样可以防止进程使用hugepage的原因:在进程使用hugepage分配内存时,是一次性分配、且独占的,既然一次性已经分配满了那也不存在使用swap的理由,你可以通过top命令看到进程一开始就占用了你所为它设定大小的内存。
- 可以设置/proc/sys/vm/overcommit<sub>memory</sub>=2(其他值代表什么含义,可以自己www.linuxidc.com,另外自己测试中发现这个值如果大于2,那么效果和2一样)
在值为2的情况下,可以分配的内存最大大小在swap size +
RAM\*((/proc/sys/vm/overcommit<sub>ratio</sub>) /
100)),如果大于这个直接返回错误,这个也是类似hugepage分配内存,也是一次性,独占(不能100%肯定),但是利用top命令查看,它显示当前已占用的内存大小不是我们为他设定的那个值。那为什么我认为它也是独占、且一次性分配呢?因为启动其他进程的时候,直接报错:
Error occurred during initialization of VM Could not reserve enough
space for object heap
- 使用mysql自带的参数–memlock
它将内存锁定,原理同hugepage一样,不过缺陷是必须由root运行mysqld
二、怎么避免进程因为OOM机制被kill掉?
- 与OOM相关的几个文件是 /proc/\<pid\>/oom<sub>adj</sub> 、
/proc/\<pid\>/oom<sub>score</sub>。前者是一个权值-17至15。设置为0表示永远不被kill,其余情况值越大越容易被kill。后者就是它计算出来的一个值,就是根据这个值来选择哪些进程被kill掉的。
- 上面放置使用swap中的第三个方法
overcommit参数,因为它分配不出内存就会返回错误,所以永远也不能达到内存被耗尽。OOM也就不会有影响了。
<h1 id="网络">网络</h1>
<h2 id="高负载服务器网络参数调整">高负载服务器网络参数调整</h2>
- net.ipv4.tcp<sub>syncookies</sub> = 1 表示开启SYN
Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
- net.ipv4.tcp<sub>twreuse</sub> = 1 表示开启重用。允许将TIME-WAIT
sockets重新用于新的TCP连接,默认为0,表示关闭;
- net.ipv4.tcp<sub>twrecycle</sub> = 1 表示开启TCP连接中TIME-WAIT
sockets的快速回收,默认为0,表示关闭。
- net.ipv4.tcp<sub>fintimeout</sub> = 30
表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
- net.ipv4.tcp<sub>keepalivetime</sub> = 1200
表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
- net.ipv4.ip<sub>localportrange</sub> = 1024 65000
表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
- net.ipv4.tcp<sub>maxsynbacklog</sub> = 8192
表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
- 如果高负载系统使用了netfilter/iptables,调整以下参数
net.ipv4.ip<sub>conntrackmax</sub> = 655360
在内核内存中netfilter可以同时处理的“任务”(连接跟踪条目)
- net.ipv4.netfilter.ip<sub>conntracktcptimeoutestablished</sub> = 180
跟踪的连接超时结束时间
然后执行/sbin/sysctl -p让参数生效
<h1 id="systemtap">systemtap</h1>
<h2 id="debuginfo">debuginfo</h2>
使用systemtap需要安装内核的调试信息包。在CentOS有专门的debuginfo仓库(默认情况下没有启用):
<http://debuginfo.centos.org/6/$basearch/>
并有专门的工具支持debuginfo的安装,安装kernel的debuginfo只要下面的命令:
example
yum install kernelname-devel-version
debuginfo-install kernelname-version
debuginfo-install命令会启用debuginfo仓库并安装相应软件包。
Ubuntu下安装需要重新编译kernel:
bash
$ cd $HOME
$ sudo apt-get install dpkg-dev debhelper gawk
$ mkdir tmp
$ cd tmp
$ sudo apt-get build-dep --no-install-recommends linux-image-$(uname -r)
$ apt-get source linux-image-$(uname -r)
$ cd linux-2.6.31 (this is currently the kernel version of 9.10)
$ fakeroot debian/rules clean
$ AUTOBUILD=1 fakeroot debian/rules binary-generic skipdbg=false
$ sudo dpkg -i ../linux-image-debug-2.6.31-19-generic_2.6.31-19.56_amd64.ddeb
$ sudo dpkg -i linux-image-2.6.38-15-generic_2.6.38-15.64_i386.deb
$ sudo apt-get install linux-headers-uname -r
用下面的命令测试stap是否成功安装:
example
stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'
``socktop
socktop是对systemtap脚本的封装,Ubuntu14.04发行版中是在systemtap-doc包里面作为example来提供的。