问题背景

最近在维护一个在线文档处理服务时,频繁收到用户反馈文件上传和处理速度过慢的投诉。通过监控系统发现,服务器的CPU和内存使用率都处于合理范围内,但磁盘I/O等待时间却经常飙升到90%以上。这种典型的I/O瓶颈问题在数据密集型应用中很常见,今天就来分享我是如何定位和解决这个问题的。

初步诊断:识别I/O瓶颈

首先使用iostat命令查看磁盘I/O状况:

# 每2秒刷新一次,显示扩展统计信息
iostat -x 2

输出结果显示:

  • %util持续在95%以上
  • await(平均I/O等待时间)高达150ms以上
  • svctm(服务时间)相对正常

这表明磁盘已经严重过载,I/O请求在队列中等待时间过长。

深入分析:找出罪魁祸首

1. 使用iotop定位高I/O进程

iotop -o -P

发现主要问题是nginx和php-fpm进程在处理大文件上传时产生了大量临时文件写入操作。

2. 检查文件系统日志

dmesg | grep -i "error\|warning\|i/o"

发现了大量关于"I/O error"和"buffer I/O"的警告信息。

解决方案实施

优化文件系统挂载参数

原来的挂载参数比较简单:

/dev/sdb1 /data ext4 defaults 0 0

优化后的挂载选项:

/dev/sdb1 /data ext4 defaults,noatime,nodiratime,data=writeback 0 0

关键参数说明:

  • noatimenodiratime:减少访问时间更新带来的I/O开销
  • data=writeback:提高写入性能,但需要确保有可靠的UPS

调整I/O调度器

检查当前调度器:

cat /sys/block/sdb/queue/scheduler

将调度器从默认的cfq改为更适合SSD的deadline

echo deadline > /sys/block/sdb/queue/scheduler

优化nginx临时文件配置

在nginx配置中增加缓冲优化:

client_body_buffer_size 1M;
client_max_body_size 10M;
client_body_temp_path /dev/shm/nginx_temp 1 2;
proxy_temp_path /dev/shm/nginx_proxy_temp;

这里的关键是把临时文件目录指向/dev/shm(内存文件系统),大幅减少磁盘I/O。

调整内核I/O参数

/etc/sysctl.conf中添加:

# 增加脏页回写阈值
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10

# 调整I/O队列深度
block.queue_depth = 256

# 优化虚拟内存参数
vm.swappiness = 10
vm.vfs_cache_pressure = 50

应用配置:

sysctl -p

监控与验证

优化后再次使用iostat监控:

iostat -x 2

关键指标改善:

  • %util从95%+降至40%-60%
  • await从150ms+降至20ms左右
  • 用户响应时间改善明显

经验总结

  1. 监控先行:没有监控就谈不上优化,iostat、iotop是基础工具
  2. 分层排查:从应用层到系统层再到硬件层,逐层排查
  3. 权衡利弊:每个优化都有代价,比如data=writeback提高了性能但牺牲了数据安全性
  4. 持续观察:优化后要持续监控,确保没有引入新问题

这次调优让我深刻体会到,很多时候性能问题不是单一因素造成的,而是多个小问题的累积效应。通过系统性的分析和针对性的优化,往往能用较小的代价获得显著的性能提升。