在专栏一开始的时候,我和你说过,在计算机组成原理这门课里面,很多设计的核心思路,都来源于性能。在前免讲解CPU的时候,相信你已经有了切身的感受了。
大部分程序员开发的都是应⽤系统。在开发应用系统的时候,我们遇到的性能瓶颈大部分都在I/O上。在第36讲讲解局部性原理的时候,我们一起看了通过把内存当作是缓存,
来提升系统的整体性能。在第37讲讲解CPU Cache的时候,我们一起看了CPU Cache和主内存之间性能的巨大差异
然而,我们知道,并不是所有问题都能靠利⽤内存或者CPU Cache做一层缓存来解决。特别是在这个“大数据”的时代。我们在硬盘上存储了越来越多的数据,
一个MySQL数据库的单表有个几千万条记录,早已经不算是什么罕见现象了。这也就意味着,用内存当缓存,存储空间是不够用的。大部分时间,
我们的请求还是要打到硬盘上。那么,这⼀讲我们就来看看硬盘I/O性能的事儿。
一、随机IO
1、硬盘厂商
2、数据传输率
HDD硬盘
SSD硬盘
3、响应时间
Acc.Time
指标和日常的经验不符啊
光看响应时间和吞吐率这两个指标,似乎我们的硬盘性能很不错、我们平时往数据库里写入一条记录,也就是1KB左右的大小。我们拿200MB去除以1KB,
也能够在几毫秒时间返回、一秒钟能够传输的数据,也有200MB左右
顺序读写和随机读写
随机读写
顺序读写
二、OPS和DTR才是输入输出性能的核心指标
实际开发中对数据的访问
服务器承受的“并发”
三、性能瓶颈在
硬盘性能
CPU性能
和我们硬盘能够进⾏的操作数,也有好一个数量级的差异,因为很多时候,CPU指令发出去之后,不得不去“等”我们的I/O操作完成,才能进入下一步的操作
四、如何定位IO_WAIT?
那么,在实际遇到服务端程序的性能问题的时候,我们怎么知道这个问题是不是来自于CPU等I/O来完成操作呢?别着急,我们接下来,
就通过top和iostat这些命令,一起来看看CPU到底有没有在等待io操作。
1、top
top - 06:26:30 up 4 days, 53 min, 1 user, load average: 0.79, 0.69, 0.65 Tasks: 204 total, 1 running, 203 sleeping, 0 stopped, 0 zombie %Cpu(s): 20.0 us, 1.7 sy, 0.0 ni, 77.7 id, 0.0 wa, 0.0 hi, 0.7 si, 0.0 st KiB Mem: 7679792 total, 6646248 used, 1033544 free, 251688 buffers KiB Swap: 0 total, 0 used, 0 free. 4115536 cached Mem
2、iowat
avg-cpu: %user %nice %system %iowait %steal %idle 17.02 0.01 2.18 0.04 0.00 80.76 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 1.81 2.02 30.87 706768 10777408
你会看到,这个命令⾥,不仅有iowait这个CPU等待时间的百分⽐,还有⼀些更加具体的指标了,并且它还是按照你机器上安装的多块不同的硬盘划分的。
这⾥的tps指标,其实就对应着我们上⾯所说的硬盘的IOPS性能。⽽kB_read/s和kB_wrtn/s指标,就对应着我们的数据传输率的指标。
知道实际硬盘读写的tps、kB_read/s和kb_wrtn/s的指标,我们基本上可以判断出,机器的性能是不是卡在I/O上了。那么,接下来,
我们就是要找出到底是哪一个进程是这些I/O读写的来源了。这个时候,你需要“iotop”这个命令。
3、iotop
iotop Total DISK READ : 0.00 B/s | Total DISK WRITE : 15.75 K/s Actual DISK READ: 0.00 B/s | Actual DISK WRITE: 35.44 K/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 104 be/3 root 0.00 B/s 7.88 K/s 0.00 % 0.18 % [jbd2/sda1-8] 383 be/4 root 0.00 B/s 3.94 K/s 0.00 % 0.00 % rsyslogd -n [rs:main Q:Reg] 1514 be/4 www-data 0.00 B/s 3.94 K/s 0.00 % 0.00 % nginx: worker process
通过iotop这个命令,你可以看到具体是哪⼀个进程实际占⽤了⼤量I/O,那么你就可以有的放⽮,去优化对应的程序了。
上面的这些示例里,不管是wa也好,tps也好,它们都很小。那么,接下来,我就给你用Linux下,用stress命令,来模拟一个高I/O复杂的情况,来看看这个时候的iowait是怎么样的。
4、完整案例
我在一台云平台上的单个CPU核⼼的机器上输⼊“stress-i2”,让stress这个程序模拟两个进程不停地从内存里往硬盘上写数据。
stress -i 2
top查看负载和wa
top top - 06:56:02 up 3 days, 19:34, 2 users, load average: 5.99, 1.82, 0.63 Tasks: 88 total, 3 running, 85 sleeping, 0 stopped, 0 zombie %Cpu(s): 3.0 us, 29.9 sy, 0.0 ni, 0.0 id, 67.2 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1741304 total, 1004404 free, 307152 used, 429748 buff/cache KiB Swap: 0 total, 0 free, 0 used. 1245700 avail Mem
iowat查看iops
iostat 2 5 avg-cpu: %user %nice %system %iowait %steal %idle 5.03 0.00 67.92 27.04 0.00 0.00 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 39762.26 0.00 0.00 0 0
如果我们通过iostat,查看硬盘的I/O,你会看到,里面的tps很快就到了4万左右,占满了对应硬盘的IOPS。
iotop查看io占用排行榜
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 29161 be/4 xuwenhao 0.00 B/s 0.00 B/s 0.00 % 56.71 % stress -i 2 29162 be/4 xuwenhao 0.00 B/s 0.00 B/s 0.00 % 46.89 % stress -i 2 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init
相信到了这里,你也应该学会了怎么通过top、iostat以及iotop,一步一步快速定位服务器端的I/O带来的性能瓶颈了。
你也可以自己通过Linux的man命令,看一看这些命令还有哪些参数,以及通过stress来模拟其他更多不同的性能压力,看看我们的机器负载会发生什么变化。
五、总结延伸
这一讲里,我们从硬盘的两个核心指标,响应时间和数据传输率,来理解和研究I/O的性能问题。你也自己可以通过as ssd这样的性能评测软件,看一看自己的硬盘性能。
在顺序读取的情况下,无论是HDD硬盘还是SSD硬盘,性能看起来都是很不错的。不过,等到进行随机读取测试的时候,硬盘的性能才能见了真章。因为在大部分的应用开发场景下,
我们关注的并不是在顺序读写下的数据量,而是每秒钟能够进行输入输出的操作次数,也就是IOPS这个核心性能指标。
你会发现,即使是使PCI Express接口的SSD硬盘,IOPS也就只是到了2万左右。这个性能,和我们CPU的每秒20亿次操作的能⼒⽐起来,可就差得远了。
所以很多时候,我们的程序对外响应慢,其实都是CPU在等待I/O操作完成。
在Linux下,我们可以通过top这样的命令,来看整个服务器的整体负载。在应⽤响应慢的时候,我们可以先通过这个指令,来看CPU是否在等待I/O完成⾃⼰的操作。
进一步地,我们可以通过iostat这个命令,来看到各个硬盘这个时候的读写情况。⽽iotop这个命令,能够帮助我们定位到到底是哪⼀个进程在进行大量的I/O操作。
这些命令的组合,可以快速帮你定位到是不是我们的程序遇到了I/O的瓶颈,以及这些瓶颈来自于哪些程序,你就可以根据定位的结果来优化你自己的程序了。