这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。
IO 栈连接文件系统和块设备。应用一次 write 可能经历页缓存、文件系统、通用块层、IO 调度器、设备驱动和云存储。定位 IO 问题要区分慢在应用、文件系统、块设备还是远端存储。
17.1 IO 栈全景
Application
-> System Call
-> VFS
-> File System
-> Page Cache
-> Block Layer
-> IO Scheduler
-> Device Driver
-> Disk / SSD / Cloud Disk
同步 IO:
调用线程等待 IO 完成
异步 IO:
提交 IO 后继续执行
-> completion notification
17.2 指标
延迟:
| 指标 | 含义 |
|---|---|
| await | IO 平均等待时间 |
| r_await | 读等待 |
| w_await | 写等待 |
| svctm | 旧指标,现代工具不推荐单独依赖 |
吞吐:
| 指标 | 含义 |
|---|---|
| r/s、w/s | IOPS |
| rkB/s、wkB/s | 带宽 |
| rrqm/s、wrqm/s | 合并请求 |
队列:
| 指标 | 含义 |
|---|---|
| aqu-sz | 平均队列深度 |
| rareq-sz | 平均读请求大小 |
| wareq-sz | 平均写请求大小 |
| util | 设备忙碌时间占比 |
iostat -xz 1
17.3 iostat 解读
示例:
Device r/s w/s rkB/s wkB/s await aqu-sz util
vdb 100 500 800 8000 20.00 10.0 95.00
判断:
| 现象 | 方向 |
|---|---|
| await 高、util 高 | 设备压力高或规格不足 |
| IOPS 高、吞吐低 | 小 IO |
| 吞吐高、IOPS 低 | 大块顺序 IO |
| util 100% 但 await 低 | SSD 并行能力强,util 语义有限 |
| 单进程 bo 高 | 应用刷盘或日志 |
util 在多队列设备上不能简单理解为“磁盘满”。要结合延迟、队列和容量指标。
17.4 进程 IO
pidstat -d 1
iotop -P -d 2
cat /proc/<pid>/io
/proc/<pid>/io 常见字段:
| 字段 | 含义 |
|---|---|
| read_bytes | 实际读块设备字节 |
| write_bytes | 实际写块设备字节 |
| syscr | 读系统调用次数 |
| syscw | 写系统调用次数 |
| cancelled_write_bytes | 被截断等方式抵消的写 |
应用写页缓存成功不代表已经落盘。
17.5 块大小与对齐
查看:
blockdev --getbsz /dev/vdb
blockdev --getss /dev/vdb
stat -f /data
影响:
- 数据库 page size;
- 文件系统分配组;
- RAID 条带大小;
- 小 IO 合并效率;
- 云盘性能规格。
生产不建议盲目手工调块大小,应以数据库、文件系统和存储厂商建议为准,并用真实 workload 验证。
17.6 IO 调度器
查看:
cat /sys/block/vdb/queue/scheduler
lsblk -D
常见策略:
| 策略 | 特点 |
|---|---|
| none / noop | 尽量不重排 |
| mq-deadline | 面向延迟目标 |
| bfq | 面向公平和交互 |
| kyber | 面向低延迟 |
修改:
echo mq-deadline | sudo tee /sys/block/vdb/queue/scheduler
云盘和 NVMe 设备常有默认优化策略。没有压测证据时不要随意修改。
17.7 直 IO 与缓冲 IO
Buffered IO:
write -> page cache -> writeback
Direct IO:
write -> block device
特点:
| 模式 | 优点 | 代价 |
|---|---|---|
| Buffered | 命中快、接口简单 | 双缓存、回写时机不确定 |
| Direct | 应用自控缓存、减少拷贝 | 对齐要求高、实现复杂 |
数据库引擎通常按自身配置决定 direct IO、fdatasync、O_DSYNC 等策略。
17.8 预读
查看:
cat /sys/block/vdb/queue/read_ahead_kb
blockdev --getra /dev/vdb
调整:
sudo blockdev --setra 4096 /dev/vdb
顺序扫描可能受益,随机读可能浪费缓存。调整后必须用真实业务压测验证。
17.9 压测与验证
fio 示例:
sudo fio --name=rand-read \
--filename=/data/fio.test \
--direct=1 \
--rw=randread \
--bs=4k \
--ioengine=libaio \
--iodepth=32 \
--numjobs=4 \
--runtime=60 \
--time_based \
--group_reporting
写测试:
sudo fio --name=rand-write \
--filename=/data/fio.test \
--direct=1 \
--rw=randwrite \
--bs=8k \
--ioengine=libaio \
--iodepth=64 \
--numjobs=4 \
--runtime=60 \
--time_based \
--group_reporting
注意:
- 只在测试盘执行;
- 写测试会破坏数据;
- 记录文件系统和云盘规格;
- 记录队列深度和块大小;
- 和数据库真实模型分开评估。
17.10 常见故障
数据库写入延迟高
iostat -xz 1
pidstat -d 1
cat /proc/meminfo | grep -E 'Dirty|Writeback'
检查:
- checkpoint;
- redo 刷盘;
- 云盘规格;
- 快照并发;
- 同宿主机干扰;
- 慢 SQL 放大读。
应用 fsync 慢
strace -T -e trace=fsync,fdatasync -p <pid>
iostat -xz 1
评估:
- 存储延迟;
- fsync 频率;
- 批量提交能力;
- 持久化语义;
- 文件系统行为。
NFS 延迟高
mount | grep nfs
nfsstat -rc
ping -c 100 <nfs-server>
mtr -rwzc 100 <nfs-server>
本章小结
IO 分析要把延迟、IOPS、吞吐、队列和进程写入对应起来。await 是核心指标,util 只能作参考。生产优化前先确认存储规格和真实 workload,再用压测验证调度器、预读、直 IO 和应用刷盘策略。
思考题
- IOPS 和吞吐分别适合评估什么 workload?
await高说明什么?- buffered IO 和 direct IO 有什么差异?
- 为什么 fsync 频繁会造成延迟?
- 设计一个数据库盘的 fio 测试方案。