Linux磁盘性能测试完全指南:使用FIO深入评估存储系统

前言

在服务器运维和数据库调优工作中,了解磁盘的真实性能至关重要。不同的应用场景对磁盘性能的要求各不相同:数据库系统需要高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 顺序读写混合

三、测试环境准备

测试前注意事项

  1. 建议使用direct=1:绕过系统页缓存,测试真实磁盘性能
  2. 测试文件大小:应大于内存大小(建议为内存的2倍),避免缓存影响
  3. 测试时间:建议至少60秒,确保结果稳定
  4. 空闲系统:测试时尽量停止其他应用,避免干扰

创建测试目录

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

测试最佳实践

  1. 测试前清空缓存:确保测试真实磁盘性能
  2. 使用direct=1:绕过页缓存
  3. 测试时间不少于60秒:获得稳定结果
  4. 多次测试取平均值:减少偶然误差
  5. 记录测试环境:包括内核版本、文件系统、挂载参数

快速命令参考

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

本文为原创内容,欢迎转载,请注明出处。

相关推荐
csdn_aspnet2 小时前
告别重复造轮子:Codex写脚本,运维/DevOps场景下,用Codex批量生成Shell/Python自动化脚本
运维·python·ai·自动化·devops·codex·辅助编程
positive_zpc2 小时前
计算机网络——数据链路层(二)
服务器·网络·计算机网络
木心术12 小时前
DevOps全流程实践:从CI/CD到自动化测试的最佳实践
运维·ci/cd·devops
一直会游泳的小猫2 小时前
Homebrew - macOS 与 Linux 的包管理器
linux·运维·macos·brew·包管理工具
开开心心_Every2 小时前
文件强制删除工具,单文件拖入解锁删除简单
运维·edge·pdf·计算机外设·逻辑回归·散列表·启发式算法
代码论斤卖2 小时前
OpenHarmony的watchdog service频繁崩溃问题分析
linux·harmonyos
halazi1002 小时前
WSL 安装与 Ubuntu 24.04 配置使用指南
linux·运维·ubuntu·wsl
MDIOT旗舰2 小时前
4G低功耗边缘计算网关:让智能抄表与远程运维从未如此简单
运维·人工智能·边缘计算·modbus·红外抄表·plc智能网关
2302_803132252 小时前
ensp中关于交换机S3700不被识别的错误列举以及vlan 绑定acl后无法删除问题
运维·服务器·网络