上周三凌晨2点15分,监控系统突然发出刺耳的警报声——我们部署在阿里云上的Ubuntu 18.04生产服务器出现持续性的高IO读延迟,平均响应时间从平时的3ms飙升至187ms。通过CloudMonitor控制台可以看到,磁盘读吞吐量稳定在95MB/s左右,iowait长期维持在40%以上,这种情况已经持续了将近6小时。
重要提示:当发现服务器iowait持续超过20%时,就应该立即着手排查,否则可能引发连锁反应导致服务雪崩。
我第一时间登录服务器执行了iostat -x 1命令,观察到以下关键指标:
code复制Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
vda 0.00 5.00 285.00 12.00 97280.00 768.00 642.56 32.17 108.33 112.50 62.50 3.37 100.00
从输出中可以清晰看到:
第一板斧:iotop定位进程
bash复制sudo iotop -oP -d 5
这个命令每5秒刷新一次,只显示实际产生IO的进程。关键列说明:
第二板斧:lsof确认文件访问
bash复制sudo lsof +D /var/log # 监控特定目录
sudo lsof -p <PID> # 查看指定进程打开的文件
第三板斧:strace动态追踪
bash复制sudo strace -tt -T -f -p <PID> -e trace=file 2>&1 | grep -v ENOENT
参数解析:
-tt:显示微秒级时间戳-T:显示系统调用耗时-e trace=file:只追踪文件操作云监控集成分析
bash复制aliyuncli ecs DescribeDisks --InstanceId i-bp1xxxxxx --output cols=DiskId,DiskName,IOPSRead,IOPSWrite
ESSD性能诊断
bash复制sudo smartctl -a /dev/vda | grep -i wear_leveling
现象特征:
解决方案:
bash复制# 立即清理旧日志
sudo find /var/log -type f -name "*.log" -size +100M -exec truncate -s 0 {} \;
# 长期方案:优化logrotate配置
sudo vim /etc/logrotate.d/nginx
示例配置:
code复制/var/log/nginx/*.log {
daily
rotate 7
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
判断依据:
sql复制SHOW ENGINE INNODB STATUS\G
关注BUFFER POOL AND MEMORY章节中的read/s和read requests/s比值,如果超过0.1说明存在大量物理读。
优化方案:
sql复制-- 临时缓解
SET GLOBAL innodb_buffer_pool_size=4G;
-- 永久优化
ALTER TABLE large_table ADD INDEX idx_important_column(important_column);
诊断命令:
bash复制aliyun ecs DescribeDisks --DiskIds '["d-bp1xxxxxx"]' --query "Disks.Disk[0].BurstingEnabled"
应对策略:
通过iotop发现一个异常的python进程持续占用25%的IO资源:
code复制PID USER DISK READ DISK WRITE SWAPIN IO> COMMAND
1234 www-data 45.62 M/s 0.00 B/s 0.00 % 25.42 % python3 /opt/app/main.py
使用lsof查看该进程打开的文件:
bash复制sudo lsof -p 1234 | grep -i delete
发现大量已删除但未释放的临时文件,这是典型的内存泄漏表现。
通过strace追踪进程行为:
bash复制sudo strace -tt -p 1234 -e trace=open,read 2>&1 | head -20
输出显示该进程在循环读取/proc/self/maps文件,这是某些Python库内存调试的典型特征。
bash复制sudo systemctl restart app-service
python复制# 在代码中添加内存限制
import resource
resource.setrlimit(resource.RLIMIT_AS, (1_000_000_000, 1_000_000_000))
不同性能级别的最大IOPS:
| 类型 | 基础IOPS | 突发IOPS | 每GB增加IOPS |
|---|---|---|---|
| ESSD | 5万 | 无 | 50 |
| ESSD PL1 | 5万 | 30万 | 50 |
| ESSD PL2 | 10万 | 50万 | 100 |
| ESSD PL3 | 100万 | 100万 | 1000 |
经验法则:当业务需要持续超过5万IOPS时,必须选择PL1及以上级别
修改/etc/sysctl.conf:
ini复制# 减少文件系统缓存压力
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
# 优化块设备队列
block.queue_depth = 64
在CloudMonitor中设置智能阈值:
bash复制#!/bin/bash
# 检查IO瓶颈
iowait=$(vmstat 1 2 | tail -1 | awk '{print $16}')
[ $iowait -gt 20 ] && echo "警告:iowait过高 - $iowait%" | mail -s "IO告警" admin@example.com
# 检查磁盘配额
used_iops=$(iostat -d | grep vda | awk '{print $4}')
max_iops=$(aliyun ecs DescribeDisks --DiskIds '["d-bp1xxxxxx"]' --query "Disks.Disk[0].IOPS" --output text)
[ $used_iops -gt $((max_iops * 70 / 100)) ] && echo "IOPS使用超过70%" >> /var/log/io_check.log
使用fio进行压力测试:
ini复制[global]
ioengine=libaio
direct=1
runtime=300
[read4k]
rw=randread
bs=4k
numjobs=4
iodepth=32
关键指标解读: