磁盘 I/O 性能分析与优化
磁盘 I/O 是系统性能的常见瓶颈,理解存储子系统对优化至关重要。
存储性能基础
存储设备类型
HDD(机械硬盘):
特点:
├─ 顺序读写快(100-200 MB/s)
├─ 随机读写慢(100-200 IOPS)
├─ 寻道时间:5-10ms
└─ 旋转延迟:4-8ms
性能瓶颈:
└─ 机械部件移动速度
SSD(固态硬盘):
特点:
├─ 顺序读写快(500-3500 MB/s)
├─ 随机读写快(10K-500K IOPS)
├─ 无寻道时间
└─ 延迟低(<0.1ms)
性能考虑:
├─ 写入放大
├─ 磨损均衡
└─ 垃圾回收
NVMe SSD:
优势:
├─ PCIe 接口(vs SATA)
├─ 并行队列(vs 单队列)
├─ 低延迟(~10μs)
└─ 高带宽(3-7 GB/s)
I/O 栈层次
应用层
↓ read/write/open/close
VFS(虚拟文件系统)
↓
页缓存(Page Cache)
↓
文件系统(Ext4/XFS/Btrfs)
↓
块层(Block Layer)
├─ I/O 调度器
├─ 块设备队列
└─ I/O 合并
↓
设备驱动(SCSI/NVMe)
↓
硬件控制器
↓
物理磁盘
I/O 性能指标
IOPS(I/O Per Second):
- 每秒完成的 I/O 操作数
- 随机 I/O 的关键指标
- HDD:~100,SSD:10K-500K
吞吐量(Throughput):
- 每秒传输的数据量(MB/s)
- 顺序 I/O 的关键指标
- 受设备带宽和 I/O 大小影响
延迟(Latency):
- 单个 I/O 请求的完成时间
- 影响应用响应性
- 包含:排队时间 + 服务时间
队列深度(Queue Depth):
- 等待处理的 I/O 请求数
- 影响并发性能
- 过深可能增加延迟
I/O 模式分析
顺序 vs 随机
顺序 I/O:
特点:
├─ 访问连续的数据块
├─ HDD 友好(无需寻道)
├─ 高吞吐量
└─ 预读有效
典型场景:
├─ 日志文件
├─ 大文件复制
└─ 流媒体
随机 I/O:
特点:
├─ 访问分散的数据块
├─ HDD 性能差(频繁寻道)
├─ 低吞吐量
└─ 预读效果差
典型场景:
├─ 数据库查询
├─ 随机读写文件
└─ 元数据操作
读 vs 写
读操作:
缓存影响:
├─ 命中缓存:极快(内存速度)
├─ 未命中缓存:需访问磁盘
└─ 预读优化
同步 vs 异步:
├─ 同步读:等待数据返回
└─ 异步读:可并发处理
写操作:
缓存写策略:
├─ Write-Through:直接写磁盘(慢但安全)
├─ Write-Back:先写缓存(快但风险)
└─ 文件系统日志
同步 vs 异步:
├─ 同步写(fsync):确保持久化
├─ 异步写:写入缓存即返回
└─ 定期刷新(pdflush/writeback)
小 I/O vs 大 I/O
I/O 大小影响:
小 I/O(4KB):
├─ IOPS 受限
├─ 元数据开销大
└─ 适合随机访问
大 I/O(1MB):
├─ 吞吐量高
├─ 摊销开销
└─ 适合顺序访问
最优大小:
├─ 数据库:8-16KB
├─ 流媒体:64KB-1MB
└─ 备份:1MB+
I/O 瓶颈识别
I/O 等待症状
iowait 高:
含义:
├─ CPU 等待 I/O 完成时的空闲时间
├─ 不代表 I/O 有问题
└─ 需结合其他指标
判断真正的 I/O 瓶颈:
1. iowait 高
2. 磁盘利用率高(%util > 80%)
3. 队列深度大(avgqu-sz > 1)
4. 等待时间长(await > 20ms)
磁盘饱和:
指标:
├─ %util 接近 100%
├─ avgqu-sz 持续 > 1
├─ await 显著增加
└─ svctm 接近极限
影响:
├─ 应用响应慢
├─ 数据库查询慢
└─ 系统整体性能下降
I/O 模式问题
小随机写:
问题:
├─ HDD 最差场景
├─ 频繁寻道
└─ 写入放大(SSD)
解决:
├─ 合并写入(批处理)
├─ 使用 SSD
├─ 调整应用逻辑
└─ 使用写缓存
同步 I/O 过多:
问题:
├─ 每次 I/O 等待完成
├─ 无法并发
└─ 延迟累积
解决:
├─ 异步 I/O(aio)
├─ 减少 fsync 调用
├─ 使用 O_DIRECT
└─ 批量提交
I/O 调度器
调度器类型
NOOP(No Operation):
特点:
├─ 简单的 FIFO 队列
├─ 无重排序
└─ 几乎无开销
适用:
├─ SSD/NVMe(无寻道)
├─ 虚拟化环境(宿主机已调度)
└─ 硬件 RAID(控制器调度)
Deadline(截止时间):
特点:
├─ 保证 I/O 在截止时间前完成
├─ 读写分离队列
└─ 防止饿死
默认截止时间:
├─ 读:500ms
└─ 写:5000ms
适用:
├─ 实时性要求高
├─ 数据库
└─ 多媒体应用
CFQ(Completely Fair Queuing):
特点:
├─ 每进程一个队列
├─ 时间片轮转
├─ 公平性好
└─ 吞吐量可能降低
适用:
├─ 多用户系统
├─ 桌面环境
└─ 公平性重要的场景
BFQ(Budget Fair Queuing):
特点:
├─ CFQ 的改进版
├─ 按 I/O 预算分配
└─ 更好的交互性
适用:
├─ 桌面/笔记本
└─ 低延迟要求
mq-deadline / kyber(多队列):
特点:
├─ 支持多队列(NVMe)
├─ 并行调度
└─ 现代 SSD 优化
适用:
├─ NVMe SSD
└─ 高性能存储
调度器选择
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# 修改调度器(临时)
echo deadline > /sys/block/sda/queue/scheduler
echo noop > /sys/block/sda/queue/scheduler
# 永久修改(内核参数)
# /etc/default/grub
GRUB_CMDLINE_LINUX="elevator=deadline"
update-grub
# 推荐配置
HDD:deadline 或 cfq
SSD:noop 或 deadline
NVMe:none(mq-deadline)
文件系统优化
挂载选项
性能相关选项:
# noatime:不更新访问时间
mount -o noatime /dev/sda1 /mnt
# nodiratime:不更新目录访问时间
mount -o nodiratime /dev/sda1 /mnt
# relatime:相对访问时间(默认,推荐)
mount -o relatime /dev/sda1 /mnt
# commit:延长提交间隔(秒)
mount -o commit=60 /dev/sda1 /mnt
# barrier:写屏障(安全 vs 性能)
mount -o barrier=0 /dev/sda1 /mnt # 禁用(风险)
mount -o barrier=1 /dev/sda1 /mnt # 启用(安全)
# 日志模式(Ext4)
mount -o data=writeback /dev/sda1 /mnt # 最快
mount -o data=ordered /dev/sda1 /mnt # 默认
mount -o data=journal /dev/sda1 /mnt # 最安全
预读优化
# 查看预读大小
blockdev --getra /dev/sda
# 设置预读大小(扇区数)
blockdev --setra 4096 /dev/sda # 2MB
# 对于 SSD
blockdev --setra 256 /dev/sda # 128KB(较小)
# 对于 HDD + 顺序读
blockdev --setra 8192 /dev/sda # 4MB(较大)
文件系统选择
Ext4:
特点:
├─ 成熟稳定
├─ 性能均衡
└─ 广泛支持
优化:
├─ extent 机制(大文件)
├─ 延迟分配
└─ 目录索引(HTree)
XFS:
特点:
├─ 大文件性能好
├─ 并行 I/O 优化
└─ 延迟分配
适用:
├─ 大文件系统(TB 级)
├─ 流媒体服务器
└─ 备份存储
Btrfs:
特点:
├─ 写时复制(CoW)
├─ 快照和克隆
├─ 数据校验
└─ 压缩支持
适用:
├─ 需要快照
├─ 数据完整性重要
└─ 存储池管理
I/O 优化技术
直接 I/O(Direct I/O)
O_DIRECT 标志:
特点:
├─ 绕过页缓存
├─ 直接读写磁盘
└─ 减少内存拷贝
优势:
├─ 避免缓存污染
├─ 应用自己管理缓存
└─ 确定性延迟
限制:
├─ 必须对齐(块大小)
├─ 大小限制
└─ 失去内核缓存优化
适用:
├─ 数据库(自己有缓存)
├─ 流媒体
└─ 备份工具
异步 I/O(AIO)
Linux AIO:
优势:
├─ 非阻塞
├─ 并发提交
└─ 批量完成通知
API:
├─ io_setup:创建上下文
├─ io_submit:提交 I/O
├─ io_getevents:获取完成事件
└─ io_destroy:销毁上下文
适用:
├─ 高并发 I/O
├─ 数据库
└─ 异步服务器
io_uring(新接口):
优势:
├─ 更低延迟
├─ 更高吞吐
├─ 更少系统调用
└─ 支持更多操作
性能提升:
├─ 共享内存环形缓冲区
├─ 无锁提交
└─ 批量操作
内存映射(mmap)
mmap 优势:
优势:
├─ 减少内存拷贝
├─ 页面按需加载
├─ 内核管理缓存
└─ 多进程共享
适用:
├─ 只读大文件
├─ 随机访问
└─ 共享数据
预分配
fallocate:
作用:
├─ 预分配磁盘空间
├─ 避免碎片
└─ 保证空间可用
使用:
fallocate -l 10G file.bin
优势:
├─ 减少文件系统元数据更新
├─ 连续分配(减少碎片)
└─ 快速失败(空间不足)
I/O 性能分析工具
iostat
# 扩展统计
iostat -x 1
# 关键指标
rrqm/s, wrqm/s:读写请求合并率
r/s, w/s:读写请求速率
rkB/s, wkB/s:读写吞吐量
avgqu-sz:平均队列长度
await:平均等待时间(ms)
svctm:平均服务时间(已废弃)
%util:设备利用率
# 解读
%util 接近 100%:可能饱和
await 大幅增加:性能下降
avgqu-sz > 1:有排队
iotop
# 实时监控进程 I/O
iotop
# 只显示有 I/O 的进程
iotop -o
# 累积模式
iotop -a
# 关键列
TID:线程 ID
DISK READ/WRITE:读写速率
IO:I/O 百分比
COMMAND:命令
blktrace
# 块层追踪
blktrace -d /dev/sda -o trace
# 停止(另一终端)
Ctrl+C
# 分析
blkparse -i trace
# 输出解读
Q:请求入队
G:获取请求
D:请求发送到驱动
C:请求完成
# 计算延迟
延迟 = C - Q
fio 基准测试
# 随机读测试
fio --name=randread \
--ioengine=libaio \
--iodepth=16 \
--rw=randread \
--bs=4k \
--direct=1 \
--size=1G \
--numjobs=4 \
--runtime=60 \
--group_reporting
# 顺序写测试
fio --name=seqwrite \
--ioengine=libaio \
--iodepth=16 \
--rw=write \
--bs=1M \
--direct=1 \
--size=1G \
--numjobs=1 \
--runtime=60
# 混合测试
fio --name=mixed \
--ioengine=libaio \
--iodepth=16 \
--rw=randrw \
--rwmixread=70 \
--bs=4k \
--direct=1 \
--size=1G \
--runtime=60
RAID 优化
RAID 级别选择
RAID 0(条带化):
特点:
├─ 无冗余
├─ 最高性能
└─ 容量 = 磁盘数 × 单盘容量
性能:
├─ 读写性能倍增
└─ 并行 I/O
风险:
└─ 任一磁盘故障导致数据丢失
RAID 1(镜像):
特点:
├─ 完全冗余
├─ 读性能提升
└─ 容量 = 单盘容量
性能:
├─ 读:可以翻倍
└─ 写:无提升(需写两份)
可靠性:
└─ 容忍单盘故障
RAID 5(奇偶校验):
特点:
├─ 单盘冗余
├─ 容量 = (磁盘数 - 1) × 单盘容量
└─ 分布式校验
性能:
├─ 读:好
└─ 写:差(写惩罚)
适用:
└─ 读多写少场景
RAID 10(1+0):
特点:
├─ 镜像 + 条带
├─ 容量 = 总容量 / 2
└─ 最佳性能和可靠性
性能:
├─ 读写都优秀
└─ 无写惩罚
成本:
└─ 磁盘利用率 50%
I/O 优化案例
案例1:数据库慢查询
症状:
- 数据库查询慢
- iowait 高
- 磁盘 %util 高
分析:
iostat -x 1
iotop -o
优化方案:
- 添加索引(减少扫描)
- 使用 SSD
- 调整 innodb_buffer_pool_size
- 启用查询缓存
- 分库分表
案例2:日志写入慢
症状:
- 应用日志写入延迟高
- 同步写性能差
优化:
- 异步日志(syslog-ng, rsyslog)
- 批量写入
- 使用内存文件系统(tmpfs)
- 调整日志级别
- 日志轮转优化
案例3:小文件性能差
症状:
- 大量小文件读写慢
- 元数据操作多
优化:
- 合并小文件(tar)
- 使用对象存储
- 启用目录索引
- 增加 inode 缓存
- 考虑 SSD
下一步:学习 网络性能分析与优化 章节。