目录

Linux操作系统-磁盘IO测试

常用的测试磁盘 IO 性能的命令.

工具

日常运维少不了对服务器的磁盘 IO 性能进行测试的操作,记这些命令太费脑子了,每次都要去百度又比较麻烦,所以将常用的命令记录下来,以备后续使用可以快速查找。

常用的工具有:

  • dd

  • fio

dd

我们可以利用 dd 命令测试某个磁盘的 IO 性能,须要注意的是 dd 命令只能大致测出磁盘的 IO 性能,不是非常准确。

测试写性能命令:

1
2
3
4
dd if=/dev/zero of=test.file bs=1G count=2 oflag=direct
2+0 records in
2+0 records out
2147483648 bytes (2.1 GB) copied, 13.5487 s, 159 MB/s

该分区磁盘写入速率为 159M/s

参数解释:

  • /dev/zero 伪设备,会产生空字符流,对它不会产生 IO 。

  • if 参数用来指定 dd 命令读取的文件。

  • of 参数用来指定 dd 命令写入的文件。

  • bs 参数代表每次写入的块的大小。

  • count 参数用来指定写入的块的个数。

  • offlag=direc 参数测试 IO 时必须指定,代表直接写如磁盘,不使用 cache 。

测试读性能命令:

1
2
3
4
dd if=test.file of=/dev/null  iflag=direct
4194304+0 records in
4194304+0 records out
2147483648 bytes (2.1 GB) copied, 4.87976 s, 440 MB/s

该分区的读取速率为 440MB/s

fio

fio 命令是专门测试 iops 的命令,比 dd 命令准确,fio 命令的参数很多。

安装

yum install fio libaio-devel -y

(可选)安装 libaio 引擎:libaio 测试出来的 IO 会比 psync 要准确一些。

参数

参数的含义如下1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-filename=/dev/sdb		#要测试盘的名称,支持文件系统或者裸设备,/dev/sda2或/dev/sdb
-direct=1		 #测试过程绕过机器自带的buffer,使测试结果更真实(Linux在读写时,数据会先写到缓存,再在后台写到硬盘,读的时候也是优先从缓存中读,这样访问速度会加快,但是一旦掉电,缓存中数据就会清空,所有一种模式为DirectIO,可以跳过缓存,直接读写硬盘)
-ioengine=libaio		#定义使用什么io引擎去下发io请求,常用的一些 libaio:Linux本地异步I/O;rbd:通过librbd直接访问CEPH Rados 
-iodepth=16		 #队列的深度为16,在异步模式下,CPU不能一直无限的发命令到硬盘设备。比如SSD执行读写如果发生了卡顿,那有可能系统会一直不停的发命令,几千个,甚至几万个,这样一方面SSD扛不住,另一方面这么多命令会很占内存,系统也要挂掉了。这样,就带来一个参数叫做队列深度。
-bs=4k           #单次io的块文件大小为4k
-numjobs=10      #本次测试的线程数是10
-size=5G         #每个线程读写的数据量是5GB
-runtime=60      #测试时间为60秒,可以设置2m为两分钟。如果不配置此项,会将设置的size大小全部写入或者读取完为止
-rw=randread     #测试随机读的I/O
-rw=randwrite    #测试随机写的I/O
-rw=randrw       #测试随机混合写和读的I/O
-rw=read         #测试顺序读的I/O
-rw=write        #测试顺序写的I/O
-rw=rw           #测试顺序混合写和读的I/O
-thread          #使用pthread_create创建线程,另一种是fork创建进程。进程的开销比线程要大,一般都采用thread测试
rwmixwrite=30    #在混合读写的模式下,写占30%(即rwmixread读为70%,单独配置这样的一个参数即可)
-group_reporting     #关于显示结果的,汇总每个进程的信息
-name="TDSQL_4KB_read_test"     #定义测试任务名称

-lockmem=1g       #只使用1g内存进行测试
-zero_buffers     #用全0初始化缓冲区,默认是用随机数据填充缓冲区
-random_distribution=random    #默认情况下,fio 会在询问时使用完全均匀的随机分布,有需要的话可以自定义访问区域,zipf、pareto、normal、zoned
-nrfiles=8        #每个进程生成文件的数量

场景

顺序读:

fio -filename=/var/test.file -direct=1 -iodepth 1 -thread -rw=read -ioengine=libaio -bs=16k -size=2G -numjobs=10 -runtime=60 -group_reporting -name=test_r

随机写:

fio -filename=/var/test.file -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=libaio -bs=16k -size=2G -numjobs=10 -runtime=60 -group_reporting -name=test_randw

顺序写:

fio -filename=/var/test.file -direct=1 -iodepth 1 -thread -rw=write -ioengine=libaio -bs=16k -size=2G -numjobs=10 -runtime=60 -group_reporting -name=test_w

混合随机读写(70%随机读,30%随机写):

fio -filename=/var/test.file -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=libaio -bs=16k -size=2G -numjobs=10 -runtime=60 -group_reporting -name=test_r_w -ioscheduler=noop

报告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
test_randw: (g=0): rw=randwrite, bs=(R) 4096KiB-4096KiB, (W) 4096KiB-4096KiB, (T) 4096KiB-4096KiB, ioengine=libaio, iodepth=1
...
fio-3.7
Starting 10 threads
test_randw: Laying out IO file (1 file / 2048MiB)
Jobs: 10 (f=10): [w(10)][100.0%][r=0KiB/s,w=96.1MiB/s][r=0,w=24 IOPS][eta 00m:00s]
test_randw: (groupid=0, jobs=10): err= 0: pid=1294: Mon Jan 30 16:48:20 2023
  write: IOPS=22, BW=89.3MiB/s (93.7MB/s)(5404MiB/60489msec)
    slat (usec): min=172, max=10281, avg=750.41, stdev=600.38
    clat (msec): min=181, max=1103, avg=444.93, stdev=140.29
     lat (msec): min=182, max=1103, avg=445.68, stdev=140.31
    clat percentiles (msec):
     |  1.00th=[  218],  5.00th=[  255], 10.00th=[  279], 20.00th=[  321],
     | 30.00th=[  359], 40.00th=[  393], 50.00th=[  422], 60.00th=[  460],
     | 70.00th=[  502], 80.00th=[  550], 90.00th=[  634], 95.00th=[  709],
     | 99.00th=[  860], 99.50th=[  919], 99.90th=[ 1036], 99.95th=[ 1099],
     | 99.99th=[ 1099]
   bw (  KiB/s): min= 8175, max=16384, per=10.80%, avg=9876.32, stdev=3313.89, samples=1112
   iops        : min=    1, max=    4, avg= 2.27, stdev= 0.90, samples=1112
  lat (msec)   : 250=4.22%, 500=65.06%, 750=27.54%, 1000=2.96%
  cpu          : usr=0.14%, sys=0.06%, ctx=1377, majf=0, minf=17
  IO depths    : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwts: total=0,1351,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
  WRITE: bw=89.3MiB/s (93.7MB/s), 89.3MiB/s-89.3MiB/s (93.7MB/s-93.7MB/s), io=5404MiB (5667MB), run=60489-60489msec

分析

I/O 统计数据信息的值的含义

  • read/write: 读/写的IO操作(还有一个trim没用过)

  • salt: 提交延迟,这是提交I/O所花费的时间(min:最小值,max:最大值,avg:平均值,stdev:标准偏差)

  • chat: 完成延迟,表示从提交到完成I/O部分的时间

  • lat: 相应时间,表示从fio创建I/O单元到完成I/O操作的时间

  • bw: 带宽统计

  • iops: IOPS统计

  • lat(nsec/usec/msec): I/O完成延迟的分布。这是从I/O离开fio到它完成的时间。与上面单独的读/写/修剪部分不同,这里和其余部分的数据适用于报告组的所有I/ o。10=0.01%意味着0.01%的I/O在250us以下完成。250=0.02%意味着0.02%的I/O需要10到250us才能完成。

  • cpu: cpu使用率

  • IO depths: I/O深度在作业生命周期中的分布

  • IO submit: 在一个提交调用中提交了多少个I/O。每一个分录表示该数额及其以下,直到上一分录为止——例如,4=100%意味着我们每次提交0到4个I/O调用

  • IO complete: 和上边的submit一样,不过这个是完成了多少个

  • IO issued rwt: 发出的read/write/trim请求的数量,以及其中有多少请求被缩短或删除

  • IO latency: 满足指定延迟目标所需的I/O深度

Run status group 0 (all jobs) 汇总信息的含义:

  • bw: 总带宽以及最小和最大带宽

  • io: 该组中所有线程执行的累计I/O

  • run: 这组线程中最小和最长的运行时。

Linux磁盘状态统计信息的值含义:

  • ios: 所有组的I/ o个数

  • merge: I/O调度器执行的总合并数

  • ticks: 使磁盘繁忙的滴答数(仅供参考,原文是Number of ticks we kept the disk busy)

  • in_queue: 在磁盘队列中花费的总时间

  • util: 磁盘利用率。值为100%意味着我们保留了磁盘,如果一直很忙,那么50%的时间磁盘就会闲置一半的时间