etcd磁盘性能要求与fio测试工具
- linux
- 10天前
- 0评论
背景:
当我们部署kubernetes集群时,部署etcd组件,要测试磁盘的IO读写性能,如果磁盘的IO读写性能较低,就会在连接etcd时,出现timeout超时,或者出现leader change现象,所以必须先测试磁盘的IO读写性能,当性能满足时,才能部署etcd集群。
fio(Flexible I/O Tester)是 Linux/Unix 下最常用的 I/O 基准测试工具,用来模拟各种磁盘/存储工作负载(顺序读写、随机读写、混合读写、不同 block size、不同并发等)。
它可以对 文件(文件系统层)或 裸设备(block 设备)进行测试,因此既能测文件系统,也能测原始磁盘/SSD/NVMe/RAID/云盘的性能。
1 理解相关指标和概念
### IOPS(I/O ops/sec)
单位:每秒完成的 IO 请求数。与 block size 有直接关系(同样 IOPS、较大 block size => 更高吞吐量)。
### 带宽(BW)
单位:KB/s、MB/s(或 MiB/s)。BW ≈ IOPS × block_size(相同 block_size 时成立)。
### 延迟(latency)
fio 报告通常包含:slat(submit latency)、clat(completion latency)和 lat(总延迟 = slat + clat)。单位通常是 usec(微秒)或 ms。
### 队列深度(iodepth)
每个 job 在内核/设备前可以同时挂起的未完成 IO 数。更高 iodepth 可以提高并行性与吞吐,但也可能增加延迟。
### numjobs / threads
同时运行的并发 job 数(每个 job 又有 iodepth)。总并发 = numjobs × iodepth(近似)。
### 随机 vs 顺序(rand vs seq)
随机 I/O 对 SSD/HDD 的表现差异很大;数据库类负载通常更关注 4K 随机读/写和尾延迟。
### O_DIRECT / direct=1
绕过内核 page cache(测真实设备性能),通常测试物理设备时应启用 direct=1。
### 预热(preconditioning)
对 SSD 做随机写测试前,要先“预热”设备直到性能稳定(否则会测到缓存/擦写放大等非稳态表现)。
2 主要使用参数
--name=<name>:测试名称(job的名字)
--filename=<path>:文件或设备路径(例如 /data/testfile 或 /dev/nvme0n1)
可以是具体的文件位置,也可以是设备文件
注意:如果写裸设备(如 /dev/nvme0n1)会抹掉数据,请注意系统和数据安全,测试原始磁盘性能时一定要确认设备是未挂载使用的。
ioengine=libaio时不能对目录并发IO
--size=1G:文件大小(或每 job 的目标大小)
如何合理的设置size的大小???
如果你只想测设备极限性能,size 可以设得较小,比如 1G 或 10G,要注意小数据量可能命中缓存;如果是评估稳定性能(更真实)推荐size至少大于设备缓存,甚至大于内存容量;如果是长时间压力测试(带 --time_based)一般使用time_based 和runtime=60(或更久),再设一个比较大的size(比如设备容量的 50% 或固定 100G/500G)
数据库(OLTP,小块随机 IO)
--size建议 ≥ 内存大小(比如 2× 内存),避免命中页缓存。
日志/队列(顺序写)
--size可以相对小,但最好覆盖设备写缓存(几 GB 以上)。
大数据/顺序读写
--size建议几十 GB 以上,模拟大文件吞吐。
虚拟化/分布式存储
--size建议设置为设备容量的 30%~50%,更接近生产环境。
--ioengine=libaio|io_uring|sync|psync:I/O 后端。
fio 的核心参数之一,决定了 fio 发起 I/O 请求的方式。不同引擎对应着不同的 I/O 调用模型(fio 在执行 I/O 时,需要调用操作系统/库函数来“发请求”;不同的
ioengine就是不同的 I/O 实现方式,比如 同步/异步、系统调用/内核接口、用户态/内核态;)
--direct=1:使用 O_DIRECT(绕过 page cache)。
--rw=read|write|randread|randwrite|randrw|readwrite:I/O 模式。
--bs=4k|1M:每个 I/O 的大小(block size)。
小块(1K ~ 8K)
- 单次 I/O 数据少,IOPS 高,但吞吐低;
- 延迟对业务影响更明显;
- 适合模拟数据库(OLTP)这类小块随机访问。
中等块(16K ~ 64K)
- IOPS 和吞吐兼顾;
- 很多日志型/中等负载业务会落在这里。
大块(128K ~ 1M 甚至更大)
- 单次 I/O 数据大,IOPS 低,但吞吐高;
- 适合模拟顺序扫描、大文件拷贝、大数据分析。
--iodepth=32:队列深度(对 libaio、io_uring 有效)。
每个job任务向IO引擎提交IO请求,内核会把请求放入队列,最大可挂起32个请求,使队列维持32的“飞行深度”,配置建议参考如下,具体的设置要结合自己的业务场景模式进行,重点在于你的场景中更多的关注那些指标。
SSD(SATA SSD)
- 控制器并行度有限,一般
iodepth=32就够,超过 64 提升不大。NVMe SSD
- 支持很高队列深度(通常 64k),但 fio 实测常用范围是
1 ~ 256;- 推荐测试点:
iodepth=1, 4, 16, 32, 64, 128,看性能和延迟曲线。HDD(机械盘)
- 物理寻道瓶颈明显,高队列深度提升有限;
iodepth=1~4就能看到真实表现,再大意义不大。企业存储/RAID/分布式存储
- 建议扫描从小到大(1, 4, 16, 32, 64, 128, 256),找到性能饱和点。
--numjobs=4:并发 job 数。
--time_based / --runtime=60:以时间为基准运行(比如跑 60 秒)。
默认情况下,fio 运行时是“写完多少数据”就结束,例如你设定
--size=1G,那么每个 job 会写/读 1GB 后退出。加上--time_based,fio 会忽略--size(除非--size比运行时间还小),只按时间运行,直到--runtime到期才停止,也会配合--ramp_time=10使用,意味着前10 秒不记录结果(给设备/缓存“预热”),之后才算正式数据
--group_reporting:合并 job 报告,便于查看总吞吐/IOPS。
--rwmixread=70:读写混合时设置读占比(适用于 randrw)。
--output=xxx / --output-format=json:把结果导出为文件或 JSON,便于自动分析。
--buffered=1/0:是否使用文件系统缓存(通常用 direct=1 替代)。
通常使用--direct=1,否则你可能只在测内存缓存,得到假高的吞吐
--fallocate=1:先用 fallocate 预分配文件,避免测试时文件系统分配干扰。
3 测试常用推荐
注意!注意!注意!
必须先确定是对文件系统测试,还是原始磁盘进行测试,保证数据和系统安全(写操作会擦除数据),要知道这个命令实际做的事和测到的性能,以及命令执行的风险。
# 顺序读(4K)
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k \
--iodepth=32 --rw=read --size=8G --name=seqread_4k \
--time_based --runtime=60 --group_reporting
模拟 全表扫描 场景
看 bw=(带宽)和 iops=
# 随机读(4K)
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k \
--iodepth=32 --numjobs=4 --rw=randread --size=8G --name=randread_4k \
--time_based --runtime=60 --group_reporting
模拟 数据库索引查找、OLTP 查询场景
看 iops=(数据库性能关键指标)
# 顺序写(4K)
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k \
--iodepth=32 --rw=write --size=8G --name=seqwrite_4k \
--time_based --runtime=60 --group_reporting
模拟 顺序写 WAL/redo 日志
看带宽和延迟
# 随机写(4K)
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k \
--iodepth=32 --numjobs=4 --rw=randwrite --size=8G --name=randwrite_4k \
--time_based --runtime=60 --group_reporting
模拟 事务更新(UPDATE/INSERT)
随机写 IOPS 越高,数据库事务吞吐越高
# 混合读写(4K)
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k \
--iodepth=32 --numjobs=4 --rw=randrw --rwmixread=70 --size=8G \
--name=randrw_8k --time_based --runtime=60 --group_reporting
模拟 真实数据库工作负载(70% 读,30% 写)
3.1 测试案例:
touch /data/testfile
fio --filename=/data/testfile --direct=1 --ioengine=libaio --bs=4k --iodepth=32 --numjobs=4 --rw=randrw --rwmixread=70 --size=1G --name=randrw_4k --time_based --runtime=60 --group_reporting
输出:
Starting 4 processes
Jobs: 4 (f=4): [m(4)][100.0%][r=6164KiB/s,w=2576KiB/s][r=1541,w=644 IOPS][eta 00m:00s]
randrw_4k: (groupid=0, jobs=4): err= 0: pid=31974: Mon Nov 10 17:32:45 2025
read: IOPS=1556, BW=6225KiB/s (6375kB/s)(365MiB/60053msec)
slat (usec): min=3, max=61122, avg=36.59, stdev=1076.15
clat (usec): min=143, max=190264, avg=57489.01, stdev=30836.18
lat (usec): min=712, max=190271, avg=57525.74, stdev=30815.39
clat percentiles (msec):
| 1.00th=[ 5], 5.00th=[ 10], 10.00th=[ 11], 20.00th=[ 29],
| 30.00th=[ 41], 40.00th=[ 51], 50.00th=[ 61], 60.00th=[ 70],
| 70.00th=[ 71], 80.00th=[ 81], 90.00th=[ 93], 95.00th=[ 110],
| 99.00th=[ 131], 99.50th=[ 140], 99.90th=[ 161], 99.95th=[ 161],
| 99.99th=[ 180]
bw ( KiB/s): min= 1264, max= 4888, per=25.00%, avg=1555.97, stdev=304.23, samples=480
iops : min= 316, max= 1222, avg=388.99, stdev=76.05, samples=480
write: IOPS=673, BW=2694KiB/s (2759kB/s)(158MiB/60053msec)
slat (usec): min=3, max=61253, avg=67.21, stdev=1597.56
clat (usec): min=293, max=199686, avg=57038.51, stdev=30860.15
lat (usec): min=619, max=199715, avg=57105.88, stdev=30855.37
clat percentiles (msec):
| 1.00th=[ 5], 5.00th=[ 10], 10.00th=[ 11], 20.00th=[ 21],
| 30.00th=[ 40], 40.00th=[ 51], 50.00th=[ 61], 60.00th=[ 70],
| 70.00th=[ 71], 80.00th=[ 81], 90.00th=[ 92], 95.00th=[ 110],
| 99.00th=[ 130], 99.50th=[ 140], 99.90th=[ 161], 99.95th=[ 163],
| 99.99th=[ 190]
bw ( KiB/s): min= 472, max= 2240, per=24.99%, avg=673.26, stdev=155.31, samples=480
iops : min= 118, max= 560, avg=168.28, stdev=38.81, samples=480
lat (usec) : 250=0.01%, 500=0.02%, 750=0.01%, 1000=0.02%
lat (msec) : 2=0.04%, 4=0.62%, 10=6.64%, 20=8.75%, 50=22.75%
lat (msec) : 100=54.07%, 250=7.08%
cpu : usr=0.31%, sys=0.98%, ctx=103759, majf=0, minf=51
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=99.9%, >=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.1%, 64=0.0%, >=64=0.0%
issued rwts: total=93462,40446,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=32
Run status group 0 (all jobs):
READ: bw=6225KiB/s (6375kB/s), 6225KiB/s-6225KiB/s (6375kB/s-6375kB/s), io=365MiB (383MB), run=60053-60053msec
WRITE: bw=2694KiB/s (2759kB/s), 2694KiB/s-2694KiB/s (2759kB/s-2759kB/s), io=158MiB (166MB), run=60053-60053msec
Disk stats (read/write):
vda: ios=93635/40530, merge=0/196, ticks=5334724/2293302, in_queue=7387852, util=97.24%
随机读 IOPS=1556, BW=6225KiB/s avg=57525.74us
随机写 IOPS=673, BW=2694KiB/s avg=57105.88us
