PC用户往往觉得“磁盘挺快的,哥拷1GB的片子也就2分钟嘛”
做服务器的兄弟可能会觉得“磁盘怎么这么慢,才1MB/s就把io跑满了,他喵的磁盘比网速还慢,害我的服务器卡死了”
为什么有时候服务器的磁盘io会这么慢呢?
我们用的磁盘(IDE/SATA/SCSI等)还有一个名字,叫做“机械磁盘”。
从名字可以看出,磁盘并不是一个纯粹的“电子产品”,它在很大程度上需要依靠一个“机械臂”来读写数据,这个机械臂就是导致磁盘io慢的罪魁祸首。
如上图所示,磁盘中的数据存储在圆形的盘片上,通过磁头读写盘片上的数据,磁头则安装在一个机械臂(磁头臂)上。
当我们需要读写某个文件时,内核会将文件映射到一个线性磁盘地址(LBA)。磁盘首先根据LBA找到盘片上的一个点,然后让磁头对准这个点,再通过磁头将数据读取出来。
读取文件耗费的时间分为两部分:
1 让磁头定位到指定位置的时间(平均寻道时间)
2 磁头从盘片上读出数据的时间(传输时间)
如何让磁头定位到指定位置:
这涉及两个方向的移动。磁头会在机械臂的控制下向圆心移动(或远离圆心);同时盘片会旋转。
磁头移动到合适位置需要的时间叫平均寻道时间,一般在10毫秒左右。盘片旋转到指定位置需要的时间叫平均潜伏时间,对7200转磁盘来说大概是4毫秒。
1秒钟 ÷(4毫秒 + 10毫秒) = 71
这表示什么?
这表示磁盘平均每秒只能定位71次!!!
简单来说,可以理解成磁盘每秒只能读取71个文件(实际情况略有偏差,请参考下面第二章)
假如服务器存储了大量小文件,每个文件10KB,按照每秒71次的磁头定位速度,每秒读文件就只能达到710KB/s的速度。
那为什么PC拷片子的速度这么快呢?
当我们拷片子时,磁头基本不需要移动,主要耗时在于通过盘片的旋转,让磁头从盘片上读出数据。
盘片的旋转速度远远高于磁头移动速度。读取连续文件的速度一般能达到100MB/s以上,所以PC拷片子的时候速度非常快。
磁盘平均每秒能寻道70次,但是实际读写文件可能达不到70个,因为文件内也是有碎片的,这时读一个文件就需要很多次寻道。
我们可以把磁盘空间想象成一个巨大的内存,LBA(线性磁盘地址)就相当于内存地址。文件系统需要为每个文件分配地址,就像malloc要为每个内存块分配内存一样。
磁盘空间分配有两个特征,会导致文件系统中容易产生碎片:
1 当我们创建文件时,文件是空的,随着我们写入数据,文件变得越来越大。所以磁盘空间分配不是一次性完成的,而是随着文件写入而逐渐追加分配。
2 文件不需要在磁盘空间中连续存储,允许切割成多个碎片。但是不连续的代价是读取时需要多次寻道,性能大幅下降。
windows文件系统一直以大量的文件碎片而闻名;linux主流文件系统的碎片则相对少很多。
有不少朋友认为linux没有文件碎片,这其实是一个误解。
linux主流文件系统相对于windows文件系统,有一个巨大的改进,极大的减少了文件碎片,但这不等于没有文件碎片:
windows在分配LBA时会尽量连续分配:
假设文件A占用了地址0-99KB,然后文件B申请8KB磁盘空间时会占用100-108KB。这样当文件A再次申请磁盘空间时就会产生碎片。
linux在分配LBA时会考虑预留地址:
假设文件A占用了地址0-99KB,然后文件B申请8KB磁盘空间时会隔一段地址,例如可能会占用1GB-1GB+8KB。这样当文件A再次申请磁盘空间时就能保证连续了。
当磁盘有较多剩余空间时,linux主流文件系统能够有效避免文件碎片。对于PC和部分服务器来说,可以认为linux主流文件系统是没有碎片的。
但是对于磁盘空间经常占的比较满,又需要不停的删除-写入文件的linux服务器来说,文件碎片会让磁盘io性能下降数倍。
想深入了解的朋友可以参考:
ext4的碎片整理器设计原理:http://jsmylinux.no-ip.org/applications/using-e4defrag/
xfs文件系统作者讲解如何提升文件系统性能并减少碎片的产生:http://oss.sgi.com/projects/xfs/papers/xfs_usenix/index.html
内核预读取的基本原理:http://os.51cto.com/art/200711/60574.htm
也可以直接看内核代码中的ext4_ext_map_block函数