磁盘优化的性能分析工具有不少,往往都有不少的关联性
在找出IO性能的瓶颈之后,下一步就是对其进行优化了,如何以最快的速度完成IO操作,减少IO浪费的时间
对于IO优化的思路,首先你要确定一个IO优化的目标,也就是目标性能指标
那么为了更加合理客观的获取优化效果,我们需要得到文件系统或者磁盘IO的极限性能
fio是常用的文件和磁盘IO性能基准测试工具,提供了大量的可定制化选项,测试文件系统或者磁盘在各种场景下的IO性能,包括不同块大小,不同IO引擎是否使用缓存等场景
fio的安装很简单,使用如下的命令进行获取
yum install -y fio
接下来,我们利用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 表示异步io时候, 发出的IO请求上限
rw 表示IO模式,read/write 表示顺序读写 randread/randwrite表示随机读写
ioengine 表示io引擎,支持 sync libaio mmap net 等各种IO引擎
bs,表示IO大小,设置为了4K
filename 可以指定为文件路径,也可以是磁盘路径,磁盘路径的测试写,可能会破坏原本磁盘的文件
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% |
在上面中,重点的参数是slat clat lat等几个行
slat是指的从IO提交到实际执行IO的时长
clat IO提交到IO完成的时长
lat 就是fio创建IO到IO完成的总时长
同步IO中,IO提交到实际执行IO是连续的时间,所以slat就是IO完成时间,clat也是0
对于异步IO,lat则等于slat+clat之和
再来看bw,代表吞吐量,吞吐量大约是16MB
平均值为 16MB (17005/1024)
最后的iops,就是每秒IO的次数,平均IOPS是4250个
上面测试的IO大小相同,不能准确的模拟应用程序的IO,所以需要进行配合使用
fio可以进行IO的重放,需要对应的io重放文件,可以利用一个工具类 blktrace
进行IO重放的基准测试,首先是blktrace 记录磁盘设备的IO访问,然后是fio,重放blktrace的记录
基本操作可以如下
blktrace /dev/sdb
ls
sdb.blktrace.0 sdb.blktrace.1
blkparse sdb -d sdb.bin
fio –name=replay –filename=/dev/sdb –direct=1 –read_iolog=sdb.bin
利用这种方式,获取到应用程序IO的基准测试报告
有了性能的实际瓶颈之后,我们就可以进行相关的优化了
我们按照Linux系统整体栈图的思路,进行相关的优化
整体的IO栈图如上
从最上面的应用程序出发
从这个角度,我们可以使用追加写来代替随机写,减少寻址开销,加快IO写的速度
借助缓存IO,充分利用系统缓存,降低实际IO的次数
在应用程序中构建自己的缓存,利用Redis这类外部缓存系统,降低其他应用程序使用缓存对自身的影响
之后,C标准库提供的fopen fread 比 系统直接的 open read 有了标准库的缓存,直接调用只能利用系统提供的页缓存和缓冲区,没有库函数的缓存可以用
fopen提供了用户态的缓存
对于频繁的读写一个磁盘空间的时候,可以使用mmap来代替read/write,减少内存拷贝次数
内存映射文件
使用同步写的时候,将写请求合并,而不是将每个请求都写入磁盘,可以使用fsync()代替O_SYNC
多个应用程序共享磁盘的时候,为了保证IO不被某个应用完全占用,推荐使用cgroups来限制IOPS和吞吐量
最后在 CFQ调度器中,用ionice来调整进程的IO调度优先级,特别是提高核心应用的优先级,ionice支持三个优先级,Idel Best-effort 和 Realtime,Best-effort和Realtime支持0-7的级别,数值越小,优先级越高
然后往下走
我们就能看到文件系统优化
文件系统中也有不同的优化方式,首先根据负载场景不同,选择适合的文件系统,比如Ubuntu默认使用ext4,CentOS使用xfs系统
两者的不同就是xfs可以支持更大磁盘分区和数量,但xfs无法收缩
选好文件系统之后,进一步的优化文件系统的配置
文件系统的特性(ext_attr,dir_index),日志模式(journal,ordered,writeback),挂在选项(noatime)等
使用tune2fs工具,可以调整文件系统的特性,通过/etc/fstab,或者mount命令行参数,调整文件系统的日志模式和挂载选项或者是/etc/fstab 都可以调整文件系统日志模式或者挂载选项
然后是文件系统的缓存
就是pdflush脏页的刷新频率 dirty_expire_centisecs和dirty_writeback_centisecs ,以及脏页的限额 dirty_background_ratio和dirty_ratio等
然后内核回收目录项缓存和索引节点缓存的倾向 调整vfs_cache_pressure 数值越大,越容易回收
不需要持久化的时候,还可以使用tmpfs,获取更好的IO性能,tmpfs将数据保存在内存中,而不是磁盘,比如dev/shm,就是大多数Linux默认配置的一个内存文件系统,默认大小为总内存的一半
到了磁盘角度,就是实际的物理了
简单点,就是SSD代替HDD
然后使用RAID将多个磁盘组成一个逻辑磁盘,提供冗余的磁盘阵列,增强可靠性
针对磁盘和应用程序IO的特征,选择不同的IO调度算法,比如SSD必选noop调度
然后对于应用程序的数据,比如日志,数据库这种重IO的应用,配置单独磁盘
然后优化内核快的IO选项,比如调整磁盘队列的长度 /sys/block/sdb/queue/nr_requests,增大队列长度,提高磁盘吞吐量,但是也会导致IO延迟增加
今天说的主要是常见的文件系统和磁盘IO的性能优化思路和方法,我们发现IO性能之后,找到重要的问题,然后进行具体的优化
优化的方式也要从上往下,从应用程序到文件系统到最终的物理设备