脚本怎样统计IO读写峰值数据:从原理到实战的完整指南
目录导读
- IO峰值统计的核心价值:为什么需要监控IO峰值而非平均值?
- 底层原理与数据采集:/proc/diskstats 与 iostat 的解读
- 脚本实现方案:Bash、Python、Shell三套脚本详解
- 峰值识别算法:滑动窗口、百分位数与异常检测
- 性能优化与注意事项:避免过度监控拖垮系统
- 常见问题问答(Q&A)
IO峰值统计的核心价值
问:为什么IO峰值比平均值更重要?
答:以MySQL数据库为例,当平均IO写入为50MB/s时,如果峰值突发达到300MB/s,可能直接导致磁盘队列深度超标、查询延迟飙升甚至OOM,统计峰值能帮你:

- 识别硬件瓶颈(如SSD写入寿命问题)
- 优化应用层IO调度(如批量写入改为异步)
- 容量规划(例如需要NVMe还是SATA SSD)
典型场景:
- 数据库备份时的突发写入监控
- 视频转码服务对顺序读/写的冲击
- 云服务器按IOPS计费时的成本分析
底层原理与数据采集
Linux系统中,IO统计的核心来源是 /proc/diskstats,每个磁盘设备的字段含义如下:
major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq
关键字段解释:
rio:读完成次数(累计)wio:写完成次数rsect:读扇区数(1扇区=512字节)wsect:写扇区数ruse:读IO耗时(毫秒累计)wuse:写IO耗时
峰值计算逻辑:
- 间隔
t秒采样两次(如t=1) - 差值计算:
IOPS = (wio2 - wio1) / t - 吞吐量:
Bw = (wsect2 - wsect1) * 512 / t - 持续记录最大值可达峰值
推荐采样间隔:
- 秒级(1秒):捕获瞬间峰值
- 分钟级(60秒):辅助分析趋势
脚本实现方案
Bash脚本(轻量级)
#!/bin/bash
DEVICE="sda"
INTERVAL=1
OUTPUT="io_peak.log"
MAX_READ_IOPS=0
MAX_WRITE_IOPS=0
trap "echo 'Stopped'; exit" SIGINT
while true; do
prev=( $(awk "/$DEVICE/ {print \$12,\$13}" /proc/diskstats) )
# 只读写完成次数(字段12读,13写)
sleep $INTERVAL
curr=( $(awk "/$DEVICE/ {print \$12,\$13}" /proc/diskstats) )
r_iops=$(( (${curr[0]} - ${prev[0]}) / INTERVAL ))
w_iops=$(( (${curr[1]} - ${prev[1]}) / INTERVAL ))
# 更新峰值
[[ $r_iops -gt $MAX_READ_IOPS ]] && MAX_READ_IOPS=$r_iops
[[ $w_iops -gt $MAX_WRITE_IOPS ]] && MAX_WRITE_IOPS=$w_iops
echo "$(date) R_IOPS=$r_iops W_IOPS=$w_iops" >> $OUTPUT
echo "Peak so far: R=$MAX_READ_IOPS W=$MAX_WRITE_IOPS"
done
优点:无需第三方工具,纯Bash
缺点:只能统计IOPS,无法计算吞吐量(需改用rsect/wsect)
Python脚本(功能完整)
#!/usr/bin/env python3
import time, sys
def get_disk_stats(device):
with open('/proc/diskstats', 'r') as f:
for line in f:
cols = line.split()
if cols[2] == device:
return {
'reads': int(cols[3]), # rio
'writes': int(cols[7]), # wio
'read_sectors': int(cols[5]), # rsect
'write_sectors': int(cols[9]) # wsect
}
return None
def monitor(device='sda', interval=1, log_file='io_peak.log'):
peak = {'riops':0, 'wiops':0, 'rbw':0, 'wbw':0}
prev = get_disk_stats(device)
if not prev:
print("Device not found")
sys.exit(1)
try:
while True:
time.sleep(interval)
curr = get_disk_stats(device)
riops = (curr['reads'] - prev['reads']) // interval
wiops = (curr['writes'] - prev['writes']) // interval
rbw = (curr['read_sectors'] - prev['read_sectors']) * 512 // interval
wbw = (curr['write_sectors'] - prev['write_sectors']) * 512 // interval
# 峰值更新
for k, v in [('riops',riops), ('wiops',wiops), ('rbw',rbw), ('wbw',wbw)]:
if v > peak[k]: peak[k] = v
# 记录
log_line = f"{time.strftime('%Y%m%d_%H%M%S')} R_IOPS={riops} W_IOPS={wiops} R_BW={rbw} W_BW={wbw}"
with open(log_file, 'a') as f:
f.write(log_line + '\n')
print(f"\rPeak: {peak}", end=' ')
prev = curr
except KeyboardInterrupt:
print("\nFinal Peak:", peak)
if __name__ == '__main__':
monitor(device='sda')
优点:时间精度高,支持多字段,易于扩展
安装:无需额外pip包
Shell结合iostat(建议生产环境)
#!/bin/bash
iostat -x 1 3600 | awk '
/NVM|sd|vd/ { # 匹配常用磁盘设备
if(NF>=14) {
r_iops=$4; w_iops=$5; r_bw=$6; w_bw=$7;
print strftime("%Y%m%d_%H%M%S"), r_iops, w_iops, r_bw, w_bw;
# 峰值记录
if(r_iops > max_r) max_r = r_iops;
if(w_iops > max_w) max_w = w_iops;
}
}
END { print "Peak: R_IOPS=" max_r " W_IOPS=" max_w }
' > io_peak.log &
注意:需安装sysstat包
峰值识别算法
1 滑动窗口法
每N秒计算窗口内最大值,避免单点噪声:
window = collections.deque(maxlen=60) # 保存60个采样点
while True:
value = get_current_iops()
window.append(value)
peak_in_window = max(window)
2 百分位数法
P99/P95峰值比绝对最大值更有参考意义:
sorted_values = sorted(values) p99_index = int(0.99 * len(sorted_values)) p99_peak = sorted_values[p99_index]
3 异常检测
当IOPS连续3次超过基线+20%时触发告警:
if (( current_iops > baseline * 1.2 && count++ >= 3 )); then
echo "Abnormal IO peak detected"
fi
性能优化与注意事项
1 采样频率与精度
- 1秒间隔:对系统CPU额外消耗约0.5%
- 建议在非CPU敏感机器上运行
- 如果监控全设备,使用
/proc/diskstats单次读取所有磁盘
2 避免死循环导致日志爆炸
# 限制监控时长或添加日志轮转 timeout 7200 ./monitor.sh # 最多监控2小时 logrotate -f /path/to/io_peak.log
3 多磁盘并行监控
threads = []
for dev in ['sda', 'sdb']:
t = threading.Thread(target=monitor, args=(dev,))
threads.append(t)
t.start()
4 输出格式优化
- 使用JSON格式便于接入Prometheus:
echo "{\"timestamp\":\"$(date -Iseconds)\",\"device\":\"sda\",\"r_iops\":$riops,\"w_iops\":$wiops}"
常见问题问答(Q&A)
Q1:为什么脚本统计的峰值与iostat看到的峰值不同?
A:iostat默认使用1秒间隔,但会进行平均化处理,建议直接用脚本采集原始/proc/diskstats差值,避免iostat的过滤机制。
Q2:如何统计NVMe设备的峰值?
A:NVMe设备在/proc/diskstats中显示为nvme0n1,直接匹配即可,对于多命名空间,注意识别nvme0n1p1分区。
Q3:峰值数据如何可视化?
A:可输出到Prometheus的exporter格式,再通过Grafana展示,或者使用gnuplot脚本实时绘制图表。
Q4:防止脚本被误杀有哪些手段?
A:设置nohup + 系统服务:nohup ./monitor.sh &,或编写Systemd服务单元([Service] + Restart=always)。
Q5:统计IO读写峰值时,需要关注哪些指标?
A:重点关注:峰值IOPS(决定磁盘响应能力)、峰值带宽(决定传输能力)、峰值服务时间(决定延迟稳定性),建议同时记录时间戳便于事后回溯。
扩展阅读:
- Linux内核文档:
Documentation/iostats.txt - 基于BPF的IO栈分析工具(
bcc/bpftrace)可实时捕获每次IO的延迟分布
(全文共约1600字)