磁盘通常是计算机最慢的子系统,也是最容易出现性能瓶颈的地方,
因为磁盘离 CPU 距离最远而且 CPU 访问磁盘要涉及到机械操作,比如转轴、寻轨等。
访问硬盘和访问内存之间的速度差别是以数量级来计算的,就像1天和1分钟的差别一样。
要监测 IO 性能,有必要了解一下基本原理和 Linux 是如何处理硬盘和内存之间的 IO 的。

物理结构

硬盘VS固态硬盘
结构如下图所述

硬盘

常见的硬盘根据接口类型有SAS, SATA磁盘两种,以下是一个2T SATA盘的参数

描述 参数
接口类型 SATA 6 Gb/s
容量 2 TB
缓存 64 MB
转速 5400RPM

每个硬盘都有一个磁头(相当于银行的柜台),硬盘的工作方式是:

  • 收到IO请求,得到地址和数据大小
  • 移动磁头(寻址)
  • 找到相应的磁道(寻址)
  • 读取数据
  • 传输数据
  • 则磁盘的随机IO服务时间:

服务时间 = 寻道时间 + 旋转时间 + 传输时间

对于10000转速的SATA硬盘来说,一般寻道时间是7 ms,旋转时间是3 ms, 64KB的传输时间是 0.8 ms,
则SATA硬盘每秒可以进行随机IO操作是 1000/(7 + 3 + 0.8) = 93,
所以我们估算SATA硬盘64KB随机写的IOPS是93。一般的硬盘厂商都会标明顺序读写的MBPS。

我们在列出IOPS时,需要说明IO大小,寻址空间,读写模式,顺序/随机,队列深度。
我们一般常用的IO大小是4KB,这是因为文件系统常用的块大小是4KB。

固态硬盘

SSD的延时很低,并行度很高,多个nand(上图中的储存芯片)同时工作,,缺点是寿命和GC造成的响应时间不稳定

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。

测试工具

### dd(磁盘的顺序读写性能测试)

dd是最为常用并且简单的磁盘io性能测试工具,它主要测试顺序读写的环境下,小文件的读写IOQPS和大文件读写的吞吐(即block较小时,测试的主要是iops。block较大时,测试的主要是吞吐)。

磁盘IO有BufferIO、DirectIO两种,其中BufferIO的读写会经过设备->内核->用户的一种转换,而DirectIO是直接从设备->用户,因此使用DirectIO可以更好的了解磁盘的读写性能

iops—写测试

1
2
3
4
5
6
7
8
9
10
11
dd if=/dev/zero of=./a.dat bs=8k count=1M oflag=direct
----------------------HDD--------------------------
1048576+0 records in
1048576+0 records out
8589934592 bytes (8.6 GB) copied, 102.629 s, 83.7 MB/s
那么IOQPS=1M/102.629=1 W/s,吞吐=83.7MB/s
----------------------SSD--------------------------
1048576+0 records in
1048576+0 records out
8589934592 bytes (8.6 GB) copied, 47.5207 s, 181 MB/s
那么IOQPS=1M/47.5207=2.2 W/s,吞吐=181MB/s

iops—读测试

1
2
3
4
5
6
7
8
9
10
11
dd if=./a.dat of=/dev/null bs=8k count=1M iflag=direct
----------------------HDD--------------------------
1048576+0 records in
1048576+0 records out
8589934592 bytes (8.6 GB) copied, 102.062 s, 84.2 MB/s
那么IOQPS=1M/102.629=1 W/s,吞吐=84.2MB/s
----------------------SSD--------------------------
1048576+0 records in
1048576+0 records out
8589934592 bytes (8.6 GB) copied, 73.1328 s, 117 MB/s
那么IOQPS=1M/73=1.4 W/s,吞吐=117 MB/s

小数据的顺序读写,相比HDD,SSD的提升不是很明显,才1.5~2倍!!

吞吐—写测试

1
2
3
4
5
6
7
8
9
10
11
dd if=/dev/zero of=./a.dat bs=1M count=8k oflag=direct
----------------------HDD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 104.326 s, 82.3 MB/s
那么IOQPS=8k/104=78/s,吞吐=82.3 MB/s变化不大
----------------------SSD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 5.15269 s, 1.7 GB/s
那么IOQPS=8k/5=1638/s,吞吐=1.7 GB/s

吞吐—读测试

1
2
3
4
5
6
7
8
9
10
11
dd if=./a.dat of=/dev/null bs=1M count=8k iflag=direct
----------------------HDD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 99.2913 s, 86.5 MB/s
那么IOQPS=8k/99=82/s,吞吐=86.5 MB/s
----------------------SSD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 5.22082 s, 1.6 GB/s
那么IOQPS=8k/5=1638/s,吞吐=1.6 GB/s

对于大块数据的读写,SSD提升还是很明显,接近20倍!

不过BufferIO在对大文件的读写测试还是有很大用处,即特别是大文件的吞吐量的测试,不过测试过程中,需要增加一个conv=fdatasync,使用该参数,在完成所有读写后会调用一个sync确保数据全部刷到磁盘上,否则就是主要在测内存读写了;另外还有一个参数是oflag=dsync,使用该参数也是走的BufferIO,但却是会在每次IO操作后都执行一个sync。

吞吐—写测试

1
2
3
4
5
6
7
8
9
10
11
dd if=/dev/zero of=./a.dat bs=1M count=8k conv=fdatasync
----------------------HDD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 112.771 s, 76.2 MB/s
那么IOQPS=8k/112=73/s,吞吐=76.2 MB/s变化不大
----------------------SSD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 10.4019 s, 826 MB/s
那么IOQPS=8k/10s=819/s,吞吐=1.6 GB/s

大数据的写,BufferIO相比DirectIO,吞吐和iops都有下降,原因是数据最终都是要落盘,但是BufferIO落盘前经过一次内核buffer的缓存时间,算是浪费了!

吞吐—读测试

1
2
3
4
5
6
7
8
9
dd if=./a.dat of=/dev/null bs=1M count=8k
----------------------HDD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 2.77366 s, 3.1 GB/s
----------------------SSD--------------------------
8192+0 records in
8192+0 records out
8589934592 bytes (8.6 GB) copied, 3.27296 s, 2.6 GB/s

BufferIO的读就不一样了,buffer的存储,基本就相当于读内存了

fio

hdparam

(磁盘参数修改,支持对磁盘顺序读写性能进行测试)

hdparam可以针对磁盘每个分区进行性能测试了,也可以修改分配和磁盘的配置参数,需要root指向权限;
针对参数的需求,这里就不描述了,这里就看一下怎么测试分区的读写性;

1
2
3
4
5
6
7
8
9
----------------------HDD--------------------------
hdparm -Tt /dev/sda6
Timing cached reads: 12144 MB in 2.00 seconds = 6081.48 MB/sec
Timing buffered disk reads: 322 MB in 3.01 seconds = 106.81 MB/sec
----------------------SSD--------------------------
hdparm -Tt /dev/sdb1
Timing cached reads: 13244 MB in 2.00 seconds = 6632.48 MB/sec
Timing buffered disk reads: 2268 MB in 3.00 seconds = 755.97 MB/sec
//这个数值是读写的平均数据,HDD与SSD之间的7倍平均性能差也是大家一致公认的一个平均值!

hdtunepro

下图是一个三星的固态测试样图
ssd.png

参考
http://www.vpsee.com/2009/11/linux-system-performance-monitoring-io/
http://way4ever.com/?p=465
https://community.emc.com/message/740198#740198
http://support.huawei.com/huaweiconnect/enterprise/thread-208653-1-1.html
http://www.cnblogs.com/wuchanming/p/3789512.html
https://github.com/ColZer/DigAndBuried/blob/master/system/disk-io.md