由于公司要部署一套Ceph环境,趁任务并不太着急,在部署之前先对HDD和SSD进行周密的测试,以便和部署之后的Ceph服务能力(RBD、CephFS、NFS的性能)进行对比。
本文的目的是解答如下几个问题:
- fio工具的参数到底该如何选择,比如:多长时间的
runtime
更经济?bs
对测试结果有何影响?numjobs
和iodepth
应该设置多少? - 正儿八经了解一下HDD和SSD的性能。
本文有不少数据和图表,希望能够为有同样疑惑的朋友提供有益参考。(请阅读时注意BW的单位)
测试过程日志:https://github.com/get-set/fio-bench-disks-ceph/tree/master/disks
测试HDD性能
磁盘的配置如下:
- 磁盘为7200转3.5寸SAS机械硬盘;
- 根据部署Ceph的官方推荐配置,每块磁盘以RAID0单盘挂在RAID卡下,RAID卡为H730 mini(拥有1G缓存),具体配置:
- 开启预读(read-ahead);
- 开启写回(write-back),有电池;
- 关闭磁盘缓存(disk-cache)。
注:
- 为了加快测试速度,以下测试是在两台服务器上测试的,比如有可能随机写在第一台的/dev/sdd上测试,而随机读就是在另一台的/dev/sde上测试。但是确保针对所测试参数的同一类型的IO操作的所有参数变化都是在同一块硬盘上进行的,所以请只关注各参数内的纵向测试结果即可。
- RAID卡有缓存,由于是为后续部署和测试Ceph做前期测试,因此以下关于磁盘的测试结果是有RAID缓存加持的,请酌情参考。
FIO参数对HDD测试的影响
1. runtime(测试时长)
一、测试目的
了解能够得到客观性能结果的最短时间,以便为后续的测试节省时间。
二、测试内容
测试4k随机读写和64k顺序读写的过程中,不同的runtime所得到的测试结果,观察从多长时间开始就可以得到稳定的测试结果。
为了观察
util
值(磁盘利用率),将读写分别测试。
测试脚本fio.conf
:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
iodepth=1 # 队列深度1(后续有关于参数的测试)
numjobs=1 # 同时开启的fio进程/线程数为1
rw=randread # 每次测试修改该值:randread/read/randwrite/write
bs=4k # 每次测试修改该值:rand对应4k,seq对应64k
[job]
runtime=10 # 本次测试runtime:10 20 30 40 50 60 90
测试命令:
mkdir -p logs/{runtime.4k_randread,runtime.4k_randwrite,runtime.64k_read,runtime.64k_write}
# 修改不同的rw和bs值,然后执行4次如下命令(注意修改tee输出的log目录)
for runtime in 10 20 30 40 50 60 90; do sed -i "/^runtime/c runtime=${runtime}" fio.conf && fio fio.conf | tee logs/runtime.4k_randread/${runtime}.log && sleep 10s; done
测试结果(IOPS和BW):
对于4k_randread
、64k_read
和64_write
的IOPS
和BW
值的观察可以基本看出,从30s之后,测试结果基本趋于稳定;而4k_randwrite
看不到明显的稳定区间。
再观察磁盘利用率:
通过观察util
的值,发现不同的测试方法大约都能够在30秒之后得到比较稳定的结果。对于随机读来说,似乎随着时间的延长而越来越乏力,与性能结果类似。
三、测试结论
对于随机读写和顺序读写的测试来说,runtime达到30-40秒即可得到相对客观的结果,保险起见也可以时间更长。
2. bs(块大小)
一、测试目的
观察不同的块大小对读写性能的影响。
二、测试内容
测试随机读写和顺序读写的过程中,不同的bs所得到的测试结果。
为了观察
util
值(磁盘利用率),将读写分别测试。
测试脚本fio.conf
:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
iodepth=1 # 队列深度1(后续有关于参数的测试)
numjobs=1 # 同时开启的fio进程/线程数为1
rw=randread # 每次测试修改该值:randread/read/randwrite/write
[job]
bs=4k # 本次测试bs:1k 2k 4k .. 64m
测试命令:
mkdir -p logs/{bs.randread,bs.randwrite,bs.read,bs.write}
# 修改不同的rw值,然后执行4次如下命令(注意修改tee输出的log目录)
for bs in 1k 2k 4k 8k 16k 32k 64k 128k 256k 512k 1m 2m 4m 8m 16m 32m 64m; do sed -i "/^bs/c bs=${bs}" fio.conf && fio fio.conf | tee logs/bs.randread/${bs}.log && sleep 10s; done
测试结果(BW和IOPS):
测试结果(BW和util):
三、测试结论
- 随着块大小的增加,读写速度会显著增加,同时IOPS显著下降,其中:
- 随机读写相对顺序读写来说,变化趋势相对较平缓;
- 随机读写在小于8k以前性能变化不大,顺序读写在大于64k之后性能变化不大。
- 随着块大小的增加,磁盘利用率先平稳,后下降,其中:
- 随机读写的磁盘利用率在块大小超过256k之后会有显著提升;而顺序读写的磁盘利用率在超过256k之后缓步下滑;
- 对于顺序读写来说,磁盘利用率保持高位,而随机读写的磁盘利用率相对较低,其中随机读的利用率低于随机写。
- 后续的测试,将采用4k作为随机读写的块大小,64k作为顺序读写的块大小。
3. iodepth
iodepth是队列深度,简单理解就是一批提交给系统的IO个数,由于同步的IO是堵塞的,IO操作会依次执行,iodepth一定小于1,因此该参数只有使用libaio时才有意义。因为异步的时候,系统能够在读写结果返回之前就吸纳后续的IO请求,所以loop里边可能在跑多个IO操作,然后等待结果异步返回。
libaio引擎会用这个iodepth值来调用io_setup准备个可以一次提交iodepth个IO的上下文,同时申请个io请求队列用于保持IO。 在压测进行的时候,系统会生成特定的IO请求,往io请求队列里面扔,当队列里面的IO个数达到iodepth_batch值的时候,就调用io_submit批次提交请求,然后开始调用io_getevents开始收割已经完成的IO。 每次收割多少呢?由于收割的时候,超时时间设置为0,所以有多少已完成就算多少,最多可以收割iodepth_batch_complete值个。随着收割,IO队列里面的IO数就少了,那么需要补充新的IO。 什么时候补充呢?当IO数目降到iodepth_low值的时候,就重新填充,保证OS可以看到至少iodepth_low数目的io在电梯口排队着。
一、测试目的
观察不同的iodepth大小对读写性能的影响。
二、测试内容
测试随机读写和顺序读写的过程中,不同的iodepth所得到的测试结果。
为了观察
util
值(磁盘利用率),将读写分别测试。
测试脚本fio.conf
:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
numjobs=1 # 同时开启的fio进程/线程数为1
rw=randread # 每次测试修改该值:randread/read/randwrite/write
bs=4k # 每次测试修改该值:rand对应4k,seq对应64k
[job]
iodepth=1 # 本次测试队列深度:1 2 4 8 16(视情况增加)
测试命令:
mkdir -p logs/{iodepth.4k_randread,iodepth.4k_randwrite,iodepth.64k_read,iodepth.64k_write}
# 修改不同的rw值,然后执行4次如下命令(注意修改tee输出的log目录)
for iodepth in 1 2 4 8 16; do sed -i "/^iodepth/c iodepth=${iodepth}" fio.conf && fio fio.conf | tee logs/iodepth.4k_randread/${iodepth}.log && sleep 10s; done
测试结果:
4k随机读的IOPS
、BW
、clat
、util
(随机读的情况多测试了一些iodepth值):
lat
是总延迟,slat
是提交io到内核的延迟,clat
是内核到磁盘完成之间的延迟,因此lat=slat+clat
。slat
通常是比较稳定的值,所以这里通过观察clat
来判断IO延迟情况。
4k随机写的IOPS
、BW
、clat
、util
:
64k顺序读的IOPS
、BW
、clat
、util
:
64k顺序写的IOPS
、BW
、clat
、util
:
三、测试结论
- 对于
IOPS
和BW
来说,随着队列深度的增加,总体是呈现上涨趋势的,磁盘利用率util
的趋势与这两个指标也是比较相关的:- 对于4k随机读来说,在
iodepth
达到64
之后,这两个性能指标趋于平缓; - 对于4k随机写来说,似乎不喜欢
iodepth
高于1
; - 对于顺序读写来说,
iodepth
对这两个性能指标几乎没有影响。
- 对于4k随机读来说,在
- 在追求更高的
IOPS
和BW
的同时,也要追求更低的延迟clat
,随着队列深度的增加,延迟显著上升。 - 可以基本确定
iodepth
值的选择:- 4k随机读:
32
/64
是不错的选择; - 4k随机写:
1
即可; - 64k顺序读写:
1
/2
即可。
- 4k随机读:
- 以上关于iodepth的值的选择可能还需numjobs的值进行调整;
- 以上的测试结论是基于我的磁盘配置来的,也就是本文前面提到的基于单盘RAID0的磁盘配置,请酌情参考。
4. numjobs
numjobs个数指定在测试的时候同时启动的进程/线程数,主要用来测试并发IO的情况。
一、测试目的
观察不同的numjobs大小对读写性能的影响。
二、测试内容
测试随机读写和顺序读写的过程中,不同的numjobs所得到的测试结果。
为了观察
util
值(磁盘利用率),将读写分别测试。
测试脚本fio.conf
:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
rw=randread # 每次测试修改该值:randread/read/randwrite/write
bs=4k # 每次测试修改该值:rand对应4k,seq对应64k
iodepth=1 # 队列深度固定为1
group_reporting # 多个job合并出报告
[job]
numjobs=1 # 本次测试numjobs:1 2 4 8 16
测试命令:
mkdir -p logs/{numjobs.4k_randread,numjobs.4k_randwrite,numjobs.64k_read,numjobs.64k_write}
# 修改不同的rw值,然后执行4次如下命令(注意修改tee输出的log目录)
for numjobs in 1 2 4 8 16; do sed -i "/^numjobs/c numjobs=${numjobs}" fio.conf && fio fio.conf | tee logs/numjobs.4k_randread/${numjobs}.log && sleep 10s; done
测试结果:
4k随机读的IOPS
、BW
、clat
、util
:
4k随机写的IOPS
、BW
、clat
、util
:
64k顺序读的IOPS
、BW
、clat
、util
:
64k顺序写的IOPS
、BW
、clat
、util
:
三、测试结论
- 总体来说,一个比较容易理解的情况就是,job越多,延迟越高。
- 性能表现方面,则各有千秋:
- 对于4k随机读,job数量在1-16的时候有显著提升,延迟并没有明显提高,但是从16之后,性能显著下降,延迟也迅速提高;
- 对于4k随机写,job数量从1-4,性能大约会下降一半,之后保持平稳,磁盘利用率下降为30%;
- 对于64k顺序读,job数量从1-4,会有显著的下降,之后保持平稳,磁盘利用率也显著下降;
- 对于64k顺序写,对numjobs参数无感。
- 可以基本确定
numjobs
值的选择:- 4k随机读:
16
; - 4k随机写和64k顺序读写:
1
即可。
- 4k随机读:
以上的测试结论是基于我的磁盘配置来的,也就是本文前面提到的基于单盘RAID0的磁盘配置,请酌情参考。
猜测对于iodepth和numjobs对4k随机读的性能影响,与RAID的read-ahead和缓存有一定关系,因为可以攒一波批量处理,不知这个猜测是否正确,有对存储比较了解的朋友欢迎留言,感谢!
5. iodepth*numjobs?
一、测试目的
iodepth
可以简单理解为一次提交的IO操作的数量,numjobs
可以理解为有多少个同时进行的任务。
但是,前面关于iodepth
和numjobs
的测试,都是在另一个指标为1
的情况下进行的,虽然确定了两个参数的可选值,但是放到一起就不一定是好的选择了,因此,最后再将两个参数组合起来测试一下。
由于根据上面的分析,两个指标只是对4k随机读有助力,因此,我们只对4k随机读作测试。
二、测试内容
测试iodepth
从1
-64
和numjobs
从1
-16
的性能表现。测试脚本:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
rw=randread # 每次测试修改该值:randread/read/randwrite/write
bs=4k # 每次测试修改该值:rand对应4k,seq对应64k
group_reporting # 多个job合并出报告
[job]
numjobs=1 # 本次测试numjobs:1 2 4 8 16
iodepth=1 # 本次测试iodepth:1 2 4 8 16 32 64
为了得到每次的util
值,我们还是用for循环的方式。
mkdir -p logs/iodepth_numjobs
idx=0
for numjobs in 1 2 4 8 16; do
for iodepth in 1 2 4 8 16 32 64; do
sed -i "/^numjobs/c numjobs=${numjobs}" fio.conf && \
sed -i "/^iodepth/c iodepth=${iodepth}" fio.conf && \
fio fio.conf | tee logs/iodepth_numjobs/$(printf "%02d" ${idx})_n${numjobs}i${iodepth}.log && \
let idx++ && sleep 30s
done
done
其中,自增的idx
和$(printf "%02d" ${idx})
是为了方便日志文件排序。
最终测试结果如下:
三、测试结论
对于4k随机读来说,
- 性能总体是与
iodepth
成正相关的,其中iodepth
为64,numjobs
为1时,性能和磁盘利用率最高,延迟相对来说比较低; - 上面的图并不直观,其实延迟是与
numjobs * iodepth
的乘积成正相关的,相对来说,numjobs
对延迟的影响更大,更多的并行IO任务会显著提高IO延迟。
以上的测试结论是基于我的磁盘配置来的,也就是本文前面提到的基于单盘RAID0的磁盘配置,请酌情参考,尤其是RAID卡有缓存并为磁盘配置了预读(read-ahead)。
再结合对iodepth
和numjobs
的测试结论,“为了测得更高的磁盘性能”,建议的参数设置是:
numjobs=1
- 4k随机读:
iodepth=64/32
,4k随机写/64k顺序读写:iodepth=1/2
。
HDD性能结果
根据前面的测试结论,测试脚本如下:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/sdd # XFS格式的磁盘,未直接用裸盘
numjobs=1 # 同时进行的任务数
iodepth=1 # 队列深度
group_reporting # 多个job合并出报告
[4k_randwrite]
stonewall # 隔离各测试任务
rw=randwrite
bs=4k
[4k_randread]
stonewall
rw=randread
bs=4k
iodepth=64
[64k_write]
stonewall
rw=write
bs=64k
[64k_read]
stonewall
rw=read
bs=64k
前面提到,我是两台物理机上进行的测试,不过两台物理机的RAID卡并不一样,一台是H730P mini,一台是H730 mini。区别在于二者的缓存分别是2G和1G,测试结果如下:
H730P mini(2G内存),总体利用率“64.09%”。
IOPS&BW | clat(usec) | |
---|---|---|
4k随机写 | IOPS=1600, BW=6401KiB/s | 597.20 |
4k随机读 | IOPS=571, BW=2286KiB/s | 111941.46 |
64k顺序写 | IOPS=2745, BW=172MiB/s | 341.96 |
64k顺序读 | IOPS=1501, BW=93.8MiB/s | 648.37 |
H730 mini(1G内存),总体利用率“74.72%”。
IOPS&BW | clat(usec) | |
---|---|---|
4k随机写 | IOPS=1102, BW=4409KiB/s | 871.05 |
4k随机读 | IOPS=573, BW=2294KiB/s | 111525.20 |
64k顺序写 | IOPS=2739, BW=171MiB/s | 341.74 |
64k顺序读 | IOPS=2786, BW=174MiB/s | 341.09 |
从以上的结果可以发现,4k随机读和64k顺序写并没有明显的性能差异,但是:
- 4k随机写的性能,2G缓存的情况比1G缓存高约45%,延迟低约45%;
- 64k顺序读的性能,1G缓存的情况比2G缓存高约84%,延迟低约90%。
关于这里的性能差异,还请存储方面的大神能帮忙解析一下。
测试SSD性能
一、测试目的
我们的测试还是以HDD为主,SSD(PCIe Intel P3600 1.6T)简单测试一下iodepth
和numjobs
,观察它与HDD在并发IO的情况下表现的趋势是HDD有什么差异,测试多了还挺心疼的。
二、测试内容
测试脚本:
[global]
ioengine=libaio # 异步IO
direct=1 # 排除OS的IO缓存机制的影响
size=5g # 每个fio进程/线程的最大读写
lockmem=1G # 锁定所使用的内存大小
runtime=30 # 根据上面的结论以后采用30值
directory=/mnt/ssd # 挂载的SSD,XFS格式
numjobs=1 # 同时进行的任务数
iodepth=1 # 队列深度
group_reporting # 多个job合并出报告
[4k_randwrite]
stonewall # 隔离各测试任务
rw=randwrite
bs=4k
[4k_randread]
stonewall
rw=randread
bs=4k
[64k_write]
stonewall
rw=write
bs=64k
[64k_read]
stonewall
rw=read
bs=64k
然后测试numjobs
和iodepth
的变化对结果的影响:
mkdir -p logs/ssd
idx=0
for numjobs in 1 2 4; do
for iodepth in 1 2 4 8 16 32; do
sed -i "/^numjobs/c numjobs=${numjobs}" fio.conf && \
sed -i "/^iodepth/c iodepth=${iodepth}" fio.conf && \
fio fio.conf | tee logs/ssd/$(printf "%02d" ${idx})_n${numjobs}i${iodepth}.log && \
let idx++ && sleep 30s
done
done
最后,测试结果如下:
这个结果增加了一列numjobs和iodepth的乘积,并按照乘积进行了排序(HDD的时候是按numjobs
进行排序的);而IOPS列被隐藏了,因为反正和BW是固定的比例。
三、测试结论
- SSD对
numjobs
和iodepth
的敏感程度似乎是一样的,并没有哪一个参数起主导作用的情况(而HDD的测试中numjobs
对延迟等方面的影响更大)。如果说numjobs
是批次数量,而iodepth
是每个批次中的IO数量,SSD则完全不管IO操作是不是一批、是不是来自一个测试进程,只是机械地处理,所以跟总的吞吐量(即“乘积”)是有关系的。从HDD与SDD的不同表现可以推测,RAID卡的缓存机制对HDD是有影响的(SSD是PCIe直插,不经过RAID卡)。 - SSD的各种IO读写方式的性能高峰:
- 4k随机写:**乘积到达
8
**的时候BW到达最高,而延迟并不高,再大的量会略微提高延迟。 - 4k随机读:乘积为
64
和128
的时候BW差距已经不大(为了验证,再增加一项测试,单独为4k随机读开个小灶numjobs=8, iodepth=32
,BW为1530,calt为630.55),所以可以认为乘积达到64
是较优参数。 - 64k顺序写:乘积
2
,出道即巅峰,乘积越高延迟越高。 - 64k顺序读:这似乎是个特例,两个参数都为
1
的时候,估计比较“专心”,性能挺高(这不是性能抖动,两块SSD的两次测试都是相近的结果),总体来说,乘积为32
是较优参数。
- 4k随机写:**乘积到达
- 整个测试过程,SSD的磁盘利用率都很高,毕竟相比HDD来说,没有各种机械运动。
- 总起来说,SSD(PCIe Intel P3600 1.6T)的最高性能:4k随机写1180MiB/s,4k随机读1450MiB/s,64k顺序写1550MiB/s,64k顺序读2650MiB/s。
对比一下HDD(单盘RAID0-with-1Gcache/read-ahead/write-back)的最高性能:4k随机写4+MiB/s,4k随机读2+MiB/s,64k顺序写170MiB/s,64k顺序读170MiB/s。留点面子,延迟就不拿出来了 T_T