当有新服务进行部署时,基准测试是必要的,这样的话能了解到系统的极限性能,可以选择更合适的方式去部署服务,或者当服务遇到性能瓶颈时,也有好的参照,是需要进行优化还是增加资源。
I/O 的真实性能比较难以测试,会涉及到两个方面,一个是文件系统 I/O, 一个是磁盘 I/O, 文件系统会有缓存操作,如果误用测试文件系统性能,往往会得到很好的结果,实际上与真实的磁盘性能相差甚远。在做磁盘 I/O 测试时,一定要使用 iostat 观察,通过直观的看,然后与测试结果进行比较,如果相差很大,需要调整测试方法。
测试指标
I/O 主要涉及两个指标,IOPS(每秒 IO 操作数),以及吞吐量(每秒 IO 大小)。
I/O 测试也需要很多参数调整,主要如下:
- bock size 大小,读写的参数大小,主要分 4K, 8K, 16K, 64K, 1M 。需要根据应用情况,选择合适的参数。如果哪个不清楚,可以用 strace 跟踪应用,看下具体的大小,如果还不清楚,就用 4K
- 读写模式,大体分为 顺序读,顺序写,随机读,随机写,顺序混合读写,随机混合读写
- 操作的文件大小
- 操作的线程数量,单线程还是多线程并发读写
dd
对于顺序读写,可以用 dd 很方便的得出结论。
1 2 3 4 |
dd if=/dev/zero of=sun.txt bs=64K count=10000 记录了10000+0 的读入 记录了10000+0 的写出 655360000字节(655 MB)已复制,5.7277 秒,114 MB/秒 |
上面使用 dd 测试写性能,bs 表示 block size, 每次操作大小为 64KB,count 为执行次数。但我们发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
iostat -xzdk 1 /dev/vdb Linux 2.6.32-504.30.3.el6.x86_64 (devtest.bj.sm) 2018年12月25日 _x86_64_ (8 CPU) Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.02 19.58 0.05 2.11 0.89 86.79 81.48 0.03 14.62 6.02 14.81 1.43 0.31 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 24000.00 0.00 192.00 0.00 96768.00 1008.00 156.60 854.35 0.00 854.35 5.21 100.10 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 27901.00 0.00 224.00 0.00 112500.00 1004.46 155.74 749.81 0.00 749.81 4.46 100.00 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 24000.00 0.00 192.00 0.00 96768.00 1008.00 156.69 770.62 0.00 770.62 5.21 100.00 |
iostat 显示 w/s 次数也就 200 左右,wkB/s 的文件操作也就 十几M 左右。但是 dd 显示性能很好。这里就是文件系统的影响,优先写入缓存,然后再由内核提交到磁盘设备。
1 2 3 4 |
dd if=/dev/zero of=sun.txt bs=64K count=10000 oflag=direct,nonblock 记录了10000+0 的读入 记录了10000+0 的写出 655360000字节(655 MB)已复制,25.3643 秒,25.8 MB/秒 |
我们增加了参数,使用了 direct 直接读写模式,绕过了系统缓存,然后通过 iostat 对比输出,发现数据指标基本一致了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
iostat -xzdk 1 /dev/vdb Linux 2.6.32-504.30.3.el6.x86_64 (devtest.bj.sm) 2018年12月25日 _x86_64_ (8 CPU) Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.02 19.59 0.05 2.11 0.89 86.82 81.50 0.03 14.63 6.02 14.83 1.43 0.31 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 0.00 1.00 231.00 4.00 14784.00 127.48 0.59 2.56 13.00 2.52 2.56 59.40 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 0.00 0.00 415.00 0.00 26560.00 128.00 0.99 2.38 0.00 2.38 2.38 98.70 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 15.00 0.00 417.00 0.00 26568.00 127.42 0.98 2.36 0.00 2.36 2.35 98.10 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util vdb 0.00 0.00 0.00 403.00 0.00 25792.00 128.00 0.98 2.44 0.00 2.44 2.44 98.20 |
在测试读时,确保缓存没有影响,可以用 echo 1 > /proc/sys/vm/drop_caches 删除缓存。然后测试
1 2 3 4 |
dd of=/dev/zero if=sun.txt bs=64K count=10000 iflag=direct,nonblock 记录了10000+0 的读入 记录了10000+0 的写出 655360000字节(655 MB)已复制,6.82211 秒,96.1 MB/秒 |
以上都是单线程测试,可以开启多个 dd 开启测试,但是结果无法汇总,只能通过 iostat 观察。有时候,单线程的读写没有充分发挥性能,需要逐步增加观察。
fio
对于更复杂的读写模式测试,可以使用 fio 工具,可以从 https://github.com/axboe/fio 下载。或者 yum 安装 sudo yum install fio。
测试顺序写
1 |
fio --name=randwrite --ioengine=sync --rw=write --bs=64k --direct=1 --size=1G --numjobs=8 --runtime=240 --group_reporting |
先来解释下参数
-
name 这个报告的名称
-
ioengine 使用的 I/O 引擎, sync 表示直接读写方式,调用 read or write
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
fio --enghelp= Available IO engines: e4defrag falloc fusion-aw-sync rdma binject guasi syslet-rw splice sg posixaio libaio net netsplice null sync psync vsync mmap cpuio |
- rw 读写模式, 我们选择顺序写测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fio --cmdhelp=rw rw: IO direction alias: readwrite type: string (opt=bla) default: read valid values: read Sequential read : write Sequential write : trim Sequential trim : randread Random read : randwrite Random write : randtrim Random trim : rw Sequential read and write mix : readwrite Sequential read and write mix : randrw Random read and write mix |
- bs block size 大小
- direct 是否设置 O_DIRECT 标记, 设置绕过系统缓存
- size 测试文件大小
- numjobs 并发执行线程数,并发执行多个写操作
- runtime 最多运行 240s
报告输出如下
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 |
randwrite: (g=0): rw=write, bs=64K-64K/64K-64K/64K-64K, ioengine=sync, iodepth=1 fio-2.0.13 Starting 8 processes Jobs: 7 (f=7): [WWWWWWEW] [100.0% done] [0K/96192K/0K /s] [0 /1503 /0 iops] [eta 00m:00s] randwrite: (groupid=0, jobs=8): err= 0: pid=8671: Tue Dec 25 10:11:36 2018 *write: io=8192.0MB, bw=95845KB/s, iops=1497 , runt= 87523msec clat (msec): min=1 , max=349 , avg= 5.33, stdev= 8.11 lat (msec): min=1 , max=349 , avg= 5.33, stdev= 8.11 * clat percentiles (msec): | 1.00th=[ 3], 5.00th=[ 3], 10.00th=[ 3], 20.00th=[ 4], | 30.00th=[ 4], 40.00th=[ 4], 50.00th=[ 4], 60.00th=[ 4], | 70.00th=[ 5], 80.00th=[ 6], 90.00th=[ 11], 95.00th=[ 14], | 99.00th=[ 19], 99.50th=[ 22], 99.90th=[ 113], 99.95th=[ 210], | 99.99th=[ 277] bw (KB/s) : min= 6016, max=18067, per=12.52%, avg=11995.65, stdev=1394.58 lat (msec) : 2=0.01%, 4=61.64%, 10=27.74%, 20=9.89%, 50=0.51% lat (msec) : 100=0.10%, 250=0.10%, 500=0.02% cpu : usr=0.20%, sys=0.44%, ctx=131544, majf=0, minf=230 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 : total=r=0/w=131072/d=0, short=r=0/w=0/d=0 Run status group 0 (all jobs): WRITE: io=8192.0MB, aggrb=95844KB/s, minb=95844KB/s, maxb=95844KB/s, mint=87523msec, maxt=87523msec * Disk stats (read/write): vdb: ios=0/131087, merge=0/160, ticks=0/695879, in_queue=695800, util=99.96% |
带 * 是重点关注的, write 可以看到 iops, 以及吞吐量 bw, 95M/s。
clat percentiles 统计的是提交 IO 任务到完成的时间间隔。可以关注 95%, 99% 之类的指标,总体的请求在多少时间内能完成。
顺序读
1 |
fio --name=randwrite --ioengine=sync --rw=read --bs=64k --direct=1 --size=1G --numjobs=8 --runtime=240 --group_reporting |
随机读
1 |
fio --name=randwrite --ioengine=sync --rw=randread --bs=64k --direct=1 --size=1G --numjobs=8 --runtime=240 --group_reporting |
顺序读写混合, 读写比例各占一半
1 |
fio --name=randwrite --ioengine=sync --rw=rw --bs=64k --direct=1 --size=1G --numjobs=8 --runtime=240 --group_reporting |
顺序读写混合,写占 20%
1 |
fio --name=randwrite --ioengine=sync --rw=rw --bs=64k --direct=1 --size=1G --numjobs=8 --runtime=240 --group_reporting --rwmixwrite=20 |
sysbench
sysbench 可以用来测试 CPU, 数据库,I/O 等性能指标。
查看测试 IO 的相关选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# sysbench fileio help sysbench 1.0.9 (using system LuaJIT 2.0.4) fileio options: --file-num=N number of files to create [128] --file-block-size=N block size to use in all IO operations [16384] --file-total-size=SIZE total size of files to create [2G] --file-test-mode=STRING test mode {seqwr, seqrewr, seqrd, rndrd, rndwr, rndrw} --file-io-mode=STRING file operations mode {sync,async,mmap} [sync] --file-async-backlog=N number of asynchronous operatons to queue per thread [128] --file-extra-flags=STRING additional flags to use on opening files {sync,dsync,direct} [] --file-fsync-freq=N do fsync() after this number of requests (0 - don't use fsync()) [100] --file-fsync-all[=on|off] do fsync() after each write operation [off] --file-fsync-end[=on|off] do fsync() at the end of test [on] --file-fsync-mode=STRING which method to use for synchronization {fsync, fdatasync} [fsync] --file-merged-requests=N merge at most this number of IO requests if possible (0 - don't merge) [0] --file-rw-ratio=N reads/writes ratio for combined test [1.5] |
使用 sysbench, 先进行准备阶段
1 |
sysbench --test=fileio --file-total-size=32G --file-num=8 prepare |
会生成 8 个文件,每个文件大小 32G/8 = 4G。file-total-size 最好是测试机器内存的 2倍。
测试顺序写
1 |
sysbench --test=fileio --file-total-size=32G --file-test-mode=seqwr --max-time=240 --file-block-size=4K --file-num=8 --threads=16 run |
file-test-mode 指定了测试模式, file-block-size 表示 block size, threads 表示运行的线程数。file-fsync-freq 表示刷新频率,默认每100次刷新一次。
结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
File operations: reads/s: 0.00 writes/s: 12307.20 fsyncs/s: 984.57 Throughput: read, MiB/s: 0.00 written, MiB/s: 48.07 General statistics: total time: 240.0129s total number of events: 3190221 Latency (ms): min: 0.00 avg: 1.20 max: 136.28 95th percentile: 11.65 sum: 3836714.72 Threads fairness: events (avg/stddev): 199388.8125/4609.89 execution time (avg/stddev): 239.7947/0.01 |
我们再测试下,关闭刷新频率的情况
1 |
sysbench --test=fileio --file-total-size=32G --file-test-mode=seqwr --max-time=240 --file-block-size=4K --file-num=8 --threads=16 --file-fsync-freq=0 run |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
File operations: reads/s: 0.00 writes/s: 28200.38 fsyncs/s: 0.00 Throughput: read, MiB/s: 0.00 written, MiB/s: 110.16 General statistics: total time: 240.0200s total number of events: 6768712 Latency (ms): min: 0.00 avg: 0.57 max: 844.02 95th percentile: 0.03 sum: 3834187.88 Threads fairness: events (avg/stddev): 423044.5000/8034.58 execution time (avg/stddev): 239.6367/0.02 |
发现比上面的性能要高,是不是每次刷新会提高性能吗?也不是,在测试过程中,要观察整个系统状况,CPU, 内存,IO 都要检查,第一次测试对系统影响较小,第二次系统压力更大,说明,第一次还没有充分发挥系统性能,可以增加线程数,或者文件个数。第二次基本到系统极限了,看上去性能要高。
测试顺序读
1 |
sysbench --test=fileio --file-total-size=32G --file-test-mode=seqrd --max-time=240 --file-block-size=4K --file-num=8 --threads=16 run |