人人都会AI编程

22.3 磁盘 IO 瓶颈排查:定位高 IO 进程、优化读写策略

更新时间:2026-07-12

磁盘 I/O 瓶颈通常表现为系统响应变慢、服务超时、CPU 大量时间消耗在 iowait 状态,而真正的罪魁祸首往往是少数几个进程在进行大量低效的读写操作。排查的第一步是快速建立全局视图,然后逐步收敛到具体进程和文件,最后采取针对性的优化措施。

1. 快速判断系统级 I/O 压力

iostat 是最常用的工具,不带参数运行可看到自系统启动以来的统计;加上 -x 1 则每秒输出详细扩展信息:

iostat -x 1

主要关注指标:

  • %util:设备带宽利用率。接近 100% 表示磁盘已经满负荷,但不代表一定有瓶颈(SSD 可以并行处理请求,此值可能虚高);
  • await:单个 I/O 请求的平均等待时间(毫秒),包含队列时间和处理时间。对机械盘,超过 20~30ms 需注意;对 SSD,超过 5~10ms 可能异常;
  • r/sw/s:每秒读写请求数,如果某块盘远高于其他盘,需进一步排查;
  • avgqu-sz:平均请求队列长度,持续大于 1 说明请求堆积。

若发现某块磁盘(如 sda)的 await 异常高、%util 接近 100%,基本可确认存在 I/O 瓶颈,接下来需定位是哪些进程导致。

2. 定位高 I/O 的进程

方法一:iotop
类似 top 的实时 I/O 监控工具,直观展示每个进程的磁盘读写速率和 I/O 百分比:

sudo iotop -o

-o 只显示正在执行 I/O 的进程。按左右箭头可切换排序字段,迅速锁定 DISK READDISK WRITE 最高的进程。对数据库或日志写入进程一目了然。

方法二:pidstat -d
若系统未安装 iotop(如最小化服务器环境),可使用 sysstat 包中的 pidstat

pidstat -d 1

每秒输出进程级别的 I/O 统计:kB_rd/skB_wr/siodelay(等待 I/O 完成的时钟滴答数)。iodelay 越高,说明该进程受 I/O 阻塞越严重。

方法三:从 /proc 文件系统直接获取
查看特定进程的 I/O 详情:

cat /proc/<PID>/io

会显示 read_byteswrite_bytes 等累计值。间隔几秒读取两次,差值就是该时段的 I/O 量。虽然原始,但无需额外工具,适合脚本化监控。

方法四:结合 lsof 判断进程正在读写哪些文件
锁定高 I/O 进程后,用 lsof -p <PID> 查看其打开的文件描述符,可以找到其正在操作的具体文件或设备(如日志文件、数据库数据文件、临时目录等),帮助判断 I/O 来源。

3. 分析 I/O 模式并优化

定位到高 I/O 进程及文件后,可以从以下几个方向入手优化。

(1)应用层优化 —— 通常见效最快

  • 减少不必要的写操作:检查是否记录了过于详细的调试日志;考虑降低日志级别或采用异步记录。临时解决方案可重定向日志到 /dev/shm(内存文件系统)或 /dev/null 进行对比验证。
  • 批量读写:应用程序应尽可能将多次小 I/O 合并为一次大 I/O。例如,数据库配置中增大缓冲区(如 MySQL 的 innodb_buffer_pool_size),减少直接刷盘频率;写入文件时使用缓冲流,定期调用 fflush()
  • 异步 I/O:Linux 提供了 AIO 或 io_uring 等接口,允许进程提交 I/O 请求后立即返回,不阻塞业务线程。对高并发服务(如消息队列、文件上传服务)效果显著。可检查应用是否支持并开启异步 I/O 模式。
  • 文件布局:对频繁随机读写的文件,可以考虑将其移动到更快的存储介质(如 NVMe SSD)。对于顺序读写密集的大文件,尽量使用连续存储,避免碎片化。

(2)文件系统与挂载选项

  • 选择合适的文件系统:XFS 擅长处理大文件并行写入;ext4 通用且稳定;btrfs/ZFS 提供快照和压缩,但有性能开销。
  • 挂载选项优化
  • noatime:取消文件最后访问时间的更新,能显著减少元数据写操作,对读取密集型场景(如 Web 静态资源)非常有效。
  • nodiratime:仅禁止目录的访问时间更新。
  • data=ordered(ext4默认):保证数据先于元数据写入,避免文件损坏,但有一定开销。若应用自行保证一致性,可考虑调整为 data=writeback 提升性能(有风险,需测试)。

/etc/fstab 中相应行添加选项即可:

/dev/sda2 /data ext4 defaults,noatime,nodiratime 0 0

(3)I/O 调度器调整

Linux 内核为不同的存储设备提供了不同的 I/O 调度器。可通过以下命令查看当前使用的调度器:

cat /sys/block/sda/queue/scheduler

回显中 [] 内为当前调度器,如 [mq-deadline] none

  • 对于 HDD:传统的 mq-deadlinebfq 能够较好地处理旋转延迟,保证交互性。
  • 对于 NVMe/SATA SSD:通常推荐使用 none(多队列模式)或 mq-deadlinenone 直接将请求下发给硬件,利用 SSD 内部并行通道,减少内核开销。
  • 临时修改(重启失效):
  echo none > /sys/block/sda/queue/scheduler
  
  • 永久生效:在启动器配置中(如 GRUB)添加 elevator=none 内核参数。

(4)内核参数微调

涉及内存与写回策略的 /proc/sys/vm/ 参数可以在某些场景缓解 I/O 尖峰:

  • vm.dirty_ratiovm.dirty_background_ratio:控制多少百分比的系统内存被“脏页”(待写回磁盘的缓存)占用后开始强制写回。适当减小这些值可以让写操作更平滑,避免突发大量刷盘导致 I/O 阻塞。例如:
  sysctl -w vm.dirty_background_ratio=5
  sysctl -w vm.dirty_ratio=10
  
  • vm.vfs_cache_pressure:控制系统保留 directory/inode 缓存的倾向。设为较低值(如 50)可使内核更积极地缓存文件元数据,对文件大量增删的操作场景有益。

每次修改内核参数均应在测试环境评估,并监控具体业务指标。

(5)硬件与架构层面

当应用和系统层面的优化已达到极限,负载增长仍需要更多 I/O 能力时,考虑:

  • 使用更高性能的磁盘:从机械盘迁移到企业级 SSD,或从 SATA SSD 升级至 NVMe。
  • RAID 与条带化:将单个大文件或高频访问文件分散到多块物理磁盘上,并行读写提高吞吐量。
  • 采用具备缓存层的方案:如使用带有掉电保护的 RAID 卡 + 缓存,或在操作系统层部署 bcachedm-cache 将 SSD 作为 HDD 的缓存。

实践建议

磁盘 I/O 排查是一个逐步聚焦的过程:先用 iostat 看全局,再用 iotoppidstat 找进程,接着用 lsof 关联到文件,最后针对该文件的使用模式进行优化。大部分紧急关头,临时降低日志级别、清理不必要的写操作或重启可快速恢复服务,而根本性的解决方案往往需要与应用开发人员协同,从读写模式入手。优化后务必再次通过相同工具验证指标改善,确保变更确实解决了瓶颈,而非仅仅是掩盖了问题。