Sysbench 完全指南 / 第八章:文件 I/O 测试
第八章:文件 I/O 测试
8.1 概述
文件 I/O 性能直接影响数据库、日志系统、大数据处理等场景的整体性能。Sysbench 的 fileio 测试模块可以测量存储设备的顺序读写、随机读写性能,是服务器选型和存储评估的必备工具。
8.1.1 适用场景
| 场景 | 测试内容 |
|---|---|
| 服务器选型 | 对比 SSD vs HDD、NVMe vs SATA |
| 文件系统对比 | ext4 vs xfs vs btrfs vs zfs |
| RAID 方案评估 | RAID 0 / 1 / 5 / 10 性能对比 |
| 云存储评估 | EBS / 云盘 / NAS 性能对比 |
| I/O 调度器对比 | mq-deadline / bfq / none / kyber |
| 内核参数调优 | 预读大小、队列深度等 |
8.2 测试模式
8.2.1 可用的 I/O 测试模式
| 模式 | 说明 | 用途 |
|---|---|---|
seqwr | 顺序写(Sequential Write) | 日志写入、备份、数据导入 |
seqrd | 顺序读(Sequential Read) | 数据扫描、备份恢复 |
seqrewr | 顺序重写(Sequential Rewrite) | 文件原地更新 |
rndrd | 随机读(Random Read) | 数据库查询、随机访问 |
rndwr | 随机写(Random Write) | 数据库写入、随机更新 |
rndrw | 随机读写混合(Random Read/Write) | OLTP 数据库、混合负载 |
8.2.2 测试流程
文件 I/O 测试遵循三阶段流程:
prepare → run → cleanup
│ │ │
│ │ └── 删除测试文件
│ └─────────── 执行 I/O 测试
└──────────────────── 创建测试文件
# 1. 准备(创建测试文件)
sysbench fileio --file-total-size=10G prepare
# 2. 运行测试
sysbench fileio --file-total-size=10G --file-test-mode=rndrw --time=60 run
# 3. 清理(删除测试文件)
sysbench fileio --file-total-size=10G cleanup
重要:
prepare阶段会创建与--file-total-size等大的文件。确保目标目录有足够空间!
8.3 文件 I/O 选项
8.3.1 核心选项
| 选项 | 默认值 | 说明 |
|---|---|---|
--file-test-mode | rndrw | 测试模式:seqwr/seqrd/seqrewr/rndrd/rndwr/rndrw |
--file-total-size | 2G | 测试文件总大小 |
--file-num | 128 | 测试文件数量 |
--file-block-size | 16K | I/O 块大小 |
--file-io-mode | sync | I/O 模式:sync(同步)/ async(异步) |
--file-async-backlog | 128 | 异步 I/O 的并发请求数 |
--file-extra-flags | (无) | 额外的文件打开标志 |
--file-fsync-freq | 100 | 每 N 次写操作后执行 fsync(0 = 不 fsync) |
--file-fsync-all[=on/off] | off | 每次写操作后都 fsync |
--file-fsync-end[=on/off] | on | 测试结束时 fsync |
--file-merged-requests | 0 | 合并相同位置的 I/O 请求(0 = 不合并) |
--file-rw-ratio | 1.5 | 随机读写模式下的读/写比例 |
8.3.2 I/O 模式详解
同步 I/O (sync):
请求1 → 等待完成 → 请求2 → 等待完成 → ...
优点: 延迟准确
缺点: 并发度低
异步 I/O (async, io_uring/libaio):
请求1 ──→ 发出
请求2 ──→ 发出 (并发)
请求3 ──→ 发出
等待全部完成
优点: 高并发、高吞吐
缺点: 需要内核支持
8.4 顺序读写测试
8.4.1 顺序写测试
# 准备测试文件
sysbench fileio --file-total-size=20G --file-num=16 prepare
# 顺序写测试
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=seqwr \
--file-block-size=1M \
--file-io-mode=sync \
--file-fsync-freq=1000 \
--threads=1 \
--time=120 \
run
8.4.2 顺序读测试
# 顺序读测试(文件已在 prepare 阶段创建)
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=seqrd \
--file-block-size=1M \
--file-io-mode=sync \
--threads=1 \
--time=120 \
run
8.4.3 顺序重写测试
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=seqrewr \
--file-block-size=1M \
--threads=1 \
--time=120 \
run
8.4.4 多线程顺序读写
# 使用 4 个线程并行顺序读
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=seqrd \
--file-block-size=1M \
--threads=4 \
--time=120 \
run
注意:顺序 I/O 测试通常使用单线程或少量线程,因为顺序操作本身就是串行化的。多线程顺序 I/O 可能产生磁盘寻道,退化为随机 I/O。
8.5 随机读写测试
8.5.1 随机读测试
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=rndrd \
--file-block-size=4K \
--file-io-mode=sync \
--threads=32 \
--time=120 \
run
8.5.2 随机写测试
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=rndwr \
--file-block-size=4K \
--file-fsync-freq=100 \
--threads=32 \
--time=120 \
run
8.5.3 随机读写混合测试
# 默认读写比例 1.5:1
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=rndrw \
--file-block-size=4K \
--file-rw-ratio=1.5 \
--file-fsync-freq=100 \
--threads=32 \
--time=120 \
--histogram \
run
# 自定义读写比例 7:3(更接近 OLTP 读多写少场景)
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=rndrw \
--file-block-size=4K \
--file-rw-ratio=2.33 \
--threads=32 \
--time=120 \
run
8.5.4 异步 I/O 测试
# 使用 Linux AIO(libaio)
sysbench fileio \
--file-total-size=20G \
--file-num=16 \
--file-test-mode=rndrd \
--file-block-size=4K \
--file-io-mode=async \
--file-async-backlog=128 \
--threads=32 \
--time=120 \
run
# 注意:需要内核支持 AIO
# 检查方法: ls /dev/aio 或 cat /proc/sys/fs/aio-max-nr
8.6 输出结果解读
8.6.1 示例输出
File operations:
reads/s: 45678.90 ← 每秒读操作数
writes/s: 30452.60 ← 每秒写操作数
fsyncs/s: 3045.26 ← 每秒 fsync 次数
Throughput: 吞吐量
read, MiB/s: 178.43 ← 读吞吐量(MB/s)
written, MiB/s: 118.96 ← 写吞吐量(MB/s)
General statistics: 总体统计
total time: 120.0024s
total number of events: 9487654
Latency (ms): 延迟
min: 0.01
avg: 0.40
max: 12.34
95th percentile: 0.62
sum: 3798.12
Threads fairness:
events (avg/stddev): 296489.1875/1234.56
execution time (avg/stddev): 119.9992/0.00
8.6.2 关键指标
| 指标 | 含义 | SSD 典型值 | HDD 典型值 |
|---|---|---|---|
| reads/s | IOPS(读) | 10K-500K | 100-200 |
| writes/s | IOPS(写) | 10K-300K | 100-200 |
| read, MiB/s | 吞吐量(读) | 500-7000 MB/s | 100-200 MB/s |
| written, MiB/s | 吞吐量(写) | 400-5000 MB/s | 80-180 MB/s |
| avg latency | 平均延迟 | 0.05-0.5 ms | 5-15 ms |
| 95th percentile | P95 延迟 | 0.1-1 ms | 10-20 ms |
8.7 综合测试方案
8.7.1 完整存储性能评估
#!/bin/bash
# full_fileio_benchmark.sh - 完整存储性能评估
TEST_DIR="/data/sysbench_test" # 测试目录(建议使用独立分区)
FILE_SIZE="20G"
FILE_NUM=16
DURATION=120
RESULT_DIR="./fileio_results_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULT_DIR"
cd "$TEST_DIR"
# 创建测试文件
echo ">>> Preparing test files..."
sysbench fileio --file-total-size=$FILE_SIZE --file-num=$FILE_NUM prepare
# 测试列表
declare -A TESTS=(
["seqwr"]="顺序写"
["seqrd"]="顺序读"
["seqrewr"]="顺序重写"
["rndrd"]="随机读"
["rndwr"]="随机写"
["rndrw"]="随机读写"
)
BLOCK_SIZES=("4K" "16K" "64K" "256K" "1M")
for mode in seqwr seqrd seqrewr rndrd rndwr rndrw; do
for bs in "${BLOCK_SIZES[@]}"; do
echo ""
echo ">>> Testing: $mode, block_size=$bs"
if [[ "$mode" == seq* ]]; then
threads=1
else
threads=16
fi
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-num=$FILE_NUM \
--file-test-mode=$mode \
--file-block-size=$bs \
--file-fsync-freq=100 \
--threads=$threads \
--time=$DURATION \
--histogram \
--json="$RESULT_DIR/${mode}_${bs}.json" \
run 2>&1 | tee "$RESULT_DIR/${mode}_${bs}.txt" | \
grep -E "(reads/s|writes/s|MiB/s|95th|avg:)"
sleep 3
done
done
# 清理
echo ">>> Cleaning up..."
sysbench fileio --file-total-size=$FILE_SIZE --file-num=$FILE_NUM cleanup
echo ""
echo "Results saved to $RESULT_DIR"
8.7.2 线程扩展性测试
#!/bin/bash
# fileio_threads_test.sh - I/O 线程扩展性测试
FILE_SIZE="20G"
FILE_NUM=16
sysbench fileio --file-total-size=$FILE_SIZE --file-num=$FILE_NUM prepare
for threads in 1 2 4 8 16 32 64 128; do
echo "=== Threads: $threads ==="
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-num=$FILE_NUM \
--file-test-mode=rndrw \
--file-block-size=4K \
--threads=$threads \
--time=60 \
run 2>&1 | grep -E "(reads/s|writes/s|95th)"
sleep 3
done
sysbench fileio --file-total-size=$FILE_SIZE --file-num=$FILE_NUM cleanup
8.8 文件系统对比测试
8.8.1 对比 ext4 / xfs / btrfs
#!/bin/bash
# fs_comparison.sh - 文件系统对比测试
FS_LIST=("ext4" "xfs" "btrfs")
DEVICE="/dev/sdb"
FILE_SIZE="10G"
for fs in "${FS_LIST[@]}"; do
echo ""
echo "==========================================="
echo "File System: $fs"
echo "==========================================="
# 格式化(注意:会销毁数据!)
sudo mkfs.$fs -f $DEVICE
sudo mkdir -p /mnt/test_$fs
sudo mount $DEVICE /mnt/test_$fs
sudo chmod 777 /mnt/test_$fs
# 准备
cd /mnt/test_$fs
sysbench fileio --file-total-size=$FILE_SIZE prepare
# 随机读测试
echo "--- Random Read ---"
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-test-mode=rndrd \
--file-block-size=4K \
--threads=32 \
--time=60 \
run 2>&1 | grep -E "(reads/s|95th)"
# 随机写测试
echo "--- Random Write ---"
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-test-mode=rndwr \
--file-block-size=4K \
--file-fsync-freq=100 \
--threads=32 \
--time=60 \
run 2>&1 | grep -E "(writes/s|95th)"
# 顺序读测试
echo "--- Sequential Read ---"
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-test-mode=seqrd \
--file-block-size=1M \
--threads=1 \
--time=60 \
run 2>&1 | grep -E "(MiB/s|95th)"
# 清理
sysbench fileio --file-total-size=$FILE_SIZE cleanup
cd /
sudo umount /mnt/test_$fs
sleep 5
done
警告:此脚本会格式化磁盘!请勿在生产环境或有数据的磁盘上运行。
8.9 I/O 调度器对比测试
8.9.1 Linux I/O 调度器简介
| 调度器 | 特点 | 适用场景 |
|---|---|---|
mq-deadline | 合并请求,减少寻道 | 通用,数据库推荐 |
bfq | 公平队列,低延迟 | 桌面环境、交互式 |
kyber | 双队列(读/写),低延迟 | 快速设备(NVMe) |
none / noop | 不调度,直接发送 | NVMe SSD |
8.9.2 对比脚本
#!/bin/bash
# io_scheduler_test.sh - I/O 调度器对比测试
DEVICE="sda" # 替换为你的设备名
FILE_SIZE="10G"
# 查看当前调度器
echo "Current scheduler:"
cat /sys/block/$DEVICE/queue/scheduler
# 可用调度器列表
SCHEDULERS=$(cat /sys/block/$DEVICE/queue/scheduler | tr -d '[]' | tr ' ' '\n')
sysbench fileio --file-total-size=$FILE_SIZE prepare
for sched in $SCHEDULERS; do
echo ""
echo "=== Scheduler: $sched ==="
echo $sched | sudo tee /sys/block/$DEVICE/queue/scheduler
sleep 2
echo "--- Random Read (4K) ---"
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-test-mode=rndrd \
--file-block-size=4K \
--threads=32 \
--time=60 \
run 2>&1 | grep -E "(reads/s|95th)"
echo "--- Random Write (4K) ---"
sysbench fileio \
--file-total-size=$FILE_SIZE \
--file-test-mode=rndwr \
--file-block-size=4K \
--file-fsync-freq=100 \
--threads=32 \
--time=60 \
run 2>&1 | grep -E "(writes/s|95th)"
sleep 3
done
sysbench fileio --file-total-size=$FILE_SIZE cleanup
# 恢复默认调度器
echo "mq-deadline" | sudo tee /sys/block/$DEVICE/queue/scheduler
8.10 块大小对性能的影响
8.10.1 测试不同块大小
#!/bin/bash
# block_size_test.sh
FILE_SIZE="10G"
sysbench fileio --file-total-size=$FILE_SIZE prepare
echo "Block Size,Read IOPS,Read BW(MB/s),Read P95(ms),Write IOPS,Write BW(MB/s),Write P95(ms)"
for bs in "512" "1K" "2K" "4K" "8K" "16K" "32K" "64K" "128K" "256K" "512K" "1M"; do
# 随机读
read_result=$(sysbench fileio \
--file-total-size=$FILE_SIZE --file-test-mode=rndrd \
--file-block-size=$bs --threads=16 --time=30 run 2>&1)
read_iops=$(echo "$read_result" | grep "reads/s:" | awk '{print $2}')
read_bw=$(echo "$read_result" | grep "read, MiB/s:" | awk '{print $3}')
read_p95=$(echo "$read_result" | grep "95th percentile:" | awk '{print $3}')
# 随机写
write_result=$(sysbench fileio \
--file-total-size=$FILE_SIZE --file-test-mode=rndwr \
--file-block-size=$bs --file-fsync-freq=100 --threads=16 --time=30 run 2>&1)
write_iops=$(echo "$write_result" | grep "writes/s:" | awk '{print $2}')
write_bw=$(echo "$write_result" | grep "written, MiB/s:" | awk '{print $3}')
write_p95=$(echo "$write_result" | grep "95th percentile:" | awk '{print $3}')
echo "$bs,$read_iops,$read_bw,$read_p95,$write_iops,$write_bw,$write_p95"
done
sysbench fileio --file-total-size=$FILE_SIZE cleanup
预期趋势:
块大小 IOPS 吞吐量
4K 最高 IOPS 较低吞吐
16K 高 IOPS 中等吞吐
64K 中等 IOPS 较高吞吐
1M 最低 IOPS 最高吞吐
- 数据库:通常使用 4K-16K 块大小,关注 IOPS
- 大数据/日志:通常使用 256K-1M 块大小,关注吞吐量
8.11 高级选项
8.11.1 fsync 策略
# 不使用 fsync(最高性能,但不保证数据持久化)
sysbench fileio --file-test-mode=rndwr --file-fsync-freq=0 run
# 每 10 次写入后 fsync
sysbench fileio --file-test-mode=rndwr --file-fsync-freq=10 run
# 每次写入后 fsync(最安全,最低性能)
sysbench fileio --file-test-mode=rndwr --file-fsync-all=on run
| fsync 策略 | 场景 | 性能影响 |
|---|---|---|
fsync-freq=0 | 纯吞吐量测试 | 无影响 |
fsync-freq=100 | 一般数据库场景 | 轻微影响 |
fsync-freq=1 | 金融级持久化 | 显著影响 |
fsync-all=on | 最严格持久化 | 严重影响 |
8.11.2 Direct I/O
# 使用 O_DIRECT 标志(绕过操作系统缓存)
sysbench fileio \
--file-test-mode=rndrd \
--file-block-size=4K \
--file-extra-flags=direct \
--file-total-size=20G \
--threads=32 \
--time=60 \
run
重要:使用
direct标志可以避免操作系统文件缓存的影响,获得更准确的磁盘性能数据。但并非所有文件系统都支持 O_DIRECT。
8.11.3 同步模式
# 使用 O_SYNC 标志(每次写入后同步到磁盘)
sysbench fileio \
--file-test-mode=rndwr \
--file-block-size=4K \
--file-extra-flags=sync \
--file-fsync-freq=0 \
--file-total-size=20G \
--threads=16 \
--time=60 \
run
# 同时使用 O_DIRECT + O_SYNC
sysbench fileio \
--file-test-mode=rndwr \
--file-extra-flags="direct,fsync" \
--file-total-size=20G \
--threads=16 \
--time=60 \
run
8.12 常见问题
问题 1:测试结果受缓存影响
# 现象:第二次运行比第一次快很多
# 原因:数据被缓存在 OS 页面缓存中
# 解决方案 1:使用 O_DIRECT
sysbench fileio --file-extra-flags=direct ...
# 解决方案 2:测试前清空缓存
echo 3 | sudo tee /proc/sys/vm/drop_caches
# 解决方案 3:使用大于内存的数据集
sysbench fileio --file-total-size=$(free -g | awk '/Mem/{print $2*2}')G ...
问题 2:prepare 阶段耗时很长
# 原因:创建大文件需要时间
# 解决方案:使用 fallocate 快速创建文件
fallocate -l 20G /path/to/test_file
# Sysbench 1.0.20+ 会自动使用 fallocate
# 如果不支持,会回退到写零方式(较慢)
问题 3:测试文件占用空间
# 测试后记得清理
sysbench fileio --file-total-size=20G cleanup
# 手动检查
ls -lh /path/to/test_dir/
du -sh /path/to/test_dir/
8.13 小结
| 要点 | 说明 |
|---|---|
| 测试模式 | seqwr/seqrd/rndrd/rndwr/rndrw 六种模式 |
| 流程 | prepare → run → cleanup(三步) |
| 关键指标 | IOPS、吞吐量(MB/s)、延迟(ms) |
| 缓存影响 | 使用 O_DIRECT 或大于内存的数据集 |
| 块大小 | 数据库 4K-16K,大数据 256K-1M |
| 调度器 | NVMe 用 none,SATA SSD 用 mq-deadline |