磁盘io,顾名思义就是磁盘的输入输出。即向磁盘写入数据和从磁盘读取数据。

I/O 读写的类型,大体上讲,I/O 的类型可以分为:

读 / 写 I/O、大 / 小块 I/O、连续 / 随机 I/O, 顺序 / 并发 I/O。在这几种类型中,我们主要讨论一下:大 / 小块 I/O、连续 / 随机 I/O, 顺序 / 并发 I/O。

1,读 / 写 I/O

磁盘是用来给我们存取数据用的,因此当说到IO操作的时候,就会存在两种相对应的操作,存数据时候对应的是写IO操作,取数据的时候对应的是是读IO操作。

当控制磁盘的控制器接到操作系统的读IO操作指令的时候,控制器就会给磁盘发出一个读数据的指令,并同时将要读取的数据块的地址传递给磁盘,然后磁盘会将读取到的数据传给控制器,并由控制器返回给操作系统,完成一个读IO的操作;同样的,一个写IO的操作也类似,控制器接到写的IO操作的指令和要写入的数据,并将其传递给磁盘,磁盘在数据写入完成之后将操作结果传递回控制器,再由控制器返回给操作系统,完成一个写IO的操作。单个IO操作指的就是完成一个写IO或者是读IO的操作。

2,大 / 小块 I/O

这个数值指的是控制器指令中给出的连续读出扇区数目的多少。如果数目较多,如 64,128 等,我们可以认为是大块 I/O;反之,如果很小,比如 4,8,我们就会认为是小块 I/O,实际上,在大块和小块 I/O 之间,没有明确的界限。

3,连续 / 随机 I/O

连续 I/O 指的是本次 I/O 给出的初始扇区地址和上一次 I/O 的结束扇区地址是完全连续或者相隔不多的。反之,如果相差很大,则算作一次随机 I/O

连续 I/O 比随机 I/O 效率高的原因是:在做连续 I/O 的时候,磁头几乎不用换道,或者换道的时间很短;而对于随机 I/O,如果这个 I/O 很多的话,会导致磁头不停地换道,造成效率的极大降低。

4,顺序 / 并发 I/O

从概念上讲,并发 I/O 就是指向一块磁盘发出一条 I/O 指令后,不必等待它回应,接着向另外一块磁盘发 I/O 指令。对于具有条带性的 RAID(LUN),对其进行的 I/O 操作是并发的,例如:raid 0+1(1+0),raid5 等。反之则为顺序 I/O。

二,影响磁盘性能的因素

    传统磁盘本质上一种机械装置,如FC, SAS, SATA磁盘,转速通常为5400/7200/10K/15K rpm不等。影响磁盘的关键因素是磁盘服务时间,即磁盘完成一个I/O请求所花费的时间,它由寻道时间、旋转延迟和数据传输时间三部分构成。

1,寻道时间

Tseek是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms。

2,旋转延迟

Trotation是指盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间。旋转延迟取决于磁盘转速,通常使用磁盘旋转一周所需时间的1/2表示。比如,7200 rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms,而转速为15000 rpm的磁盘其平均旋转延迟为2ms。

3,数据传输时间

Ttransfer是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。目前IDE/ATA能达到133MB/s,SATA II可达到300MB/s的接口数据传输率,数据传输时间通常远小于前两部分消耗时间。简单计算时可忽略。

 常见磁盘平均物理寻道时间为:

7200转/分的STAT硬盘平均物理寻道时间是10.5ms

10000转/分的STAT硬盘平均物理寻道时间是7ms

15000转/分的SAS硬盘平均物理寻道时间是5ms

 常见硬盘的旋转延迟时间为:

7200   rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms

10000 rpm的磁盘平均旋转延迟大约为60*1000/10000/2 = 3ms,

15000 rpm的磁盘其平均旋转延迟约为60*1000/15000/2 = 2ms。

最大IOPS的理论计算方法:

IOPS = 1000 ms/ (寻道时间 + 旋转延迟)。可以忽略数据传输时间。

7200   rpm的磁盘 IOPS = 1000 / (10.5 + 4.17)  = 68 IOPS

10000 rpm的磁盘IOPS = 1000 / (7 + 3) = 100 IOPS

15000 rpm的磁盘IOPS = 1000 / (5 + 2) = 142 IOPS

影响测试的因素:

实际测量中,IOPS数值会受到很多因素的影响,包括I/O负载特征(读写比例,顺序和随机,工作线程数,队列深度,数据记录大小)、系统配置、操作系统、磁盘驱动等等。因此对比测量磁盘IOPS时,必须在同样的测试基准下进行,即便如此也会产生一定的随机不确定性。

三,衡量性能的重要指标

    我们常见的磁盘类型有 ATA、SATA、FC、SCSI、SAS。这几种磁盘中,服务器常用的是 SAS 和 FC 磁盘,一些高端存储也使用 SSD 盘。每一种磁盘的性能是不一样的,机械硬盘的连续读写行都很好,但随机读写性能很差,这主要是因为磁头移动到正确的磁道上需要时间,随机读写时,磁头需要不停的移动,时间都浪费在了磁头寻址上,所以性能不高。当存储小文件时,随机读写的IOPS将是重要指标;当存储像视频大文件时,顺序读写IOPS将是重要指标。

主要有两个指标:

1,IOPS

就是在一秒内,磁盘进行多少次 I/O 读写。决定IOPS的主要取决与阵列的算法,cache命中率,以及磁盘个数。阵列的算法因为不同的阵列不同而不同,如我们最近遇到在hds usp上面,可能因为ldev(lun)存在队列或者资源限制,而单个ldev的iops就上不去。cache的命中率取决于数据的分布,cache size的大小,数据访问的规则,以及cache的算法。我这里只强调一个cache的命中率,如果一个阵列,读cache的命中率越高越好,一般表示它可以支持更多的IOPS。硬盘的限制,每个物理硬盘能处理的IOPS是有限制的,如果一个阵列有120块15K rpm的光纤硬盘,那么,它能撑的最大IOPS为120*150=18000,这个为硬件限制的理论值,如果超过这个值,硬盘的响应可能会变的非常缓慢而不能正常提供业务。

2,吞度量

也叫磁盘带宽,每秒磁盘 I/O 的流量,即磁盘写入和读出的数据的总大小。他主要取决于磁盘阵列的构架,通道的大小以及磁盘的个数。不同的磁盘阵列存在不同的构架,但他们都有自己的内部带宽(如主线型或星型),不过一般情况下,内部带宽都设计足够充足,不会存在瓶颈。磁盘阵列与服务器之间的数据通道便对吞吐量的影响很大。一般情况下,因为磁盘实际使用的吞吐率一旦超过磁盘吞吐量的85%,就会出现I/O瓶颈。下面是常用通道的带宽:

2Gbps 光纤通道,(250MB/s), 4Gbps 光纤通道(500MB/S),SCSI最高速度是320MB/s,SATA是150MB/s,IED 133MB/s。最后说一下是硬盘的限制,目前SCSI硬盘数据传输率最高在80MB/s,SAS硬盘数据为传输率最高在80-100MB/S。对于数据库小数据的离散写入,其传输率快快达不到这个值,主要原因是因为磁盘寻址等浪费太多时间。

下面举例来说明:

如果写一个10M的文件需要0.1S,则磁盘计算出磁盘带宽为100M/s,如果写10000个大小为1KB的文件需要10S,则磁盘带宽只有1M/s。

IOPS 与吞吐量的关系:

每秒 I/O 吞吐量= IOPS* 平均 I/O SIZE。从公式可以看出: I/O SIZE 越大,IOPS 越高,那么每秒 I/O 的吞吐量就越高。因此,我们会认为 IOPS 和吞吐量的数值越高越好。实际上,对于一个磁盘来讲,这两个参数均有其最大值,而且这两个参数也存在着一定的关系。

四,测试磁盘性能的工具

1,下载并安装fio工具:

# git clone git://git.kernel.dk/fio.git

# yum install libaio-devel
# cd fio

# ./configure
# make
# make install

2,异步io性能测试:

不同的应用会使用不同的io类型进行io读写,故不同的应用应该使用不同的io engine做测试。

异步的话就是用类似libaio这样的linux native aio一次提交一批,然后等待一批的完成,减少交互的次数,会更有效率。

# cat nvdisk-test
[global]
bs=512
ioengine=libaio
userspace_reap
rw=randrw
rwmixwrite=20
time_based
runtime=180
direct=1
group_reporting
randrepeat=0
norandommap
ramp_time=6
iodepth=16
iodepth_batch=8
iodepth_low=8
iodepth_batch_complete=8
exitall

size=5G
[test]
filename=/data/test.data
numjobs=1

使用的参数和选项如下,更多的说明请参考man fio:

bs=16k 单次io的块文件大小为16k
ioengine io引擎使用异步libaio方式
userspace_reap libaio特有选项,默认fio使用io_getevents系统调用收割新返回的时间,开启这个选项后直接在用户空间完成收割。
rw=randwrite 测试随机写的I/O
rwmixwrite 在混合读写的模式下,写占的百分百
time_based 没有到指定的runtime时间,但测试任务已经完成,程序仍然不停止,继续重复测试,直到到指定runtime时间。
runtime 运行多少s
direct=1 测试过程绕过机器自带的buffer。使测试结果更真实。异步io模型测试,必须开启
group_reporting 关于显示结果的,汇总每个进程或这线程的信息。
randrepeat  对于随机IO负载,配置生成器,使得路径是可以预估的,使得每次重复执行生成的序列是一样的
norandommap 一般情况下,fio在做随机IO时,将会覆盖文件的每一个block。如果这个选项设置的话,fio将只是获取一个新的随机offset,而不会查询过去的历史。这意味着一些块可能没有读或写,一些块可能要读/写很多次。
ramp_time 设定在记录任何性能信息之前要运行特定负载的时间。这个用来等性能稳定后,再记录日志结果
iodepth=16
iodepth_batch=8
iodepth_low=8
iodepth_batch_complete=8
libaio异步引擎会用这个iodepth值来调用io_setup准备个可以一次提交iodepth个IO的上下文,同时申请个io请求队列用于保持IO。 在压测进行的时候,系统会生成特定的IO请求,往io请求队列里面扔,当队列里面的IO个数达到iodepth_batch值的时候,就调用io_submit批次提交请求,然后开始调用io_getevents开始收割已经完成的IO。 每次收割多少呢?由于收割的时候,超时时间设置为0,所以有多少已完成就算多少,最多可以收割iodepth_batch_complete值个。随着收割,IO队列里面的IO数就少了,那么需要补充新的IO。 什么时候补充呢?当IO数目降到iodepth_low值的时候,就重新填充,保证OS可以看到至少iodepth_low数目的io在电梯口排队着。
size 指定本次测试数据文件的大小,默认情况下单个磁盘块为4k。
exitall 当一个job完成,就退出
filename  一般情况下,fio会根据job名,线程号,文件号来产生一个文件名。如果,想在多个job之间共享同一个文件的话,可以通过这个参数设定一个文件名字来代替默认的名字。这个文件必须是在要测试的磁盘整列上。
numjobs 本次的测试进程数

fio任务配置里面有几个点需要非常注意:
1. libaio工作的时候需要文件direct方式打开。
2. 块大小必须是扇区(512字节)的倍数。
3. userspace_reap提高异步IO收割的速度。
4. ramp_time的作用是减少日志对高速IO的影响。
5. 只要开了direct,fsync就不会发生。

3,同步io性能测试案例:

顺序读:
fio -filename=/data/test.data -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=16k -size=5G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

随机写:
fio -filename=/data/test.data -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=5G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

顺序写:
fio -filename=/data/test.data -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=16k -size=5G -numjobs=30 -runtime=1000 -group_reporting -name=mytest

混合随机读写:
fio -filename=/data/test.data -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=5G -numjobs=30 -runtime=100 -group_reporting -name=mytest -ioscheduler=noop