Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

时间:2021-04-17 02:31:01

(1)磁盘 I/O 性能指标

Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

文件系统和磁盘 I/O 指标对应的工具

Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

文件系统和磁盘 I/O 工具对应的指标

Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

(2)磁盘 I/O 问题定位分析思路

Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

(3)I/O 性能优化思路

Step 1:首先采用 I/O 基准测试工具得到文件系统或磁盘 I/O 的极限性能,作为优化的目标。

Step 2:从应用程序、文件系统以及磁盘角度,分别对 I/O 性能进行优化。

1 文件系统或磁盘 I/O 的基准测试工具:fio ( Flexible I/O Tester )

# Ubuntu
apt-get install -y fio
# CentOS
yum install -y fio # 随机读
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
  • direct,表示是否跳过系统缓存。设置为 1 ,表示跳过系统缓存。
  • iodepth,表示使用异步 I/O(asynchronous I/O,简称 AIO)时,同时发出的 I/O 请求上限。在上面的示例中,我设置的是 64。
  • rw,表示 I/O 模式。我的示例中, read/write 分别表示顺序读 / 写,而 randread/randwrite 则分别表示随机读 / 写。
  • ioengine,表示 I/O 引擎,它支持同步(sync)、异步(libaio)、内存映射(mmap)、网络(net)等各种 I/O 引擎。上面示例中,我设置的 libaio 表示使用异步 I/O。
  • bs,表示 I/O 的大小。示例中,我设置成了 4K(这也是默认值)。
  • filename,表示文件路径,当然,它可以是磁盘路径(测试磁盘性能),也可以是文件路径(测试文件系统性能)。示例中,我把它设置成了磁盘 /dev/sdb。不过注意,用磁盘路径测试写,会破坏这个磁盘中的文件系统,所以在使用前,你一定要事先做好数据备份。

fio 输出:

read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64
fio-3.1
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=16.7MiB/s,w=0KiB/s][r=4280,w=0 IOPS][eta 00m:00s]
read: (groupid=0, jobs=1): err= 0: pid=17966: Sun Dec 30 08:31:48 2018
read: IOPS=4257, BW=16.6MiB/s (17.4MB/s)(1024MiB/61568msec)
slat (usec): min=2, max=2566, avg= 4.29, stdev=21.76
clat (usec): min=228, max=407360, avg=15024.30, stdev=20524.39
lat (usec): min=243, max=407363, avg=15029.12, stdev=20524.26
clat percentiles (usec):
| 1.00th=[ 498], 5.00th=[ 1020], 10.00th=[ 1319], 20.00th=[ 1713],
| 30.00th=[ 1991], 40.00th=[ 2212], 50.00th=[ 2540], 60.00th=[ 2933],
| 70.00th=[ 5407], 80.00th=[ 44303], 90.00th=[ 45351], 95.00th=[ 45876],
| 99.00th=[ 46924], 99.50th=[ 46924], 99.90th=[ 48497], 99.95th=[ 49021],
| 99.99th=[404751]
bw ( KiB/s): min= 8208, max=18832, per=99.85%, avg=17005.35, stdev=998.94, samples=123
iops : min= 2052, max= 4708, avg=4251.30, stdev=249.74, samples=123
lat (usec) : 250=0.01%, 500=1.03%, 750=1.69%, 1000=2.07%
lat (msec) : 2=25.64%, 4=37.58%, 10=2.08%, 20=0.02%, 50=29.86%
lat (msec) : 100=0.01%, 500=0.02%
cpu : usr=1.02%, sys=2.97%, ctx=33312, 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 rwt: total=262144,0,0, short=0,0,0, dropped=0,0,0
latency : target=0, window=0, percentile=100.00%, depth=64
Run status group 0 (all jobs):
READ: bw=16.6MiB/s (17.4MB/s), 16.6MiB/s-16.6MiB/s (17.4MB/s-17.4MB/s), io=1024MiB (1074MB), run=61568-61568msec
Disk stats (read/write):
sdb: ios=261897/0, merge=0/0, ticks=3912108/0, in_queue=3474336, util=90.09%

I/O 延迟(latency):slat、clat、lat。

  • slat ,是指从 I/O 提交到实际执行 I/O 的时长(Submission latency);
  • clat ,是指从 I/O 提交到 I/O 完成的时长(Completion latency);
  • lat ,指的是从 fio 创建 I/O 到 I/O 完成的总时长。

    对于同步 I/O 来说,由于 I/O 提交和 I/O 完成是一个动作,所以 slat 实际上就是 I/O 完成的时间,而 clat 是 0。对于异步 I/O(libaio),lat 近似等于 slat + clat 之和。

bw ,代表吞吐量

iops ,代表每秒 I/O 的次数

精确的模拟应用程序的 I/O 模式(读写并行、每次的 I/O 大小也不一定相同等情况下):blktrace + fio 的组合使用。

# 使用 blktrace 跟踪磁盘 I/O,注意指定应用程序正在操作的磁盘
$ blktrace /dev/sdb
# 查看 blktrace 记录的结果
# ls
sdb.blktrace.0 sdb.blktrace.1
# 将结果转化为二进制文件
$ blkparse sdb -d sdb.bin
# 使用 fio 重放日志
$ fio --name=replay --filename=/dev/sdb --direct=1 --read_iolog=sdb.bin

Linux性能优化从入门到实战:16 文件系统篇:总结磁盘I/O指标/工具、问题定位和调优

2.1 应用程序优化

(1)可以用追加写(顺序写)代替随机写,减少寻址开销,加快 I/O 写的速度。

(2)可以借助缓存 I/O ,充分利用系统缓存,降低实际 I/O 的次数。

(3)可以在应用程序内部构建自己的缓存,或者用 Redis 这类外部缓存系统。这样,一方面,能在应用程序内部,控制缓存的数据和生命周期,并且推荐采用自身方式缓存数据,而不是利用系统缓存;另一方面,也能降低其他应用程序使用缓存对自身的影响。

如:C 标准库的 fopen、fread 等库函数,都会利用标准库的缓存,减少磁盘的操作;而使用 open、read 等系统调用时,就能利用操作系统提供的页缓存和缓冲区等,而没有库函数的缓存可用。

(4)在需要频繁读写同一块磁盘空间时,可以用 mmap 代替 read/write,减少内存的拷贝次数。

(5)在需要同步写的场景中,尽量将写请求合并,而不是让每个请求都同步写入磁盘,即可以用 fsync() 取代 O_SYNC。

(6)在多个应用程序共享相同磁盘时,为了保证 I/O 不被某个应用完全占用,推荐你使用 cgroups 的 I/O 子系统,来限制进程 / 进程组的 IOPS 以及吞吐量。

(7)在使用 CFQ 调度器时,可以用 ionice 来调整进程的 I/O 调度优先级,特别是提高核心应用的 I/O 优先级。ionice 支持三个优先级类:Idle、Best-effort 和 Realtime。其中, Best-effort 和 Realtime 还分别支持 0-7 的级别,数值越小,则表示优先级别越高。

2.2 文件系统优化

(1)你可以根据实际负载场景的不同,选择最适合的文件系统。

比如 Ubuntu 默认使用 ext4 文件系统,而 CentOS 7 默认使用 xfs 文件系统。相比于 ext4 ,xfs 支持更大的磁盘分区和更大的文件数量,如 xfs 支持大于 16TB 的磁盘,但是 xfs 文件系统的缺点在于无法收缩,而 ext4 则可以。

(2)在选好文件系统后,还可以进一步优化文件系统的配置选项,包括文件系统的特性(如 ext_attr、dir_index)、日志模式(如 journal、ordered、writeback)、挂载选项(如 noatime)等等。

比如, 使用 tune2fs 这个工具,可以调整文件系统的特性(tune2fs 也常用来查看文件系统超级块的内容)。 而通过 /etc/fstab ,或者 mount 命令行参数,我们可以调整文件系统的日志模式和挂载选项等。

(3)可以优化文件系统的缓存。

比如,你可以优化 pdflush 脏页的刷新频率(比如设置 dirty_expire_centisecs 和 dirty_writeback_centisecs)以及脏页的限额(比如调整 dirty_background_ratio 和 dirty_ratio 等)。

再如,你还可以优化内核回收目录项缓存和索引节点缓存的倾向,即调整 vfs_cache_pressure(/proc/sys/vm/vfs_cache_pressure,默认值 100),数值越大,就表示越容易回收。

(4)在不需要持久化时,你还可以用内存文件系统 tmpfs,以获得更好的 I/O 性能 。tmpfs 把数据直接保存在内存中,而不是磁盘中。比如 /dev/shm/ ,就是大多数 Linux 默认配置的一个内存文件系统,它的大小默认为总内存的一半。

2.3 磁盘优化

(1)最简单有效的优化方法,就是换用性能更好的磁盘,比如用 SSD 替代 HDD。

(2)我们可以使用 RAID ,把多块磁盘组合成一个逻辑磁盘,构成冗余独立磁盘阵列。这样做既可以提高数据的可靠性,又可以提升数据的访问性能。

(3)针对磁盘和应用程序 I/O 模式的特征,我们可以选择最适合的 I/O 调度算法。

比如,SSD 和虚拟机中的磁盘,通常用的是 noop 调度算法。而数据库应用,我更推荐使用 deadline 算法。

(4)我们可以对应用程序的数据,进行磁盘级别的隔离。

比如,我们可以为日志、数据库等 I/O 压力比较重的应用,配置单独的磁盘。

(5)在顺序读比较多的场景中,我们可以增大磁盘的预读数据。

比如,你可以通过下面两种方法,调整 /dev/sdb 的预读大小。① 调整内核选项 /sys/block/sdb/queue/read_ahead_kb,默认大小是 128 KB,单位为 KB。② 使用 blockdev 工具设置,比如 blockdev --setra 8192 /dev/sdb,注意这里的单位是 512B(0.5KB),所以它的数值总是 read_ahead_kb 的两倍。

(6)我们可以优化内核块设备 I/O 的选项。

比如,可以调整磁盘队列的长度 /sys/block/sdb/queue/nr_requests,适当增大队列长度,可以提升磁盘的吞吐量(当然也会导致 I/O 延迟增大)。

(7)磁盘本身可能出现硬件错误,也会导致 I/O 性能急剧下降,所以发现磁盘性能急剧下降时,你还需要确认,磁盘本身是不是出现了硬件错误。

比如,你可以查看 dmesg 中是否有硬件 I/O 故障的日志。 还可以使用 badblocks、smartctl 等工具,检测磁盘的硬件问题,或用 e2fsck 等来检测文件系统的错误。如果发现问题,你可以使用 fsck 等工具来修复。

书籍推荐

Linux 基础入门书籍:《鸟哥的 Linux 私房菜》

计算机原理书籍:《深入理解计算机系统》

Linux 编程书籍:《Linux 程序设计》和《UNIX 环境高级编程》

Linux 内核书籍:《深入 Linux 内核架构》

性能优化书籍:《性能之巅:洞悉系统、企业与云计算》