前言
在服务器运维和数据库调优工作中,了解磁盘的真实性能至关重要。不同的应用场景对磁盘性能的要求各不相同:数据库系统需要高IOPS(每秒输入输出操作次数),而视频存储、日志归档则更看重顺序读写带宽。
本文将详细介绍如何使用 FIO(Flexible I/O Tester) 工具对磁盘进行全面的性能测试,涵盖随机读写、顺序读写、混合读写等多种场景,并提供详细的指标解读和测试脚本。
一、FIO工具简介
FIO是一个强大的磁盘性能测试工具,被业界广泛用于存储系统的基准测试。它支持多种I/O引擎(libaio、posixaio、synchronous等)、多种测试模式(随机、顺序)以及丰富的参数配置。
安装FIO
bash
# CentOS/RHEL
yum install -y fio
# Ubuntu/Debian
apt-get install -y fio
# 验证安装
fio --version
二、FIO核心参数详解
在使用FIO之前,需要了解以下几个关键参数:
| 参数 | 含义 | 示例 |
|---|---|---|
--name |
测试任务名称 | random-read |
--ioengine |
I/O引擎类型 | libaio(异步I/O) |
--rw |
读写模式 | randread/randwrite/read/write/randrw |
--bs |
块大小 | 4k、16k、64k、1m |
--numjobs |
并发进程/线程数 | 16 |
--size |
每个线程测试的数据量 | 1G、10G |
--runtime |
测试运行时间 | 60(秒) |
--time_based |
基于时间运行(达到runtime后停止) | - |
--group_reporting |
汇总所有job的报告 | - |
--direct |
是否绕过系统缓存(1=绕过,0=使用) | 1 |
--iodepth |
I/O队列深度 | 16、32、64 |
读写模式说明
| 模式 | 说明 |
|---|---|
randread |
随机读 |
randwrite |
随机写 |
randrw |
随机读写混合 |
read |
顺序读 |
write |
顺序写 |
rw |
顺序读写混合 |
三、测试环境准备
测试前注意事项
- 建议使用direct=1:绕过系统页缓存,测试真实磁盘性能
- 测试文件大小:应大于内存大小(建议为内存的2倍),避免缓存影响
- 测试时间:建议至少60秒,确保结果稳定
- 空闲系统:测试时尽量停止其他应用,避免干扰
创建测试目录
bash
# 创建测试文件存放目录
mkdir -p /data/fio_test
cd /data/fio_test
四、核心测试命令及脚本
4.1 基础测试命令
随机读测试(4K块大小)
bash
fio --name=random-read \
--ioengine=libaio \
--rw=randread \
--bs=4k \
--numjobs=16 \
--size=1G \
--runtime=60 \
--time_based \
--group_reporting \
--direct=1
随机写测试(4K块大小)
bash
fio --name=random-write \
--ioengine=libaio \
--rw=randwrite \
--bs=4k \
--numjobs=16 \
--size=1G \
--runtime=60 \
--time_based \
--group_reporting \
--direct=1
4.2 完整测试脚本(覆盖多种场景)
bash
#!/bin/bash
# fio_full_test.sh - 磁盘性能全面测试脚本
TEST_DIR="/data/fio_test"
RESULT_FILE="fio_result_$(date +%Y%m%d_%H%M%S).txt"
# 创建测试目录
mkdir -p ${TEST_DIR}
cd ${TEST_DIR}
# 测试配置
SIZE="2G" # 测试文件大小(建议大于内存)
RUNTIME="60" # 运行时间(秒)
NUMJOBS="16" # 并发数
IODEPTH="32" # I/O队列深度
echo "==========================================" | tee -a ${RESULT_FILE}
echo "磁盘性能测试报告" | tee -a ${RESULT_FILE}
echo "测试时间: $(date)" | tee -a ${RESULT_FILE}
echo "测试目录: ${TEST_DIR}" | tee -a ${RESULT_FILE}
echo "测试配置: SIZE=${SIZE}, RUNTIME=${RUNTIME}, NUMJOBS=${NUMJOBS}" | tee -a ${RESULT_FILE}
echo "==========================================" | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
# 1. 4K随机读测试
echo "【测试1】4K随机读 (randread)" | tee -a ${RESULT_FILE}
fio --name=randread_4k \
--ioengine=libaio \
--rw=randread \
--bs=4k \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_randread_4k 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_randread_4k
# 2. 4K随机写测试
echo "【测试2】4K随机写 (randwrite)" | tee -a ${RESULT_FILE}
fio --name=randwrite_4k \
--ioengine=libaio \
--rw=randwrite \
--bs=4k \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_randwrite_4k 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_randwrite_4k
# 3. 4K随机读写混合(70%读,30%写)
echo "【测试3】4K随机读写混合 70%读/30%写 (randrw)" | tee -a ${RESULT_FILE}
fio --name=randrw_4k \
--ioengine=libaio \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_randrw_4k 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_randrw_4k
# 4. 1M顺序读测试
echo "【测试4】1M顺序读 (read)" | tee -a ${RESULT_FILE}
fio --name=read_1m \
--ioengine=libaio \
--rw=read \
--bs=1m \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_read_1m 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_read_1m
# 5. 1M顺序写测试
echo "【测试5】1M顺序写 (write)" | tee -a ${RESULT_FILE}
fio --name=write_1m \
--ioengine=libaio \
--rw=write \
--bs=1m \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_write_1m 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_write_1m
# 6. 1M顺序读写混合(70%读,30%写)
echo "【测试6】1M顺序读写混合 70%读/30%写 (rw)" | tee -a ${RESULT_FILE}
fio --name=rw_1m \
--ioengine=libaio \
--rw=rw \
--rwmixread=70 \
--bs=1m \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_rw_1m 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_rw_1m
# 7. 4K顺序读测试
echo "【测试7】4K顺序读 (read)" | tee -a ${RESULT_FILE}
fio --name=read_4k \
--ioengine=libaio \
--rw=read \
--bs=4k \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_read_4k 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_read_4k
# 8. 4K顺序写测试
echo "【测试8】4K顺序写 (write)" | tee -a ${RESULT_FILE}
fio --name=write_4k \
--ioengine=libaio \
--rw=write \
--bs=4k \
--size=${SIZE} \
--numjobs=${NUMJOBS} \
--iodepth=${IODEPTH} \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_write_4k 2>&1 | tee -a ${RESULT_FILE}
echo "" | tee -a ${RESULT_FILE}
rm -f test_write_4k
echo "==========================================" | tee -a ${RESULT_FILE}
echo "测试完成!结果已保存至: ${RESULT_FILE}" | tee -a ${RESULT_FILE}
echo "==========================================" | tee -a ${RESULT_FILE}
4.3 参数化测试脚本(支持自定义)
bash
#!/bin/bash
# fio_param_test.sh - 参数化磁盘测试脚本
# 使用说明
usage() {
echo "Usage: $0 -d <directory> -b <block_size> -t <test_type>"
echo " -d: 测试目录"
echo " -b: 块大小 (4k, 16k, 64k, 1m)"
echo " -t: 测试类型 (randread, randwrite, randrw, read, write, rw)"
echo " -j: 并发数 (默认16)"
echo " -r: 运行时间(秒) (默认60)"
exit 1
}
# 默认值
TEST_DIR="/data/fio_test"
BS="4k"
RW="randread"
NUMJOBS=16
RUNTIME=60
# 解析参数
while getopts "d:b:t:j:r:h" opt; do
case $opt in
d) TEST_DIR=$OPTARG ;;
b) BS=$OPTARG ;;
t) RW=$OPTARG ;;
j) NUMJOBS=$OPTARG ;;
r) RUNTIME=$OPTARG ;;
h) usage ;;
*) usage ;;
esac
done
# 创建测试目录
mkdir -p ${TEST_DIR}
cd ${TEST_DIR}
# 生成测试文件名
TEST_FILE="test_${RW}_${BS}"
echo "=========================================="
echo "FIO参数化测试"
echo "测试目录: ${TEST_DIR}"
echo "块大小: ${BS}"
echo "测试模式: ${RW}"
echo "并发数: ${NUMJOBS}"
echo "运行时间: ${RUNTIME}秒"
echo "=========================================="
# 执行测试
fio --name=${TEST_FILE} \
--ioengine=libaio \
--rw=${RW} \
--bs=${BS} \
--size=2G \
--numjobs=${NUMJOBS} \
--iodepth=32 \
--runtime=${RUNTIME} \
--time_based \
--direct=1 \
--group_reporting \
--filename=${TEST_FILE}
# 清理
rm -f ${TEST_FILE}
五、测试结果解读
FIO测试完成后,会输出详细的统计信息。以下是对关键指标的解读。
5.1 典型输出示例
random-read: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=32
...
Jobs: 16 (f=16): [r(16)][100.0%][r=124MiB/s,w=0KiB/s][r=31.7k IOPS][eta 00m:00s]
read: IOPS=31.7k, BW=124MiB/s (130MB/s)(7288MiB/60001msec)
slat (usec): min=5, max=1125, avg=12.35, stdev= 8.21
clat (usec): min=18, max=15284, avg=1024.56, stdev=245.67
lat (usec): min=25, max=15302, avg=1037.12, stdev=248.91
clat percentiles (usec):
| 1.00th=[ 245], 5.00th=[ 387], 10.00th=[ 494], 20.00th=[ 619],
| 30.00th=[ 725], 40.00th=[ 824], 50.00th=[ 947], 60.00th=[ 1057],
| 70.00th=[ 1172], 80.00th=[ 1336], 90.00th=[ 1582], 95.00th=[ 1844],
| 99.00th=[ 2507], 99.50th=[ 2835], 99.90th=[ 3957], 99.95th=[ 4424],
| 99.99th=[ 6194]
bw ( KiB/s): min=114688, max=139264, per=100.00%, avg=126976.00, stdev=5120.00
iops : min=28672, max=34816, avg=31744.00, stdev=1280.00
lat (usec) : 50=0.01%, 100=0.05%, 250=0.93%, 500=4.21%, 750=10.56%
lat (msec) : 1=25.34%, 2=52.18%, 4=6.32%, 10=0.38%, 20=0.02%
cpu : usr=2.15%, sys=23.45%, ctx=1892345, majf=0, minf=125
IO depths : 1=0.1%, 2=0.3%, 4=0.8%, 8=2.5%, 16=15.2%, 32=81.2%
5.2 关键指标解读
| 指标 | 含义 | 参考说明 |
|---|---|---|
| IOPS | 每秒I/O操作次数 | 随机读写场景的核心指标 |
| BW | 带宽(每秒传输数据量) | 顺序读写场景的核心指标 |
| clat | 完成延迟(从提交到完成) | 延迟越低性能越好 |
| slat | 提交延迟(从生成到提交) | 通常很小,可忽略 |
| lat | 总延迟(slat+clat) | 用户感知的延迟 |
| clat percentiles | 延迟百分位分布 | 99th延迟影响长尾请求体验 |
| cpu | CPU使用率 | 高IOPS通常伴随高CPU |
5.3 不同场景的性能参考
以下是不同类型磁盘的性能参考值(仅供参考,实际性能取决于具体硬件):
| 磁盘类型 | 4K随机读IOPS | 4K随机写IOPS | 1M顺序读带宽 | 1M顺序写带宽 |
|---|---|---|---|---|
| HDD 7200转 | 80-150 | 80-150 | 150-200MB/s | 150-200MB/s |
| SATA SSD | 20k-50k | 20k-40k | 500-550MB/s | 450-520MB/s |
| NVMe SSD (Gen3) | 200k-400k | 150k-300k | 3.0-3.5GB/s | 2.5-3.0GB/s |
| NVMe SSD (Gen4) | 400k-800k | 400k-600k | 6.0-7.0GB/s | 5.0-6.0GB/s |
| 企业级NVMe | 800k-1.5M | 600k-1.0M | 7.0+GB/s | 6.0+GB/s |
六、测试结果截图示例
6.1 4K随机读测试结果
random-read: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=32
...
read: IOPS=31.7k, BW=124MiB/s (130MB/s)(7288MiB/60001msec)
6.2 4K随机写测试结果
random-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=32
...
write: IOPS=28.5k, BW=111MiB/s (116MB/s)(6660MiB/60001msec)
6.3 1M顺序读测试结果
read: (g=0): rw=read, bs=1M-1M/1M-1M/1M-1M, ioengine=libaio, iodepth=32
...
read: IOPS=3050, BW=3050MiB/s (3198MB/s)(179GB/60001msec)
6.4 1M顺序写测试结果
write: (g=0): rw=write, bs=1M-1M/1M-1M/1M-1M, ioengine=libaio, iodepth=32
...
write: IOPS=2650, BW=2650MiB/s (2779MB/s)(155GB/60001msec)
七、高级测试场景
7.1 模拟数据库负载(8K块,70%读30%写)
bash
fio --name=db_simulate \
--ioengine=libaio \
--rw=randrw \
--rwmixread=70 \
--bs=8k \
--size=10G \
--numjobs=32 \
--iodepth=64 \
--runtime=300 \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_db
7.2 测试磁盘最大IOPS(极限压力)
bash
fio --name=max_iops \
--ioengine=libaio \
--rw=randread \
--bs=4k \
--size=4G \
--numjobs=64 \
--iodepth=128 \
--runtime=60 \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_iops
7.3 测试磁盘最大带宽(顺序大块)
bash
fio --name=max_bw \
--ioengine=libaio \
--rw=read \
--bs=1m \
--size=20G \
--numjobs=4 \
--iodepth=64 \
--runtime=60 \
--time_based \
--direct=1 \
--group_reporting \
--filename=test_bw
八、常见问题及解决
问题1:测试结果不稳定
原因:系统缓存、其他进程干扰
解决:
bash
# 清空系统缓存
echo 3 > /proc/sys/vm/drop_caches
# 使用direct=1绕过缓存
--direct=1
问题2:IO深度不足导致性能偏低
解决:增加iodepth和numjobs
bash
--iodepth=64 --numjobs=32
问题3:测试文件太小,结果偏高
原因:测试文件完全被缓存
解决:测试文件大小应为内存的2倍以上
bash
--size=16G # 假设内存为8G
九、总结
性能评估要点
| 应用场景 | 关注指标 | 推荐测试 |
|---|---|---|
| 数据库(OLTP) | 4K随机读/写IOPS | 4K randread/randwrite |
| 数据库(OLAP) | 大块顺序读带宽 | 1M read |
| 虚拟机/容器 | 混合读写延迟 | 4K randrw + 关注99th延迟 |
| 文件服务器 | 顺序读写带宽 | 1M read/write |
| 日志存储 | 顺序写带宽 | 1M write |
测试最佳实践
- 测试前清空缓存:确保测试真实磁盘性能
- 使用direct=1:绕过页缓存
- 测试时间不少于60秒:获得稳定结果
- 多次测试取平均值:减少偶然误差
- 记录测试环境:包括内核版本、文件系统、挂载参数
快速命令参考
bash
# 4K随机读(IOPS测试)
fio --name=test --rw=randread --bs=4k --size=2G --numjobs=16 --iodepth=32 --runtime=60 --direct=1 --group_reporting
# 1M顺序读(带宽测试)
fio --name=test --rw=read --bs=1m --size=2G --numjobs=4 --iodepth=32 --runtime=60 --direct=1 --group_reporting
# 混合读写(模拟数据库)
fio --name=test --rw=randrw --rwmixread=70 --bs=8k --size=4G --numjobs=32 --iodepth=64 --runtime=120 --direct=1 --group_reporting
本文为原创内容,欢迎转载,请注明出处。