说到IO性能,就不得不说我曾经干过的一件傻事,这件事情让我记忆犹新。那还是我在xxx干活的时候,有一次需要进行全量上线,说到上线,那肯定就要对当前应用进行备份了。这个备份的活我都干了好久了,每次都是那么几个命令,先tar
,然后再gzip
,每次都是一个接一个的备份,而备份一个又要好久;然后我就想偷懒,耍个小聪明,写了一个脚本,一次同时并行备份所有的文件,然后启动脚本就OK了,也不需要人值守。我就这么干了,没过多久,前台使用人员就反馈系统反应慢,我一听,慌了,立马看了下,备份脚本占用了大量的IO,导致IO急剧飙升,导致系统整体性能下降。我都佩服我这个小机灵鬼,立马发现了问题,解决了问题。如何发现问题,请各位继续往下看。
说到IO,就得先说说存储系统。我们知道,在存储系统里面,越靠近CPU的(比如CPU的缓存),价格越贵、容量越小、速度越快;越远离CPU的(比如磁盘),价格越便宜、容量越大、速度越慢。而磁盘通常是计算机最慢的子系统,也是最容易出现性能瓶颈的地方,因为磁盘离CPU距离最远而且CPU访问磁盘要涉及到机械操作,比如转轴、寻轨等。访问硬盘和访问内存之间的速度差别是以数量级来计算的,就像1天和1分钟的差别一样。要监测IO性能,就有必要了解一下基本原理和Linux是如何处理硬盘和内存之间的IO的。
先说理论
而要说IO性能,就不得不先说说缺页中断了。Linux利用虚拟内存极大的扩展了程序地址空间,使得原来物理内存不能容下的程序也可以通过内存和硬盘之间的不断交换(把暂时不用的内存页交换到硬盘,把需要的内存页从硬盘读到内存)来赢得更多的内存,看起来就像物理内存被扩大了一样。事实上这个过程对程序是完全透明的,程序完全不用理会自己哪一部分、什么时候被交换进内存,一切都有内核的虚拟内存管理来完成。当程序启动的时候,Linux内核首先检查CPU的缓存和物理内存,如果数据已经在内存里就忽略,如果数据不在内存里就引起一个缺页中断(Page Fault),然后从硬盘读取缺页,并把缺页缓存到物理内存里。缺页中断可分为主缺页中断(Major Page Fault)和次缺页中断(Minor Page Fault),要从磁盘读取数据而产生的中断是主缺页中断;数据已经被读入内存并被缓存起来,从内存缓存区中而不是直接从硬盘中读取数据而产生的中断是次缺页中断。
上面的内存缓存区起到了预读硬盘的作用,内核先在物理内存里寻找缺页,没有的话产生次缺页中断从内存缓存里找,如果还没有发现的话就从硬盘读取。很显然,把多余的内存拿出来做成内存缓存区提高了访问速度,这里还有一个命中率的问题,运气好的话如果每次缺页都能从内存缓存区读取的话将会极大提高性能。要提高命中率的一个简单方法就是增大内存缓存区面积,缓存区越大预存的页面就越多,命中率也会越高。
内存缓存区(也叫文件缓存区File Buffer Cache)读取页比从硬盘读取页要快得多,所以Linux内核希望能尽可能产生次缺页中断(从文件缓存区读),并且能尽可能避免主缺页中断(从硬盘读),这样随着次缺页中断的增多,文件缓存区也逐步增大,直到系统只有少量可用物理内存的时候Linux才开始释放一些不用的页。我们运行Linux一段时间后会发现虽然系统上运行的程序不多,但是可用内存总是很少,这样给大家造成了Linux对内存管理很低效的假象,事实上Linux把那些暂时不用的物理内存高效的利用起来做预存(内存缓存区)了。
比如,以下这样的一个输出:
[root@Test_Server ~]# cat /proc/meminfo MemTotal: 1883496 kB MemFree: 137236 kB MemAvailable: 286512 kB Buffers: 107028 kB Cached: 150352 kB
可以看到内存一共大概2G(MemTotal),其中大概130M可用(MemFree),100多M用来做磁盘缓存(Buffers),150多M用来做文件缓存(Cached)。
好了,有了这些基本知识,我们再来看IO性能的分析。
再说实干
说到实际的,那就要说说实际运维工作中,我们如何发现IO性能瓶颈。
我们进行IO分析,第一步还是需要祭出我们的神器vmstat
,关于vmstat
如何使用可以参见这篇《Linux vmstat命令详解》,通过vmstat
的输出,重点关注b
、bi
、bo
和wa
字段。这几个值变大,都意味着IO的消耗增加。对于读请求大的服务器,一般b
、bi
、wa
都会比较大,而对于写入量大的服务器,一般b
、bo
、wa
都会比较大。
接下来就是使用第二个神器iostat
。通过这个神器,我们就可以监视具体的磁盘性能了。对于iostat
这个命令的具体教程,可以参见这篇《Linux iostat命令详解》。
但是,在实际运维工作中,我们都希望找到是哪个进程消耗了IO,所以我们的终极目的是找到这个进程ID。可是通过vmstat
和iostat
都没法达到我们的目的。所以,神器pidstat
命令就登场了,通过这个命令,我们就可以知道是谁在后台偷用IO了。我想我整理的这篇《Linux pidstat命令详解》对你应该有一些帮助。
现在定位到进程级别了,很多时候,我们需要知道这个进程到底打开了哪些文件,这个进程到底和哪些进程关联,这个时候就不得不提到lsof
命令了。