磁盘 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

优化方案

  1. 添加索引(减少扫描)
  2. 使用 SSD
  3. 调整 innodb_buffer_pool_size
  4. 启用查询缓存
  5. 分库分表

案例2:日志写入慢

症状

  • 应用日志写入延迟高
  • 同步写性能差

优化

  1. 异步日志(syslog-ng, rsyslog)
  2. 批量写入
  3. 使用内存文件系统(tmpfs)
  4. 调整日志级别
  5. 日志轮转优化

案例3:小文件性能差

症状

  • 大量小文件读写慢
  • 元数据操作多

优化

  1. 合并小文件(tar)
  2. 使用对象存储
  3. 启用目录索引
  4. 增加 inode 缓存
  5. 考虑 SSD

下一步:学习 网络性能分析与优化 章节。