Linux 磁盘性能指标

Oct 9, 2020 23:00 · 2015 words · 5 minute read Linux Performance

衡量磁盘性能的几个指标

  • 使用率:磁盘处理 I/O 的时间百分比
  • 饱和度:磁盘处理 I/O 的繁忙程度
  • IOPS:每秒的 I/O 请求数
  • 吞吐量:每秒的 I/O 请求大小
  • 响应时间:I/O 请求从发出到接收到响应的时间

要结合多项指标综合分析,在数据库、大量小文件这类随机读写较多的场景中,IOPS 更能反应系统的整体性能;而在多媒体等顺序读写较多的场景,吞吐量最重要。

使用 fio 进行磁盘基准测试

fio 是最常用的文件系统和磁盘 I/O 性能基准测试工具。它提供了大量的可定制化选项,可以用来测试,裸盘或者文件系统在各种场景下的 I/O 性能,包括了不同块大小、不同 I/O 引擎以及是否使用缓存等场景。

可选参数

  • direct 置 1 将不使用系统缓存

  • iodepth 使用异步 I/O 时同时发出 I/O 请求的上限

  • rw I/O 模式

    • read/write 顺序读/写
    • randread/randwrite 随机读/写
  • ioengine I/O 引擎

    • sync 同步
    • libaio 异步
    • mmap 内存映射
    • net 网络
  • bs I/O 大小,默认 4k

  • filename

    • 磁盘设备
    • 文件路径
  • 随机读

    $ fio -name=randread -direct=1 -iodepth=64 -rw=randread -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
    
  • 随机写

    $ fio -name=randwrite -direct=1 -iodepth=64 -rw=randwrite -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
    
  • 顺序读

    $ fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
    
  • 顺序写

    $ fio -name=write -direct=1 -iodepth=64 -rw=write -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
    

结果指标

$ fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=1G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.7
Starting 1 process
Jobs: 1 (f=1)
read: (groupid=0, jobs=1): err= 0: pid=2747: Mon Oct 12 13:42:21 2020
   read: IOPS=92.0k, BW=359MiB/s (377MB/s)(1024MiB/2849msec)
    slat (nsec): min=918, max=988926, avg=9652.21, stdev=5779.11
    clat (usec): min=29, max=4429, avg=685.16, stdev=137.88
     lat (usec): min=40, max=4431, avg=694.93, stdev=138.49
    clat percentiles (usec):
     |  1.00th=[  486],  5.00th=[  570], 10.00th=[  603], 20.00th=[  619],
     | 30.00th=[  635], 40.00th=[  652], 50.00th=[  660], 60.00th=[  676],
     | 70.00th=[  693], 80.00th=[  734], 90.00th=[  799], 95.00th=[  889],
     | 99.00th=[ 1106], 99.50th=[ 1221], 99.90th=[ 1795], 99.95th=[ 3654],
     | 99.99th=[ 4228]
   bw (  KiB/s): min=359728, max=373088, per=99.69%, avg=366924.80, stdev=5155.61, samples=5
   iops        : min=89932, max=93274, avg=91731.60, stdev=1289.50, samples=5
  lat (usec)   : 50=0.01%, 100=0.01%, 250=0.02%, 500=1.43%, 750=82.46%
  lat (usec)   : 1000=14.07%
  lat (msec)   : 2=1.95%, 4=0.06%, 10=0.01%
  cpu          : usr=1.37%, sys=97.54%, ctx=82, majf=0, minf=75
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.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.1%, >=64=0.0%
     issued rwts: total=262144,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=64

Run status group 0 (all jobs):
   READ: bw=359MiB/s (377MB/s), 359MiB/s-359MiB/s (377MB/s-377MB/s), io=1024MiB (1074MB), run=2849-2849msec

Disk stats (read/write):
  sdb: ios=240629/0, merge=2750/0, ticks=14269/0, in_queue=131, util=96.25%
  • slat 从 I/O 提交到实际执行 I/O 的时长
  • clat 从 I/O 提交到 I/O 完成的时长
  • lat 从 fio 创建 I/O 到 I/O 完成的总时长
  • bw 带宽(吞吐量)
  • iops 每秒 I/O 的次数

对于同步 I/O,由于提交和完成是同一个动作,slat 实际上就是 I/O 完成的时间,clat 是 0;使用异步 I/O 时 lat 约等于 slat + clat

使用 iostat 观察磁盘 I/O

$ iostat [可选参数] [采样间隔] [采样次数]

可选参数

  • -c 显示CPU使用情况
  • -d 显示磁盘使用情况
  • -k 以 KB 为单位显示
  • -m 以 M 为单位显示
  • -N 显示磁盘阵列信息
  • -n 显示 NFS 使用情况
  • -p [磁盘] 显示磁盘和分区的情况
  • -t 显示终端和 CPU 的信息
  • -x 显示详细信息
  • -V 显示版本信息

结果指标

性能指标 描述
rrqm/s 每秒合并的读请求数
wrqm/s 每秒合并的写请求数
r/s 每秒发送给磁盘的读请求数
w/s 每秒发送给磁盘的写请求数
rsec/s 每秒读扇区数
wsec/s 每秒写扇区数
rkB/s 每秒从磁盘读的数据量
wkB/s 每秒从磁盘写的数据量
avgrq-sz 平均每次 I/O 操作的数据大小
avgqu-sz 平均 I/O 队列长度
r_await 读请求处理完成等待时间(毫秒)
w_await 写请求处理完成等待时间(毫秒)
svctm 处理 I/O 请求所需的平均时间(毫秒),是推断出来的,并不保证完全准确
%util 磁盘处理 I/O 的时间百分比(磁盘 I/O 使用率)
  • %util 磁盘 I/O 使用率
  • r/s + w/s IOPS
  • rkB/s + wkB/s 吞吐量
  • r_await + w_await 响应时间

使用 pidstat 观察进程 I/O

$ pidstat -d [采样间隔] [采样次数]

结果指标

性能指标 描述
kB_rd/s 每秒进程从磁盘读取的数据量
kB_wr/s 每秒进程从磁盘写入的数据量
kB_ccwr/s 每秒进程向磁盘写入,但是被取消的数据量
iodelay 块 I/O 延迟

使用 strace 分析进程系统调用

$ strace -p [PID]

读写文件必须通过系统调用完成。观察系统调用情况,就可以知道进程正在写的文件。

$ fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=10G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
$ ps -ef | grep fio
root        6472    2666 10 14:06 pts/0    00:00:00 fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=10G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
root        6476    6472 99 14:06 ?        00:00:01 fio -name=read -direct=1 -iodepth=64 -rw=read -ioengine=libaio -bs=4k -size=10G -numjobs=1 -runtime=1000 -group_reporting -filename=/dev/sdb
$ strace -p 6472
wait4(4945, 0x7ffd37518be4, WNOHANG, NULL) = 0
stat("/tmp/fio-dump-status", 0x7ffd37517b60) = -1 ENOENT (No such file or directory)
nanosleep({tv_sec=0, tv_nsec=10000000}, NULL) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4945, si_uid=0, si_status=0, si_utime=317, si_stime=3028} ---
restart_syscall(<... resuming interrupted nanosleep ...>) = 0
wait4(4945, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 4945
getpid()                                = 4938
stat("/tmp/fio-dump-status", 0x7ffd37517b60) = -1 ENOENT (No such file or directory)
nanosleep({tv_sec=0, tv_nsec=10000000}, NULL) = 0
futex(0x7f92b5670720, FUTEX_WAKE, 1)    = 1
futex(0x7f92b56706d0, FUTEX_WAKE, 1)    = 1
openat(AT_FDCWD, "/etc/localtime", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=582, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=582, ...}) = 0
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 582
lseek(3, -357, SEEK_CUR)                = 225
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 357
close(3)                                = 0
write(1, "\nread: (groupid=0, jobs=1): err="..., 1024) = 1024
write(1, "40%, sys=89.53%, ctx=440, majf=0"..., 42) = 42
write(1, "  IO depths    : 1=0.1%, 2=0.1%,"..., 79) = 79
write(1, "     submit    : 0=0.0%, 4=100.0"..., 80) = 80
write(1, "     complete  : 0=0.0%, 4=100.0"..., 80) = 80
write(1, "     issued rwts: total=2621440,"..., 68) = 68
write(1, "     latency   : target=0, windo"..., 66) = 66
write(1, "\n", 1)                       = 1
write(1, "Run status group 0 (all jobs):\n", 31) = 31
write(1, "   READ: bw=303MiB/s (318MB/s), "..., 110) = 110
write(1, "\n", 1)                       = 1
write(1, "Disk stats (read/write):\n", 25) = 25
write(1, "  sdb: ios=2587226/0, merge=2328"..., 79) = 79
munmap(0x7f92c8f01000, 104)             = 0
munmap(0x7f92c8f06000, 104)             = 0
munmap(0x7f92c8f02000, 104)             = 0
munmap(0x7f92c8f03000, 104)             = 0
munmap(0x7f92c8f05000, 104)             = 0
munmap(0x7f92c8f04000, 104)             = 0
munmap(0x7f92c8f07000, 104)             = 0
shmdt(0x7f92886fc000)                   = 0
shmctl(6, IPC_RMID, 0x7ffd37520c60)     = -1 EINVAL (Invalid argument)
munmap(0x7f92c8f08000, 104)             = 0
exit_group(0)                           = ?
+++ exited with 0 +++

进程向描述符编号为 1 的文件写入了 1024/1024 KB 数据。

建议不要在生产环境使用 strace,对被观察应用的性能影响很大。

使用 lsof 观察进程打开的文件

lsof(list open files)是一个查看当前系统文件的工具。在 Linux 中,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。如传输控制协议 (TCP) 和用户数据报协议 (UDP) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,该文件描述符提供了大量关于这个应用程序本身的信息。

$ lsof -p [PID]
$ ps -ef | grep hugo
root      5816 18516  0 Jul14 ?        04:37:13 hugo server --bind=127.0.0.1 --port=1313 --baseUrl=https://blog.crazytaxii.com/ --appendPort=false --disableLiveReload=true --disableFastRender=true --gc=true --noHTTPCache=true
$ lsof -p 5816
COMMAND  PID USER   FD      TYPE             DEVICE SIZE/OFF      NODE NAME
hugo    5816 root  cwd       DIR              253,1     4096    529770 /root/Deploy/blog
hugo    5816 root  rtd       DIR              253,1     4096         2 /
hugo    5816 root  txt       REG              253,1 46407680    402944 /usr/local/bin/hugo
hugo    5816 root    0r     FIFO                0,8      0t0 356523615 pipe
hugo    5816 root    1w     FIFO                0,8      0t0 356523616 pipe
hugo    5816 root    2w     FIFO                0,8      0t0 356523617 pipe
hugo    5816 root    3u  a_inode                0,9        0      5861 [eventpoll]
hugo    5816 root    4r     FIFO                0,8      0t0 356523618 pipe
hugo    5816 root    5w     FIFO                0,8      0t0 356523618 pipe
hugo    5816 root    6u     unix 0xffff880137ce1000      0t0 356522969 socket
hugo    5816 root    7r  a_inode                0,9        0      5861 inotify
hugo    5816 root    8u  a_inode                0,9        0      5861 [eventpoll]
hugo    5816 root    9r     FIFO                0,8      0t0 356523634 pipe
hugo    5816 root   10w     FIFO                0,8      0t0 356523634 pipe
hugo    5816 root   11u     IPv4          356522984      0t0       TCP localhost:xtel (LISTEN)
  • FD 文件描述符号
  • TYPE 文件类型
    • DIR 目录
    • CHR 表示字符类型
    • BLK 块设备
    • UNIX UNIX 域套接字
    • FIFO 先进先出 (FIFO) 队列
    • IPv4 网际协议 (IP) 套接字
  • NODE 索引节点(文件在磁盘上的标识)
  • NAME 文件路径

参考