iostat 是用来分析 cpu 负载和磁盘 I/O 情况的工具。系统不自带, 安装sysstat 即可。
iostat的数据来源于/proc/diskstats
iostat [参数] [时间] [次数]
常用操作:iostat -x -d -k 10 5: 每隔10s输出一次,一共输出5次
上面这些百分比是怎么算出来的?
比如一秒内有100个cpu时间片,这个cpu时间片就是cpu工作的最小单位。那么这100个cpu时间片在不同的区域和目的进行操作使用,就代表这个区域所占用的cpu时间比。也就是这里得出的cpu时间百分比。
比如下面一个程序:
将文件从磁盘的src位置拷贝到磁盘的dst位置。文件会从src先读取进入到内核空间,然后再读取到用户空间,然后拷贝数据到用户空间的buf上,再通过用户空间,内核空间,数据才到磁盘的dst上。
所以从上面这个程序来看,cpu消耗在kernel space的时候就是sy(系统态使用的cpu百分比),cpu消耗在user space的时候就是us(用户态使用的cpu百分比)。
简单说,Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。
Kernel space 可以执行任意命令,调用系统的一切资源;User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称 system call),才能向内核发出指令。
str = "my string" // 用户空间
x = x + 2
file.write(str) // 切换到内核空间
y = x + 4 // 切换回用户空间
上面代码中,第一行和第二行都是简单的赋值运算,在 User space 执行。第三行需要写入文件,就要切换到 Kernel space,因为用户不能直接写文件,必须通过内核安排。第四行又是赋值运算,就切换回 User space。
如果%iowait的值过高,表示硬盘存在I/O瓶颈,%idle值高,表示CPU较空闲,如果%idle值高但系统响应慢时,有可能是CPU等待分配内存,此时应加大内存容量。%idle值如果持续低于10,那么系统的CPU处理能力相对较低,表明系统中最需要解决的资源是CPU。
块设备有相应的调度算法。如果两个IO发生在相邻的数据块时,他们可以合并成1个IO。
这个简单的可以理解为快递员要给一个18层的公司所有员工送快递,每一层都有一些包裹,
对于快递员来说,最好的办法是同一楼层相近的位置的包裹一起投递,否则如果不采用这种算法,
采用最原始的来一个送一个(即noop算法),那么这个快递员,可能先送了一个包括到18层,
又不得不跑到2层送另一个包裹,然后有不得不跑到16层送第三个包裹,然后包到1层送第三个包裹,
那么快递员的轨迹是杂乱无章的,也是非常低效的。Linux常见的调度算法有: noop deadline和cfq。此处不展开了。
root@node-186:~# cat /sys/block/sdc/queue/scheduler
[noop] deadline cfq
首先我们用超市购物来比对iostat的输出。我们在超市结账的时候,一般会有很多队可以排,
队列的长度,在一定程度上反应了该收银柜台的繁忙程度。那么这个变量是avgqu-sz这个输出反应的,
该值越大,表示排队等待处理的io越多。
对比生活中的例子,超时排队的时候,你会首先查看队列的长度来评估下时间,如果队列都差不多长的情况下,
你就要关心前面顾客篮子里东西的多少了。
如果前面顾客每人手里拿着一两件商品,另一队几乎每一个人都推这满满一车子的商品,你可能知道要排那一队。
因为商品越多,处理单个顾客的时间就会越久。IO也是如此。
await = ((所有读IO的时间)+(所有写IO的时间))/((读请求的个数) + (写请求的个数))
注意一点就行了,这个所有读IO的时间和所有写IO的时间,都是包括IO在队列的时间在内的。不能一厢情愿地认为,
是磁盘控制器处理该IO的时间。注意,能不能说,await比较高,所以武断地判定这块盘的能力很菜?
答案是不能。await这个值不能反映硬盘设备的性能。await的这个值不能反映硬盘设备的性能,
await这个值不能反映硬盘设备的性能,重要的话讲三遍。我们考虑两种IO的模型:
250个IO请求同时进入等待队列
250个IO请求依次发起,待上一个IO完成后,发起下一个IO
第一种情况await高达500ms,第二个情况await只有4ms,但是都是同一块盘。但是注意await是相当重要的一个参数,它表明了用户发起的IO请求的平均延迟:
await = IO 平均处理时间 + IO在队列的平均等待时间
因此,这个指标是比较重要的一个指标。
对于iostat这个功能而言,%util固然会给人带来一定的误解和苦扰,但是svctm给人带来的误解更多。
一直以来,人们希望了解块设备处理单个IO的service time,这个指标直接地反应了硬盘的能力。回到超市收银这个类比中,如果收银员是个老手,操作流,效率很高,那么大家肯定更愿意排这一队。
但是如果收银员是个新手,各种操作不熟悉,动作慢,效率很低,那么同样多的任务,就会花费更长的时间。
因此IO的平均service time(不包括排队时间)是非常有意义的。但是service time和iostat无关,iostat没有任何一个参数能够提供这方面的信息。
而svctm这个输出给了人们这种美好的期待,却只能让人空欢喜。从现在起,我们记住,我们不能从svctm中得到自己期待的service time这个值,这个值其实并没有什么意义,
事实上,这个值不是独立的,它是根据其他值计算出来的。既然svctm不能反映IO处理时间,那么有没有一个参数可以测量块设备的IO平均处理时间呢?
很遗憾iostat是做不到的。但是只要思想不滑坡,办法总比困难多,
blktrace这个神器可能得到这个设备的IO平均处理时间。
blktrace可以讲IO路径分段,分别统计各段的消耗的时间。
%util 和磁盘设备饱和度
注意,%util是最容易让人产生误解的一个参数。
很多初学者看到%util 等于100%就说硬盘能力到顶了,这种说法是错误的。%util数据源自diskstats中的io_ticks,这个值并不关心等待在队里里面IO的个数,它只关心队列中有没有IO。
和超时排队结账这个类比最本质的区别在于,现代硬盘都有并行处理多个IO的能力,但是收银员没有。
收银员无法做到同时处理10个顾客的结账任务而消耗的总时间与处理一个顾客结账任务相差无几。
但是磁盘可以。所以,即使%util到了100%,也并不意味着设备饱和了。最简单的例子是,某硬盘处理单个IO请求需要0.1秒,有能力同时处理10个。
但是当10个请求依次提交的时候,需要1秒钟才能完成这10%的请求,,在1秒的采样周期里,%util达到了100%。
但是如果10个请一次性提交的话, 硬盘可以在0.1秒内全部完成,这时候,%util只有10%。因此,在上面的例子中,一秒中10个IO,即IOPS=10的时候,%util就达到了100%,
这并不能表明,该盘的IOPS就只能到10,事实上,纵使%util到了100%,
硬盘可能仍然有很大的余力处理更多的请求,即并未达到饱和的状态。下一小节有4张图,可以看到当IOPS为1000的时候%util为100%,
但是并不意味着该盘的IOPS就在1000,实际上2000,3000,5000的IOPS都可以达到。
根据%util 100%时的 r/s 或w/s 来推算磁盘的IOPS是不对的。那么有没有一个指标用来衡量硬盘设备的饱和程度呢。很遗憾,iostat没有一个指标可以衡量磁盘设备的饱和度。
Linux有两种IO:
计算方法:每秒采集一次/proc/diskstats
disk.io.ios_in_progress:当前正在运行的实际I / O请求数
disk.io.msec_read:所有读取花费的时间,ms
disk.io.msec_write:所有写入花费的时间 ms
disk.io.msec_total:ios_in_progress> = 1的时间量
disk.io.msec_weighted_total:磁盘花在处理请求上的总加权时间
注意:这个值的意义是『所有请求的总等待时间』
每一次请求结束后,这个值会增加这个请求的处理时间乘以当前的队列长度
1) 首先想到的就是换性能好的磁盘。这个好像是最高效的方式,SSD具有更好的性能,访问数据都是随机的。更小的功耗。LVM(逻辑卷)的扩容。
2) 然后就是Raid (RAID0, RAID1, RAID5, RAID0+1)。通过raid实际数据在多块磁盘的并发读写和数据备份。增强磁盘的可用性和容错能力
3) 确定机器的上线的需求。运维人员一定要知道,机器的使用场景。小文件(占用Inode例如图片)读写瓶颈是磁盘的寻址(tps),大文件(占用磁盘容量)读写的性能瓶颈是带宽
4) Linux有一句话(一切皆文件)。空闲内存作文件系统访问的cache,因此系统内存越大存储系统的性能也越好
5) 最后就是架构层面的优化,CDN(nginx、squid..),机房内部反向代理(squid),memcached,消息队列,缓存机制。总之就是静态的采用缓存机制。非静态的优化性能,减小调用磁盘
1)应用程序设计的缺陷和数据库查询的滥用、操作人员的失误、都有可能导致性能问题
2)性能瓶颈可能是因为程序设计缺陷/内存太小/磁盘有损坏、性能差,但是最终都是CPU耗尽的结果(这就话很实用),系统负载极高,响应迟缓,甚至暂时失去响应。登陆不上机器。
3)由于linux的swap机制。物理内存不够时会使用交换内存(可以调优参数),大量使用swap会带来磁盘I0进而导致CPU消耗
4)可能造成cpu瓶颈的问题:频繁执Perl,php,java程序生成动态web;数据库查询大量的where子句、order by/group by排序……
5)可能造成内存瓶颈问题:高并发用户访问、系统进程多(每个进程都会消耗内存,驻留内存),java内存泄露……
6)可能造成磁盘IO瓶颈问题:生成cache文件,数据库频繁更新,或者查询大表……
1)swap(当内存不足时会调用SWAP)
a.si列表示由磁盘调入内存,也就是内存进入内存交换区的数量;
b.so列表示由内存调入磁盘,也就是内存交换区进入内存的数量
c.一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足,需要考虑是否增加系统内存。或者扩展机器提高可用性
2)IO
a.bi列表示从块设备读入的数据总量(即读磁盘,单位KB/秒)
b.bo列表示写入到块设备的数据总量(即写磁盘,单位KB/秒)
这里设置的bi+bo参考值为1000,如果超过1000,而且wa值比较大,则表示系统磁盘IO性能瓶颈。
1.首先,执行 top 发现负载很高,其次,发送CPU 利用率比较低,但是iowait 很高,高达63.4,再次发现进程中占CPU和内存最高的是一个python 应用。进程ID 5593
top - 15:21:09 up 22:38, 2 users, load average: 2.54, 2.27, 1.35
Tasks: 86 total, 3 running, 50 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.2 us, 16.4 sy, 0.0 ni, 16.9 id, 63.4 wa, 0.0 hi, 0.0 si, 0.2 st
KiB Mem : 8156288 total, 5366112 free, 1109200 used, 1680976 buff/cache
KiB Swap: 8388604 total, 8388080 free, 524 used. 6763836 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5593 root 20 0 963312 934276 5320 R 37.5 11.5 4:49.05 python
5648 root 20 0 0 0 0 D 1.3 0.0 0:03.47 kworker/u128:2
1043 root 20 0 1044268 64328 27400 S 0.7 0.8 2:02.97 dockerd
8 root 20 0 0 0 0 R 0.3 0.0 0:00.96 rcu_sched
3937 root 20 0 103864 7148 6148 S 0.3 0.1 0:00.60 sshd
1 root 20 0 225036 8624 6508 S 0.0 0.1 0:02.31 systemd
2.使用 iostat -d -x 1 查看所有磁盘的io情况,发现 vda 的util 高达98% 接近饱和,其次发现IO高的原因是因为往磁盘不停的写数据,平均每秒写入数据 105856KB
3.我们通过pidstat -d 1 查看是哪个进程占用IO高,发现果然是这个python 进程。
4.然后通过 strace 追踪Python 进程 5593 发现其一直在调用write函数向文件描述符为 3的文件写入文件,平均每秒写入数据 300 MB(314572844/1024/1024) 观察stat,他打开的是/tmp/logtest.txt 文件
root@linux:~# strace -p 5593
strace: Process 5593 attached
stat("/tmp/logtest.txt", {st_mode=S_IFREG|0644, st_size=943718535, ...}) = 0
rename("/tmp/logtest.txt", "/tmp/logtest.txt.1") = 0
open("/tmp/logtest.txt", O_WRONLY|O_CREAT|O_APPEND|O_CLOEXEC, 0666) = 3
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lseek(3, 0, SEEK_END) = 0
ioctl(3, TIOCGWINSZ, 0x7ffcc341db60) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = 0
ioctl(3, TIOCGWINSZ, 0x7ffcc341da80) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = 0
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f016accf000
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f01580ce000
write(3, "2019-03-29 07:26:28,703 - __main"..., 314572844) = 314572844
munmap(0x7f01580ce000, 314576896) = 0
write(3, "\n", 1) = 1
munmap(0x7f016accf000, 314576896) = 0
select(0, NULL, NULL, NULL, {tv_sec=0, tv_usec=100000}) = 0 (Timeout)
getpid() = 1
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f016accf000
mmap(NULL, 393220096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f01535ce000
mremap(0x7f01535ce000, 393220096, 314576896, MREMAP_MAYMOVE) = 0x7f01535ce000
munmap(0x7f016accf000, 314576896) = 0
lseek(3, 0, SEEK_END) = 314572845
lseek(3, 0, SEEK_CUR) = 314572845
munmap(0x7f01535ce000, 314576896) = 0
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f016accf000
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f01580ce000
write(3, "2019-03-29 07:26:37,480 - __main"..., 314572844) = 314572844
munmap(0x7f01580ce000, 314576896) = 0
5.我们进一步确认,使用lsof 查看该进程调用情况,发现其确实打开了文件/tmp/logtest.txt,这个输出界面 3w 表示3号文件描述符的权限为写(w)
root@linux:~# lsof -p 5593
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 5593 root cwd DIR 0,51 4096 131090 /
python 5593 root rtd DIR 0,51 4096 131090 /
python 5593 root txt REG 0,51 28016 266366 /usr/local/bin/python3.7
python 5593 root mem REG 252,1 266366 /usr/local/bin/python3.7 (stat: No such file or directory)
*****
python 5593 root 1u CHR 136,0 0t0 3 /dev/pts/0
python 5593 root 2u CHR 136,0 0t0 3 /dev/pts/0
python 5593 root 3w REG 252,1 916045824 2914 /tmp/logtest.txt
近日工作中遇到了一个磁盘压测时性能上不去的问题,经排查,发现原因有以下几个方面:
本文就将通过对磁盘性能测试指标及参数的介绍,来理解以上两个原因为什么会对测试结果有影响。
首先来介绍一下磁盘性能的测试指标。
最常用的磁盘性能评价指标有两个:IOPS和吞吐量(throughput)。
IOPS是Input/Output Per Second的缩写,它表示单位时间内系统能处理的I/O请求数量,即每秒钟系统能处理的读写次数。
吞吐量衡量单位时间内系统能处理的数据的体量,即每秒钟磁盘上能读写出的数据量的大小,通常以kB/s或MB/s为单位。
两个指标相互独立,又相互关联,在不同业务场景下,侧重关注的指标也有所不同。
对于文件尺寸小,随机读写比较多的场合,比如在线交易处理系统,我们倾向于更关注IOPS,因为我们更在乎的是每秒钟能处理多少条交易。
而对于文件尺寸较大,顺序读写比较多的场合,比如视频播放服务,数据吞吐量将会成为我们主要的考量指标。
举个例子来帮助我们更好的理解这两个指标。磁盘IO就相当于我们有货物(数据)需要从A处(系统)与B处(磁盘)之间往返。货物(数据量)有多有少,因此运货车也有大有小。B处有装卸工人负责将货物卸载到仓库的指定位置,或者从仓库指定位置提取货物装载到货车上。
每次货车运输一趟货物就相当于处理一个IO请求,工人装卸货物就相当于磁盘对IO的读写处理。在工人数量和工人装卸货物速度(磁盘数据处理速度)保持一定的情况下,装卸大车上货物的时间一定会比小车上的时间长,装卸一大车货物的时间,可能已经够小车运输若干趟货物(IOPS高)。但是小车由于多次往返,其花在路上的时间要比大车多,同时每次装卸货物工人需要寻找正确的位置存取货物(磁盘寻址时间),比起大车的一次寻址,小车运货就也浪费了更多时间。因此在相同时间内,采用大车运输的货物总量是比小车要多的(吞吐量高)。
这也是为什么我们在做磁盘性能测试的时候,通常一次只关注一个指标,追求IOPS,就用小车运输少量货物,多次往返。追求吞吐量,就用大车运送大量货物,节省路上及寻址所花费的时间。
下面再说一下磁盘测试的影响因素。
实际测量中,IOPS会受到很多因素的影响,比如:
1) 数据块大小:
相当于我们前面说的大车和小车运货的情况
2) 顺序和随机:
顺序就是我们的货物都按顺序安排在仓库的一处,随机则意味着货物随机的分配在仓库的不同地点,可以想见,货物地点存放比较随机的情况下,存取货物一定是更费时间的。
3 ) 队列深度:
如果我们每次只发一辆货车在AB之间往返,那么当货车在A处处理货物或者在AB之间的路上跑的时候,B处的工人就处于闲置的状态,压力测试时,我们绝对不希望这种情况发生,我们需要工人(磁盘)一直工作,从而得出磁盘的最高性能。想实现这一点,我们可以通过一次发多辆车来解决,保持始终有车辆在等待处理的队伍里,这样装卸工人就一直有工作可做了。
队列深度就是等待处理的队伍里的货车以及正在被装卸的货车的总数量。
4 ) 线程数
测试时,增加线程数也可以增加并发度,从而使装卸工人一直处于有工作可做的状态。
5) 读写比例
读操作相当于我们将货从B中的仓库取出来,运到A处就结束了。而写操作意味着货物在A处经过一番处理之后还要再运回B处并存储在仓库中。因此不同的读写比例也会造成测试结果的不同。
正是由于这些不同影响因素的存在,我们在对磁盘进行性能测试时,需要仔细选择测试参数,否则将无法测出磁盘的最优性能。同时应将测试参数和方法定性定量,否则测试结果将失去比较的价值。
以 云盘参数和性能测试方法:
https://help.aliyun.com/document_detail/25382.html
一文中介绍的测试IOPS的方法为例,我们来看一下linux常用测试工具fio的参数如何体现以上影响因素。
测试随机写IOPS:
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/[device] -name=Rand_Write_Testing
测试随机读IOPS:
fio -direct=1 -iodepth=128 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/[device] -name=Rand_Read_Testing
测试写吞吐量:
fio -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=1024k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/[device] -name=Write_PPS_Testing
测试读吞吐量:
fio -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=1024k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/[device] -name=Read_PPS_Testing
其中:
对比四个测试方法的参数我们可以看到,测试IOPS时我们采用小数据块(bs=4k),测试吞吐量时则用大数据块(bs=1024k)。这和我们前面说到的大货车小货车的选择原理是一致的。
队列深度对IOPS的影响要大于对吞吐量的影响,因为我们测试IOPS时选择的iodepth更大。但iodepth也不是越大越好,因为当装卸工人数量、装卸货物速度、仓库寻址时间一定之后,单位时间内所能处理的最大货物量也就确定了,即磁盘的能力确定了。一味增加队列深度,增加的只能是货物在队列里的等待时间,即平均IO响应时间。
我们可以通过查看装卸工人的忙碌程度来决定是否要增加队列深度。如果磁盘的busy%为100%,那就表示所有工人都在一刻不停歇的装卸货物了,已经不再有提升的空间,此时再增加队列深度或是数据量大小对测试结果都将是徒劳。反之,则表示磁盘压力尚未到极限,得出的数据不能代表磁盘性能最高水平。
磁盘压测时如果有其他业务逻辑在运行会怎样呢?这种情况就相当于有一部分货车装运的是业务逻辑的数据,而这些货车也会占用我们的队列和装卸工人,测试引擎将无法百分之百的使用全部队列和装卸工人,那么我们的测试结果将不能体现整个磁盘的能力。尤其是当业务逻辑所涉及的IO是同步(synchronous)请求的时候,对测试结果的影响将更大,因为同步IO就相当于前面说到的一次只让一辆车在路上跑,只有等它跑完才会发下一辆车。因此在压力测试的时候,我们需要将业务逻辑关闭的。
http://bean-li.github.io/dive-into-iostat/
http://linuxperf.com/?p=156
https://yq.aliyun.com/articles/218517?spm=a2c4e.11153959.0.0.184127eeuKmmwp