背景与适用场景
磁盘IO性能是Linux服务器最常见的瓶颈之一。当服务器出现响应延迟增加、CPU的iowait占比居高不下、应用程序吞吐率下降时,磁盘IO往往是主要诱因。在生产环境中,以下场景经常需要关注和优化磁盘IO:
典型高IO场景:
- 数据库服务器(MySQL、PostgreSQL、MongoDB)的数据盘和日志盘
本文目标读者:初中级运维工程师,具备基本的Linux操作经验,熟悉常见的系统管理命令。希望通过本文能够:独立评估磁盘IO性能状态、定位IO瓶颈、掌握常用优化手段、在生产环境安全实施优化措施。
前置知识:
第一章 磁盘IO核心概念
1.1 磁盘IO性能三要素
磁盘IO性能由三个核心指标衡量,它们相互关联但各自描述不同层面的性能特征:
IOPS(Input/Output Operations Per Second)IOPS表示每秒完成的IO操作次数,反映磁盘处理随机读写请求的能力。对于数据库等随机读写密集型 workload,IOPS是首要关注的指标。
吞吐量(Throughput)吞吐量表示每秒传输的数据量,通常以MB/s或GB/s为单位。它衡量的是顺序IO场景下的数据传输能力。大文件读写、视频流处理等场景主要关注吞吐量。
延迟(Latency)延迟指单个IO请求从发送到完成的时间。延迟直接影响应用程序的响应时间,特别是对延迟敏感的应用(如数据库事务)。
三者关系:
IOPS × 平均IO大小 = 吞吐量
延迟 × IOPS ≈ 一定程度的资源占用率(当IO并发度不高时)
例如,一个SSD的IOPS为50000,假设每次IO大小为4KB,则吞吐量为50000 × 4KB ≈ 195MB/s。
1.2 存储介质类型与特性
不同存储介质的IO特性差异巨大,理解这些差异是优化IO性能的基础。
机械硬盘(HDD)机械硬盘使用旋转磁盘和机械磁头。随机读写需要磁头寻道,延迟通常在5-15ms。7200RPM硬盘的IOPS通常在100-150之间,10000RPM高端盘可达200-300 IOPS。顺序读取吞吐量在100-200MB/s区间。
机械硬盘的IO延迟主要来自三个方面:旋转延迟(平均约4-5ms,取决于转速)、寻道延迟(平均3-8ms)、数据传输延迟。优化机械硬盘IO的核心思路是减少随机读写、尽量合并IO请求。
固态硬盘(SSD)SSD使用NAND Flash存储介质,无机械部件。随机读写延迟在0.1-0.5ms级别,IOPS可达50000以上,吞吐量在500MB-3GB/s区间(取决于接口和NAND类型)。
SATA SSD受SATA接口限制,实际吞吐量约500-600MB/s。NVMe SSD通过PCIe通道直连主机,吞吐量可达3GB/s以上。SSD的IO特性是:小IO请求延迟低,但大顺序写入可能触发写入放大效应。
NVMe SSD的特殊考虑NVMe SSD支持多队列(最多64000个命令队列),相比SATA SSD的单一命令队列,在高并发场景下性能优势明显。但NVMe SSD对内核版本和驱动有要求,老旧内核可能无法充分发挥NVMe性能。
存储类型选择建议:
数据库业务(随机IO为主) → SSD或NVMe SSD,高IOPS优先
文件存储(顺序IO为主) → 大容量HDD或SATA SSD,吞吐量优先
虚拟化/容器存储 → 至少SSD,建议NVMe SSD
日志收集(顺序写为主) → 大容量HDD或SATA SSD
1.3 文件系统对IO的影响
文件系统是应用程序与存储设备之间的抽象层,不同文件系统在IO行为上有显著差异。
EXT4EXT4是RHEL/CentOS/Ubuntu等主流发行版的默认文件系统。EXT4的IO特性:
- 支持延迟分配(delayed allocation),减少碎片但增加数据丢失风险
- 日志模式:writeback、ordered、journal三种模式,默认ordered
XFSXFS是RHEL 7/8/9默认文件系统,适合大文件和高IOPS场景。XFS的IO特性:
BtrfsBtrfs是下一代文件系统,支持写时复制、快照、压缩等高级特性。但生产环境使用需谨慎,成熟度不如EXT4和XFS。
tmpfs/ramfs内存文件系统,极低延迟,适合临时文件和缓存。但数据易失,需确保有足够内存且能接受数据丢失风险。
文件系统挂载选项对IO的影响:
# noatime:关闭访问时间更新,减少元数据写
# nodiratime:关闭目录访问时间更新
# barrier=1:启用磁盘写屏障,保证文件系统一致性(默认开启)
# nobarrier:关闭写屏障,提升写性能但有数据风险
# data=writeback:EXT4的writeback日志模式,写性能最好但最不安全
# data=ordered:折中模式,默认
# commit=60:延迟刷新元数据,默认5秒
# 高性能场景推荐挂载参数(注意数据安全)
mount -o noatime,nodiratime,nobarrier,data=writeback /dev/sdb1 /data
# 标准安全场景推荐
mount -o noatime,nodiratime /dev/sdb1 /data
文件系统IO特性对比:
1.4 IO调度算法
Linux内核的IO调度器决定IO请求的发送顺序和合并策略。不同调度算法适合不同的IO模式。
调度算法类型:
CFQ(Completely Fair Queuing)基于进程公平队列的调度算法,力图给每个进程分配相等的IO带宽。在SSD上反而可能降低性能,因为CFQ的队列排序开销在快速设备上得不偿失。RHEL 6默认使用CFQ。
Deadline基于截止时间的调度算法,优先处理即将超时的请求,减少IO延迟抖动。对数据库等延迟敏感型应用友好。RHEL 6可选。
NOOP最简单的FIFO队列,只做相邻请求合并。适合SSD和存储阵列,因为这些设备本身有强大的调度能力,内核调度是冗余的。
MQ-DEADLINE和KYBER多队列版本的Deadline和KYBER调度器,专为NVMe SSD和高速存储设计,充分利用多核CPU和多队列硬件。RHEL 8/9默认使用。
BFQ(Budget Fair Queuing)基于权重的调度算法,适合桌面和交互式场景,可以保证低延迟。服务器场景一般不推荐。
查看和修改调度算法:
# 查看当前磁盘使用的调度算法
cat /sys/block/sda/queue/scheduler
# 输出示例:none [mq-deadline] kyber bfq 或 [deadline] cfq noop
# 查看具体一块磁盘的调度算法
cat /sys/block/sda/sda1/queue/scheduler # 如果sda1是独立分区
# 临时修改调度算法(重启失效)
echo mq-deadline > /sys/block/sda/queue/scheduler
# 永久修改调度算法 - 通过udev规则
cat > /etc/udev/rules.d/60-io-scheduler.rules << 'EOF'
# 为SSD设置noop调度器
ACTION=="add|change", SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="noop"
# 为HDD设置deadline调度器
ACTION=="add|change", SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="deadline"
EOF
# 重新加载udev规则
udevadm control --reload-rules
调度算法选择建议:
SSD/NVMe存储 → noop或mq-deadline,减少调度开销
机械硬盘 → deadline,减少饥饿和延迟
数据库随机IO → deadline,减少IO延迟
大文件顺序读写 → mq-deadline或noop
虚拟化/容器存储 → mq-deadline
第二章 磁盘IO性能评估工具
2.1 iostat - IO统计信息
iostat是分析磁盘IO最常用的工具,属于sysstat包。
安装:
# RHEL/CentOS
yum install sysstat -y
# Debian/Ubuntu
apt install sysstat -y
基本用法:
# 每秒刷新一次,显示CPU统计和设备IO
iostat 1
# 每2秒刷新一次,共显示5次
iostat 2 5
# 只显示设备IO统计,不显示CPU
iostat -d
# 显示指定设备
iostat -d sda sdb
# 显示扩展统计信息(包含更多指标)
iostat -x
# 显示KB为单位,配合-x更易读
iostat -xk
# 显示CPU和所有设备的扩展统计,每秒一次
iostat -xcz 1
典型输出解读:
iostat -xcz 1
Linux 5.4.0-164-generic (db-server) 05/11/2026 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.34 0.00 5.67 8.45 0.00 73.54
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 2.33 1.23 45.67 15.32 1823.45 39.45 1.23 26.12 12.34 26.89 5.23 24.56
核心指标详解:
- rrqm/s, wrqm/s:每秒合并的读/写请求数。合并说明调度器将多个相邻的小IO合并成一个大IO,减少请求次数。
- r/s, w/s:每秒发送到设备的读/写请求数(不合并后)。
- rkB/s, wkB/s:每秒读取/写入的KB数。
- avgrq-sz:平均每个IO请求的大小(扇区数),可通过avgrq-sz×512字节换算。值越大说明IO越顺序,越小说明IO越随机。
- avgqu-sz:平均队列深度,即设备驱动层的平均等待请求数。值越大说明IO压力越高。
- await:平均IO响应时间(毫秒),包括在队列等待时间和实际服务时间。这是最直观的IO性能指标。
- r_await, w_await:分别针对读和写的平均响应时间。
- svctm:平均服务时间(毫秒),设备处理IO的实际时间。注意:svctm在iostat -x中已标记为废弃,不应作为主要参考。
- %util:设备利用率,即设备忙碌时间占比。接近100%时说明设备成为瓶颈。
性能问题判断标准:
# 判断设备是否饱和
# 当 %util > 80-90% 且 await 很高时,说明设备能力达到上限
# 判断是否存在IO延迟问题
# 当 await > 100ms(HDD)或 > 10ms(SSD)时,存在明显延迟
# 判断IO是否均衡
# 当 avgqu-sz 长期 > 4(单盘)或队列忽高忽低,说明IO模式有问题
# 判断IO大小是否合理
# 当 avgrq-sz 很小(如 < 16 sectors = 8KB),说明存在大量小IO
配合时间分析IO变化:
# 记录IO基线(正常时)
iostat -xcz 1 > /tmp/iostat_baseline.log &
# 问题发生时对比
# 正常时: await约20ms,%util约30%
# 问题时: await超过100ms,%util超过80%
# 对比发现设备成为瓶颈
2.2 iotop - 按进程查看IO使用
iotop可以显示每个进程的IO使用情况,是定位IO占用元凶的利器。
安装:
# RHEL/CentOS
yum install iotop -y
# Debian/Ubuntu
apt install iotop -y
基本用法:
# 交互式显示,按IO排序
iotop
# 非交互模式,显示一次
iotop -b
# 非交互,每秒刷新一次,共10次
iotop -b -n 10 -d 1
# 只显示有IO活动的进程
iotop -b -o
# 显示特定用户的进程
iotop -b -u mysql
# 显示累计IO,不显示速率
iotop -b -a
典型输出解读:
Total DISK READ: 1.23 M/s | Total DISK WRITE: 15.67 GB/s
Current DISK READ: 0.00 B/s | Current DISK WRITE: 1.23 M/s
TID PRIO USER DISK READ DISK WRITE SWAPIN IO COMMAND
12345 be/4 mysql 15.32 K/s 0.00 B/s 0.00 % 0.23 % mysqld
12346 be/4 mysql 0.00 B/s 823.45 K/s 0.00 % 1.56 % mysqld
67890 be/3 root 0.00 B/s 12.34 K/s 0.00 % 0.01 % rsync
关键列说明:
实用技巧:
# 定位哪个进程导致高IO
iotop -b -n 1 -o | head -20
# 实时观察MySQL的IO行为
watch -n 1 'iotop -b -n 1 -u mysql'
# 查找大量小文件写的进程
iotop -b | awk '$4 ~ /^[0-9.]+[KMG]?$/ && $5 == "K/s" && $5+0 > 100 {print}'
2.3 pidstat - 按进程查看IO统计
pidstat是sysstat的一部分,可以按进程ID统计IO使用情况,比iotop更适合脚本化和长期监控。
基本用法:
# 显示每个进程的IO统计,每秒刷新
pidstat -d 1
# 只显示有IO活动的进程
pidstat -d 1 | grep -v "^[0-9]" | awk '$8 > 0 {print}'
# 查看特定进程的IO
pidstat -d -p 12345 1 10
# 显示扩展IO统计
pidstat -dx 1
# 输出格式:UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay COMMAND
# kB_rd/s: 每秒读取KB数
# kB_wr/s: 每秒写入KB数
# kB_ccwr/s: 每秒取消写入KB数(被进程取消的脏页)
# iodelay: IO等待的时钟周期数
定位高IO进程脚本:
#!/bin/bash
# 高IO进程检测脚本
echo"=== 高IO进程检测 ==="
echo"时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo""
# 检测IO写入
echo"--- 磁盘写入TOP10 ---"
pidstat -d 1 5 | grep -v "^$" | awk '
NR>4 && $7 > 0 {
split($7, a, "/");
kb_wr = a[1]+0;
if (kb_wr > 0) {
print kb_wr " KB/s", $8, $9
}
}' | sort -rn | head -10
echo""
echo"--- 磁盘读取TOP10 ---"
pidstat -d 1 5 | grep -v "^$" | awk '
NR>4 && $6 > 0 {
split($6, a, "/");
kb_rd = a[1]+0;
if (kb_rd > 0) {
print kb_rd " KB/s", $8, $9
}
}' | sort -rn | head -10
2.4 top/atop - 查看IO等待
top命令的CPU部分包含iowait指标,可以快速判断是否存在IO瓶颈。
top查看iowait:
top -b -n 1
# 输出中%Cpu(s)行:
# 12.3 us, 5.6 sy, 0.0 ni, 73.5 id, 8.4 wa, 0.0 hi, 0.2 si, 0.0 st
# 这里的 8.4 wa 就是iowait,表示CPU等待IO完成的时间百分比
iowait的含义:
- iowait高 ≠ 一定是磁盘IO问题,也可能是网络IO等待(但网络通常不计入iowait)
- iowait高 + CPU idle低:说明CPU在等待IO,此时CPU不是瓶颈,IO是瓶颈
- iowait高 + CPU idle高:说明CPU空闲但有IO在后台运行,这是正常的
- iowait低 + CPU idle低:说明CPU在处理计算,瓶颈在CPU
atop更详细的视图:
# 安装atop
yum install atop -y # 或 apt install atop
# 启动atop(默认每60秒采样一次)
atop
# 查看历史采样
atop -r /var/log/atop/atop_20230511
# atop界面的快捷键:
# g:总体视图
# d:磁盘视图
# m:内存视图
# c:CPU进程视图
# 按时间轴前进/后退:t / T
atop磁盘视图显示内容:
- DISK列:每个磁盘的 busy%、read KB/s、write KB/s
2.5 fio - 基准测试工具
fio是业界标准的存储基准测试工具,可以模拟各种IO模式,准确评估存储性能。
安装:
# RHEL/CentOS
yum install fio -y
# Debian/Ubuntu
apt install fio -y
常用测试场景:
# 1. 随机读测试(数据库读场景)
fio --name=randread --ioengine=libaio --iodepth=4 --rw=randread \
--bs=4k --size=1G --numjobs=4 --runtime=60 --time_based=1 \
--readonly --filename=/dev/sdb1
# 2. 随机写测试(数据库写场景)
fio --name=randwrite --ioengine=libaio --iodepth=4 --rw=randwrite \
--bs=4k --size=1G --numjobs=4 --runtime=60 --time_based=1 \
--filename=/dev/sdb1
# 3. 顺序读测试(大文件读取)
fio --name=seqread --ioengine=libaio --iodepth=16 --rw=read \
--bs=128k --size=2G --numjobs=1 --runtime=60 --time_based=1 \
--filename=/dev/sdb1
# 4. 顺序写测试
fio --name=seqwrite --ioengine=libaio --iodepth=16 --rw=write \
--bs=128k --size=2G --numjobs=1 --runtime=60 --time_based=1 \
--filename=/dev/sdb1
# 5. 混合读写测试(70%读30%写,模拟真实数据库)
fio --name=mixrw --ioengine=libaio --iodepth=4 --rw=randrw \
--rwmixread=70 --bs=4k --size=1G --numjobs=4 --runtime=60 \
--time_based=1 --filename=/dev/sdb1
# 6. 完整测试报告(使用预设配置文件)
fio --profile=library --filename=/dev/sdb1
关键参数说明:
# --ioengine: IO引擎
# libaio: Linux原生异步IO,生产环境推荐
# sync: 同步IO,最原始
# posixaio: POSIX异步IO
# libev: 事件驱动IO
# --iodepth: 队列深度,表示同时在途的IO请求数
# 4: 模拟单线程随机IO(如单次数据库事务)
# 16-32: 模拟多线程或数据库整体IO
# 64+: 模拟高并发场景
# --rw: 读写模式
# read: 顺序读
# write: 顺序写
# randread: 随机读
# randwrite: 随机写
# randrw: 混合随机读写
# --bs: 块大小
# 4k: 数据库小IO随机读写
# 128k-1M: 大文件顺序IO
# --numjobs: 并发线程数
# --runtime: 测试持续时间(秒)
# --time_based: 如果文件读写完了,用时间而不是大小来控制测试时长
# --filename: 测试文件/设备
# 使用文件测试: --filename=/data/fio_testfile
# 使用裸设备测试: --filename=/dev/sdb1 (危险,会擦除数据!)
fio输出解读:
randread: (g=0): rw=randread, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
...
Jobs: 4 (f=4): [R(4)][100.0%][r=15.2M/s,w=0K/s][r=3895,w=0] %uenc - ETA的省略
write: IOPS=3895, BW=15.2MiB/s (15.9MB/s)
read: IOPS=3895, BW=15.2MiB/s (15.9MB/s)
read: IOPS=3895, BW=15.2MiB/s (15.9MB/s)
write: IOPS=3895, BW=15.2MiB/s (15.9MB/s)
# 关键指标:
# IOPS: 每秒IO操作数
# BW: 吞吐量(MiB/s或MB/s)
# clat: completion latency,完成延迟
# slat: submission latency,提交延迟
# lat: 总体延迟
性能测试注意事项:
# 测试裸设备会擦除数据!生产环境禁止
# 正确做法:用文件测试
fio --name=test --size=10G --filename=/data/fio_test
# 测试完成后清理测试文件
rm -f /data/fio_test
# 建议在测试前记录基线性能
# 测试时确保系统IO相对空闲
# 多次测试取平均值以减少波动
2.6 hdparm - 读取磁盘硬件信息
hdparm可以查看磁盘硬件信息和做简单测速。
查看磁盘信息:
# 查看磁盘型号和基本参数
hdparm -I /dev/sda
# 读取磁盘缓存大小
hdparm -g /dev/sda
# 测试读取速度(直接从缓存读,不产生实际IO)
hdparm -Tt /dev/sda
# 输出示例:
# /dev/sda:
# Timing cached reads: 5234 MB in 2.00 seconds = 2617.00 MB/sec
# Timing buffered disk reads: 256 MB in 3.01 seconds = 85.05 MB/sec
hdparm注意事项:
- 敏感参数修改(如-dmar、-X等)可能影响数据安全
2.7 其他有用工具
df - 查看文件系统空间和inode使用:
# 查看文件系统使用情况
df -h
# 查看inode使用情况(大量小文件可能耗尽inode)
df -i
# 以inode显示
df -hi
# 查看磁盘挂载点和文件系统类型
df -Th
du - 估算目录空间使用:
# 查看当前目录大小
du -sh .
# 查看各子目录大小
du -sh */
# 显示文件大小排序
du -ah | sort -rh | head -20
# 查看大文件(100MB以上)
find / -type f -size +100M -exec ls -lh {} \;
lsblk - 查看块设备:
# 显示所有块设备及挂载点
lsblk
# 显示完整信息
lsblk -f
# 显示设备大小
lsblk -b
blkid - 查看设备UUID和文件系统类型:
blkid
# 输出:
# /dev/sda1: UUID="xxx" TYPE="xfs"
# /dev/sdb1: UUID="yyy" TYPE="ext4"
第三章 关键性能指标解读
3.1 %util - 设备利用率
%util的含义:设备利用率,表示设备在采样周期内处于忙碌状态的时间百分比。100%意味着设备全力运转,是判断设备是否饱和的直接指标。
判断方法:
# 单次采样不足以判断,持续观察
iostat -xcz 1
# 如果%util持续>80-90%,且其他指标(await)也明显升高
# 说明设备已经成为瓶颈
# 示例正常输出:
Device: %util
sda 15.23 # 设备大部分时间空闲
# 示例饱和输出:
Device: %util
sda 98.45 # 设备几乎满负荷
重要注意事项:
- %util>100%是不可能的,看到100%只是采样精度问题
- 即使%util只有30-50%,如果await很高,仍可能存在IO问题(队列瓶颈在驱动层)
- SSD的%util高不一定代表性能差,要结合IOPS和吞吐量看
- 多盘RAID环境下,单盘%util高可能不影响整体性能
3.2 await - 平均IO响应时间
await的含义:平均IO响应时间,从请求发送到完成的时间(毫秒),包括排队时间和实际服务时间。这是应用层面最关心的IO指标。
不同存储类型的await基线:
# 机械硬盘(HDD)基线:
# 正常负载: 5-15ms
# 轻度繁忙: 15-30ms
# 严重繁忙: >50ms(需要优化或扩容)
# SATA SSD基线:
# 正常负载: 0.1-1ms
# 轻度繁忙: 1-5ms
# 严重繁忙: >10ms
# NVMe SSD基线:
# 正常负载: 0.02-0.2ms
# 轻度繁忙: 0.2-1ms
# 严重繁忙: >2ms
# 网络存储(NFS/CIFS):
# 正常负载: 1-10ms
# 轻度繁忙: 10-30ms
# 严重繁忙: >50ms
await vs svctm:
- 当设备繁忙时,await>svctm,差值就是排队时间
- 不要单独看svctm判断性能,要看await的组成
3.3 avgqu-sz - 平均队列深度
avgqu-sz的含义:设备驱动的平均等待队列长度。在请求达到设备前,它们在内核的设备队列中等待。
队列深度的意义:
# 队列深度 = 并发在途IO数
# 当应用程序发的IO速度快于设备处理速度时,队列就会堆积
# 判断标准:
# avgqu-sz < 2:轻负载,设备能力强于IO需求
# avgqu-sz 2-4:正常负载
# avgqu-sz 4-8:较高负载,可能出现延迟
# avgqu-sz > 8:严重过载,IO延迟会显著增加
# 示例输出:
Device: avgqu-sz await %util
sda 0.15 8.23 5.23 # 轻负载,正常
sdb 12.45 156.78 98.45 # 严重过载,需要优化
avgqu-sz突然升高的排查:
# 1. 查看是哪个进程导致
iotop -b -n 1 -o
# 2. 查看IO分布
pidstat -d 1
# 3. 查看IO类型分布
iostat -xcz 1
3.4 avgrq-sz - 平均IO大小
avgrq-sz的含义:平均每个IO请求的大小(以扇区为单位,1扇区=512字节)。这个指标反映IO的顺序性程度。
avgrq-sz解读:
# avgrq-sz = 8(约4KB):小IO,随机IO为主
# avgrq-sz = 64(约32KB):中等大小IO
# avgrq-sz = 256(约128KB):大IO,顺序IO为主
# 假设看到:
# avgrq-sz很小(如8-16),说明大量随机小IO
# avgrq-sz很大(如256+),说明IO比较顺序
# 数据库典型场景:
# InnoDB page size = 16KB,avgrq-sz约32-64
# 大量小查询可能导致avgrq-sz很小
如何通过avgrq-sz判断IO模式:
# 通过iostat计算
# 如果 rKB/s=1000, r/s=250,则avgrq-sz = 1000/250/2 = 2(约1KB)
# 说明大量小IO读
# 判断:
# avgrq-sz < 16(约8KB):小IO密集,随机读写
# avgrq-sz 16-64(约8-32KB):混合IO
# avgrq-sz > 64(约32KB+):大IO为主,顺序性强
3.5 读IO vs 写IO
r/s和w/s分别代表什么:
# 读IO(r/s):
# - 来自应用程序的文件读取
# - 数据库数据页读取
# - 程序启动时的二进制读取
# - 页面缓存回读
# 写IO(w/s):
# - 应用程序的数据写入
# - 日志写入
# - 页面缓存回写(脏页刷新)
# - 数据库WAL/redo日志写入
通过读写比例判断业务特征:
# 高读低写场景(读多写少):
# 数据库读 replica、静态内容服务、代码仓库
# 优化方向:增大缓存、优化读路径
# 低读高写场景(写多读少):
# 日志收集、时序数据库写入、消息队列持久化
# 优化方向:优化写路径、批量写入策略
# 读写均衡场景:
# OLTP数据库、文件存储服务
# 需要同时优化读写路径
3.6 常见IO瓶颈识别模式
模式1:设备利用率饱和
# 特征:
# - %util持续>90%
# - await明显升高
# - avgqu-sz增加
# 诊断命令:
iostat -xcz 1
# %util列持续在90%以上
# 原因:
# - 设备能力不足(需要换更快存储)
# - IO模式不匹配(随机IO打满设备能力)
# - 多个IO源竞争同一设备
模式2:队列堆积
# 特征:
# - avgqu-sz很高
# - await很高
# - %util可能不高(因为请求在排队)
# 诊断命令:
iostat -xcz 1
# avgqu-sz持续>8
# 原因:
# - 应用并发IO过多
# - 存储响应变慢(抖动)
# - 驱动层瓶颈
模式3:元数据瓶颈
# 特征:
# - %util不高,但延迟很高
# - 大量小IO(avgrq-sz很小)
# - iowait高
# 诊断命令:
iostat -xcz 1
# avgrq-sz很小(<8,约4KB以下)
# 原因:
# - 文件系统元数据操作频繁
# - 小文件读写
# - 大目录遍历/删除
# 解决方案:
# - 使用noatime减少stat调用
# - 优化目录结构
# - 使用tmpfs缓存
模式4:IO延迟抖动
# 特征:
# - await时高时低
# - %util波动大
# - 应用响应不稳定
# 诊断命令:
# 持续监控观察抖动
whiletrue; do iostat -xcz 1 2>/dev/null | grep -A1 "Device:" | grep sda; sleep 1; done
# 原因:
# - 存储后端不稳定
# - 控制器缓存不足
# - 设备固件问题
# - 多个存储混合使用导致竞争
第四章 常见IO瓶颈排查思路
4.1 排查流程总览
┌─────────────────────────────────────────────────────────────┐
│ IO瓶颈排查流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 发现现象:响应延迟、吞吐量下降、iowait高 │
│ ↓ │
│ 2. 初步判断:确认是IO问题还是CPU/网络问题 │
│ ↓ │
│ 3. 定位瓶颈:哪个磁盘、哪个进程、什么类型的IO │
│ ↓ │
│ 4. 分析根因:硬件限制、配置不当、架构问题 │
│ ↓ │
│ 5. 制定方案:优化配置、调整架构、增加硬件 │
│ ↓ │
│ 6. 实施验证:观察效果,确认问题解决 │
│ ↓ │
│ 7. 长期方案:监控、预警、容量规划 │
└─────────────────────────────────────────────────────────────┘
4.2 现象:响应延迟增加
第一步:确认是IO导致的延迟
# 查看CPU状态,确认是iowait高
top -b -n 1
# 观察wa列(iowait)
# 如果iowait > 20%,说明存在明显IO等待
# 如果idle接近0且iowait高,说明CPU在等IO
# 如果idle高但iowait也高,说明有后台IO但CPU没被占用
第二步:确认延迟来自磁盘IO
# 排除内存问题(swap导致IO)
free -h
# 查看swap used,如果swap used > 0,说明存在内存压力
# 查看swap in/out
vmstat 1
# si/so列显示swap活动,如果有大量swap IO,说明内存不足
# 查看进程IO
pidstat -d 1 5
# 观察是否有进程在大量读写
第三步:确认具体是哪种IO问题
# 查看磁盘使用情况
iostat -xcz 1
# 常见延迟原因判断:
# 1. 如果%util高 + await高 → 设备能力达到上限
# 2. 如果avgqu-sz高 + await高 → 请求堆积
# 3. 如果avgrq-sz很小 → 小IO太多
# 4. 如果w/s远大于r/s → 写IO过多
4.3 现象:iowait持续高
排查步骤:
# 步骤1:确认iowait来源
top -b -n 1
# 如果%Cpu的wa列持续>15%,确认是IO问题
# 步骤2:定位是哪个磁盘
iostat -xcz 1
# 查看各磁盘的%util、await
# 步骤3:定位是哪个进程
iotop -b -n 1 -o
# 找出IO占用最高的进程
# 步骤4:分析IO类型
pidstat -d -p <PID> 1
# 观察读写比例和IO大小
常见iowait高原因:
# 原因1:大量小文件读写
# 现象:avgrq-sz很小(<8,约4KB),w/s或r/s很高
# 解决:优化应用IO模式、使用缓存、合并小文件
# 原因2:大文件顺序读写
# 现象:avgrq-sz很大(>128),吞吐量达到上限
# 解决:确认设备能力是否满足,考虑升级存储
# 原因3:swap频繁换入换出
# 现象:si/so列有大量活动
# 解决:增加内存、优化应用内存使用、减少swap使用
# 原因4:日志大量写入
# 现象:w/s很高
# 解决:优化日志策略、异步日志、分散日志写入
# 原因5:数据库WAL写入
# 现象:w/s高且IO大小固定(如16KB)
# 解决:优化WAL配置、使用电池备份写缓存
4.4 现象:吞吐量下降
排查步骤:
# 步骤1:确认吞吐量确实下降
# 记录当前基线
iostat -xcz 1
# 对比正常状态的rkB/s和wkB/s
# 步骤2:检查是否有硬件/驱动问题
dmesg | grep -i "error\|fail\|timeout"
# 查看系统日志是否有存储错误
# 步骤3:检查RAID/存储控制器状态
# 如果是RAID卡:
MegaCli -PDList -aAll | grep -E "Firmware state|Segments"
# 查看磁盘状态是否正常
# 步骤4:检查是否有坏道/故障块
smartctl -a /dev/sda
# 检查SMART信息
4.5 排查案例:MySQL数据库IO高
完整排查流程:
# 1. 发现问题:数据库响应慢
# 应用层报错:SQL执行时间超过阈值
# 2. 初步判断:查看CPU和IO状态
top -b -n 1
# 发现%Cpu的wa列达到45%,iowait非常高
# 3. 定位瓶颈磁盘
iostat -xcz 1
# 输出显示sdb(数据盘)的%util达到95%,await=120ms
# sda(系统盘)正常
# 4. 定位元凶进程
iotop -b -n 5 | grep -v "Total" | sort -k10 -rn | head -10
# 发现mysqld进程占用了90%以上的IO
# 5. 分析MySQL IO特征
pidstat -d -p $(pgrep -x mysqld) 1 10
# 显示写IO为主,每秒写入量约50MB
# 6. 检查MySQL相关指标
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A5 "LOG"
# 查看redo log写入情况
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log%';"
# 查看日志写IO
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_page_size';"
# 确认page size
# 7. 检查慢查询
mysql -e "SHOW FULL PROCESSLIST;"
# 发现大量慢查询在等待IO
mysql -e "SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;"
# 查看慢查询日志
# 8. 根因分析
# - 存在大量随机小查询,导致频繁数据页读取
# - 合并写导致日志写入突增
# - innodb_flush_log_at_trx_commit=1 导致每次事务都刷日志
# 9. 优化方案(见后续章节)
4.6 排查脚本模板
IO问题快速诊断脚本:
#!/bin/bash
# io_diagnose.sh - IO问题快速诊断脚本
# 用途:快速收集IO问题诊断信息
OUTPUT_DIR="/tmp/io_diagnose_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUTPUT_DIR"
echo"=== IO问题诊断 ==="
echo"输出目录: $OUTPUT_DIR"
echo"开始时间: $(date)"
# 1. 系统基本信息
echo"--- 系统信息 ---" > "$OUTPUT_DIR/01_system_info.txt"
uname -a >> "$OUTPUT_DIR/01_system_info.txt"
cat /etc/redhat-release >> "$OUTPUT_DIR/01_system_info.txt" 2>/dev/null
cat /etc/os-release >> "$OUTPUT_DIR/01_system_info.txt" 2>/dev/null
# 2. 磁盘分区和挂载
echo"--- 磁盘分区 ---" > "$OUTPUT_DIR/02_disks.txt"
lsblk >> "$OUTPUT_DIR/02_disks.txt"
echo"" >> "$OUTPUT_DIR/02_disks.txt"
df -Th >> "$OUTPUT_DIR/02_disks.txt"
# 3. 存储设备信息
echo"--- 存储设备信息 ---" >> "$OUTPUT_DIR/02_disks.txt"
for disk in $(lsblk -ndo NAME); do
if [ -e "/sys/block/$disk/queue/scheduler" ]; then
echo"=== $disk ===" >> "$OUTPUT_DIR/02_disks.txt"
cat "/sys/block/$disk/queue/scheduler" >> "$OUTPUT_DIR/02_disks.txt"
cat "/sys/block/$disk/queue/read_ahead_kb" >> "$OUTPUT_DIR/02_disks.txt"
echo"" >> "$OUTPUT_DIR/02_disks.txt"
fi
done
# 4. CPU和内存状态
echo"--- CPU状态 ---" > "$OUTPUT_DIR/03_cpu_mem.txt"
top -b -n 1 >> "$OUTPUT_DIR/03_cpu_mem.txt"
echo"" >> "$OUTPUT_DIR/03_cpu_mem.txt"
echo"--- 内存状态 ---" >> "$OUTPUT_DIR/03_cpu_mem.txt"
free -h >> "$OUTPUT_DIR/03_cpu_mem.txt"
# 5. IO统计(采样30秒)
echo"--- IO统计(30秒采样)---" > "$OUTPUT_DIR/04_io_stats.txt"
iostat -xcz 1 30 >> "$OUTPUT_DIR/04_io_stats.txt"
# 6. 高IO进程(采样30秒)
echo"--- 高IO进程(30秒采样)---" > "$OUTPUT_DIR/05_top_io_processes.txt"
pidstat -d 1 30 | awk '$8 > 0 || $9 > 0' >> "$OUTPUT_DIR/05_top_io_processes.txt"
# 7. dmesg存储错误
echo"--- 内核存储错误 ---" > "$OUTPUT_DIR/06_dmesg_errors.txt"
dmesg | grep -i "error\|fail\|timeout\|玉" >> "$OUTPUT_DIR/06_dmesg_errors.txt" || echo"未发现存储相关错误"
# 8. SMART信息(如有)
echo"--- SMART信息 ---" > "$OUTPUT_DIR/07_smart.txt"
for disk in $(lsblk -ndo NAME | grep -E '^sd[a-z]$'); do
ifwhich smartctl >/dev/null 2>&1; then
echo"=== $disk ===" >> "$OUTPUT_DIR/07_smart.txt"
smartctl -H -i /dev/$disk >> "$OUTPUT_DIR/07_smart.txt" 2>&1
echo"" >> "$OUTPUT_DIR/07_smart.txt"
fi
done
echo"诊断完成,输出在: $OUTPUT_DIR"
echo"建议查看顺序:"
echo"1. 03_cpu_mem.txt - 确认iowait"
echo"2. 04_io_stats.txt - 确认哪个磁盘%util高"
echo"3. 05_top_io_processes.txt - 确认哪个进程IO高"
echo"4. 06_dmesg_errors.txt - 检查硬件错误"
第五章 IO性能优化实战
5.1 文件系统层面优化
选择合适的文件系统
根据业务场景选择文件系统:
# 高性能数据库场景
# 推荐XFS,适合大并发随机IO,元数据操作高效
mkfs.xfs -f /dev/sdb1
# 大文件存储场景
# XFS或EXT4都可以,XFS在大文件方面略有优势
mkfs.ext4 -F /dev/sdb1
# 需要高级特性(快照、压缩)
# 考虑Btrfs(但生产环境慎用)
mkfs.btrfs /dev/sdb1
合理的挂载选项
# 高性能场景(数据可丢失或已有备份)
# 关闭访问时间更新、关闭barrier、使用writeback模式
mount -o noatime,nodiratime,nobarrier,data=writeback /dev/sdb1 /data
# 标准安全场景
mount -o noatime,nodiratime /dev/sdb1 /data
# 查看当前挂载选项
mount | grep /data
# 或
cat /proc/mounts | grep /data
挂载选项详解:
文件系统检查和修复
# 检查文件系统(必须先卸载)
umount /data
fsck.ext4 -f /dev/sdb1
# XFS检查(XFS通常不需要定期检查)
xfs_repair /dev/sdb1
# 查看文件系统详细信息
dumpe2fs -h /dev/sdb1 # EXT4
xfs_info /dev/sdb1 # XFS
文件描述符和inode优化
# 查看当前限制
ulimit -n
# 临时修改(当前会话)
ulimit -n 65535
# 永久修改 - /etc/security/limits.conf
cat >> /etc/security/limits.conf << 'EOF'
* soft nofile 65535
* hard nofile 65535
* soft nproc 65535
* hard nproc 65535
EOF
# 查看inode使用情况
df -i
# 如果inode快耗尽,需要重新格式化或使用更多分区
5.2 内核参数优化
脏页回写参数
# 查看当前脏页配置
cat /proc/sys/vm/dirty_ratio # 触发强制回写的内存比例
cat /proc/sys/vm/dirty_background_ratio # 后台回写的内存比例
cat /proc/sys/vm/dirty_expire_centisecs # 脏页过期时间(1/100秒)
cat /proc/sys/vm/dirty_writeback_centisecs # 回写间隔
# 默认值通常是:
# dirty_ratio = 20
# dirty_background_ratio = 10
# dirty_expire_centisecs = 3000(30秒)
# dirty_writeback_centisecs = 500(5秒)
脏页参数调整建议:
# 高IO压力场景(如数据库)
# 增大dirty_ratio,允许更多脏页积累再回写
# 减少回写频率,但增加数据丢失风险
cat >> /etc/sysctl.conf << 'EOF'
# 脏页参数优化
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
vm.dirty_expire_centisecs = 3000
vm.dirty_writeback_centisecs = 500
# 其他IO相关参数
vm.swappiness = 10 # 降低swap倾向
vm.zone_reclaim_mode = 0 # 禁用zone reclaim,减少本地分配失败
EOF
# 应用配置
sysctl -p
IO调度参数
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# 为SSD设置noop调度器(通过udev规则永久生效)
cat > /etc/udev/rules.d/60-io-scheduler.rules << 'EOF'
# SSD使用noop调度器
ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="noop"
# HDD使用deadline调度器
ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="deadline"
EOF
# 重新加载规则
udevadm control --reload-rules
# 验证生效
cat /sys/block/sda/queue/scheduler
预读参数
# 查看当前预读值(单位KB)
cat /sys/block/sda/queue/read_ahead_kb
# 临时修改
echo 4096 > /sys/block/sda/queue/read_ahead_kb
# 永久修改 - /etc/rc.d/rc.local
cat >> /etc/rc.d/rc.local << 'EOF'
# 设置预读值
blockdev --setra 4096 /dev/sda
blockdev --setra 4096 /dev/sdb
EOF
chmod +x /etc/rc.d/rc.local
# 预读值建议:
# 数据库随机读场景:128-512KB(减小,因为随机读不需要大预读)
# 大文件顺序读:1-4MB(增大)
# 混合场景:512KB-1MB
IO队列深度
# 查看队列深度
cat /sys/block/sda/queue/nr_requests
# 默认值128,对于高性能存储可以增大
# 注意:过大的队列可能导致延迟增加
# 临时修改
echo 256 > /sys/block/sda/queue/nr_requests
# 永久修改 - /etc/sysctl.conf
cat >> /etc/sysctl.conf << 'EOF'
# IO队列深度
kernel.sched_nr_migrate = 32
fs.nr_open = 65535
EOF
# 注意:nr_requests需要在启动脚本中设置,因为它是每设备的
5.3 应用程序层面优化
应用层IO模式优化原则
数据库IO优化(MySQL InnoDB)
# MySQL配置文件 /etc/my.cnf 或 /etc/mysql/my.cnf
[mysqld]
# InnoDB数据文件和日志文件
innodb_data_file_path = ibdata1:1G;ibdata2:1G:autoextend
innodb_log_file_size = 1G
innodb_log_files_in_group = 3
innodb_flush_log_at_trx_commit = 1 # 1=最安全,2=折中,0=最快
# Buffer Pool配置
innodb_buffer_pool_size = 12G # 通常设置为可用内存的60-80%
innodb_buffer_pool_instances = 4 # 多实例减少锁竞争
# IO相关参数
innodb_flush_method = O_DIRECT # 绕过页面缓存,直接IO
innodb_io_capacity = 2000 # SSD建议2000-10000
innodb_io_capacity_max = 4000 # SSD建议4000-20000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
# 临时表和排序优化
tmp_table_size = 256M
max_heap_table_size = 256M
sort_buffer_size = 4M
join_buffer_size = 4M
# Redo log优化
innodb_log_buffer_size = 64M
# 双一原则解释:
# innodb_flush_log_at_trx_commit=1 + sync_binlog=1
# 每次事务提交都刷新redo log和binlog
# 最安全但IO最多
# 如果可以容忍少量事务丢失,可设为2
Redis IO优化
# Redis配置文件 /etc/redis/redis.conf
# 持久化策略选择
# 如果不需要RDB和AOF:
appendonly no
save ""
# 如果需要AOF持久化:
appendonly yes
appendfilename "appendonly.aof"
# AOF刷盘策略
# everysec:每秒刷盘,最多丢失1秒数据(推荐)
appendfsync everysec
# no:不主动刷盘,由系统决定(最快但丢数据多)
# always:每次写都刷盘(最安全但最慢)
# AOF重写优化
no-appendfsync-on-rewrite yes # 重写期间暂停appendfsync
# 内存策略
maxmemory 8gb
maxmemory-policy allkeys-lru
# 如果用RDB持久化:
save 900 1 # 900秒内至少1个key变化则保存
save 300 10 # 300秒内至少10个key变化则保存
save 60 10000 # 60秒内至少10000个key变化则保存
5.4 存储配置层面优化
RAID级别选择
RAID卡缓存配置
# 查看RAID卡型号
lspci | grep -i raid
# 使用MegaCli查看RAID配置(如果使用LSI卡)
MegaCli -CfgDsply -aALL | grep -E "RAID Level|Size|Cache" | head -20
# 启用RAID卡缓存(需要电池或超级电容)
MegaCli -LDSetProp -CachedBadBBU -Lall -aAll # 启用缓存
MegaCli -LDSetProp -Direct -Lall -aAll # 直通模式(关闭缓存)
# 查看RAID卡缓存设置
MegaCli -LDGetProp -Cache -Lall -aAll
# 注意:
# 如果没有电池保护写缓存,关闭写缓存以防断电丢失数据
# WriteBack模式:性能好但需要电池保护
# WriteThrough模式:性能差但安全
LVM缓存配置
# 使用LVM SSD缓存加速HDD
# 假设:
# /dev/sda1 - SSD,作为缓存设备
# /dev/sdb1 - HDD,作为慢速数据设备
# 1. 创建PV
pvcreate /dev/sda1
pvcreate /dev/sdb1
# 2. 创建VG
vgcreate vg_data /dev/sdb1
vgextend vg_data /dev/sda1
# 3. 创建缓存池
lvcreate --type cache-pool -l 100%FREE -n cache_pool vg_data /dev/sda1
# 4. 将缓存池关联到逻辑卷
lvconvert --cache --cachemode writethrough vg_data/lv_data --cachepool vg_data/cache_pool
# 5. 查看缓存状态
lvs -o lv_name,cache_read_hits,cache_read_misses,cache_mode vg_data
# cachemode选项:
# writethrough:数据同时写入SSD和HDD,安全
# writeback:数据先写入SSD,后台写入HDD,性能更好但有风险
5.5 常见IO问题修复示例
场景1:大量小文件导致IO高
# 问题:大量小文件读写导致%util高但吞吐量低
# 现象:avgrq-sz很小(约4KB),iowait高
# 解决方案1:合并小文件
# 使用cat合并小日志文件
cat /var/log/app/*.log > /var/log/app/merged_$(date +%Y%m%d).log
# 解决方案2:使用tmpfs缓存
# 在内存中创建tmpfs挂载点存储临时文件
mount -t tmpfs -o size=2G tmpfs /tmp/app_cache
# 解决方案3:优化应用IO模式
# 如果是代码问题,引导开发修改:
# - 批量写入而不是单条写入
# - 使用缓冲IO
# - 异步写日志
# 解决方案4:使用更合适的文件系统
# XFS对小文件支持更好
# 或考虑在SSD上运行
场景2:日志写入阻塞
# 问题:应用日志写入导致IO延迟
# 现象:w/s很高,IO延迟增加
# 解决方案1:日志异步化
# 修改应用配置,将日志输出到内存文件系统
mount -t tmpfs -o size=1G tmpfs /var/log/app_async
# 应用写入/var/log/app_async/
# 另一进程定期同步到磁盘
# 解决方案2:使用rsyslog远程日志
# 编辑 /etc/rsyslog.conf
*.* @@remote-log-server:514
# 日志通过网络发送到远程日志服务器
# 解决方案3:日志轮转优化
# /etc/logrotate.conf
daily # 每天轮转
rotate 7 # 保留7天
compress # 压缩旧日志
delaycompress # 延迟压缩(上一次轮转的)
missingok # 忽略丢失文件
notifempty # 空文件不轮转
sharedscripts # 所有日志轮转后只执行一次脚本
postrotate
/bin/kill -HUP $(cat /var/run/rsyslogd.pid 2>/dev/null) 2>/dev/null || true
endscript
# 解决方案4:使用noatime减少stat调用
# 重新挂载
mount -o remount,noatime,nodiratime /var/log
场景3:数据库IO延迟抖动
# 问题:数据库IO延迟时高时低,不稳定
# 现象:await波动大,平均值高
# 诊断:查看是否有IO竞争
iostat -xcz 1 > /tmp/iostat.log &
# 同时观察其他进程
pidstat -d 1 &
# 解决方案1:隔离数据库IO
# 如果有多个磁盘,将数据目录、日志、临时目录分开
# MySQL示例:
[mysqld]
datadir=/data/mysql/data
innodb_log_group_home_dir=/data/mysql/redolog
tmpdir=/tmp/mysql
# 解决方案2:绑定磁盘IO到特定磁盘
# 使用cgroup限制其他进程的IO
# 安装blkio cgroup
yum install libcgroup -y
service cgconfig start
# 创建cgroup
cat > /etc/cgconfig.conf << 'EOF'
group db {
blkio {
blkio.throttle.read_bps_device="8:1 100M";
blkio.throttle.write_bps_device="8:1 100M";
}
}
EOF
# 将MySQL加入cgroup
# 需要在启动脚本中添加
# 解决方案3:使用IO优先级
# ionice命令设置进程IO调度优先级
# -c 1: real time(最高)
# -c 2: best effort(默认)
# -c 3: idle(最低)
# MySQL使用idle优先级,不影响其他进程
ionice -c 3 -p $(pgrep -x mysqld)
# 或使用rt(real time)保证数据库IO优先
ionice -c 1 -n 0 -p $(pgrep -x mysqld)
# 解决方案4:固定IO调度器
# 通过udev规则确保所有SSD使用noop
cat > /etc/udev/rules.d/60-ssd-scheduler.rules << 'EOF'
ACTION=="add|change", SUBSYSTEM=="block", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="noop"
EOF
第六章 监控与预警
6.1 关键监控指标
操作系统层面
# 需要监控的关键指标:
# 1. 磁盘利用率
# - %util > 80% 持续5分钟 → 告警
# - %util > 95% 持续1分钟 → 严重告警
# 2. IO响应时间
# - await > 100ms(HDD)或 > 10ms(SSD) → 告警
# - await > 500ms → 严重告警
# 3. IO队列深度
# - avgqu-sz > 4 持续5分钟 → 告警
# - avgqu-sz > 8 → 严重告警
# 4. IOPS和吞吐量
# - 吞吐量接近设备能力上限 → 告警
# - 吞吐量突然下降50%以上 → 严重告警
# 5. 磁盘使用率
# - 使用率 > 80% → 告警
# - 使用率 > 95% → 严重告警
# 6. inode使用率
# - inode使用率 > 80% → 告警
# - df -i 查看
数据库层面(MySQL)
# 监控SQL:
# 1. Innodb_buffer_pool_wait_free
# - 正常值接近0,如果持续增长说明buffer pool太小
# 2. Innodb_log_waits
# - redo log空间不足时需要等待
# - 值持续增长说明需要增大redo log
# 3. Slow_queries
# - 慢查询数量
# - 超过阈值告警
# 4. Questions / Queries
# - QPS监控
# - 异常下降需要关注
# 监控脚本示例
mysql -e "
SELECT
VARIABLE_NAME,
VARIABLE_VALUE,
@@GLOBAL.${VARIABLE_NAME} AS GLOBAL_VALUE
FROM information_schema.GLOBAL_STATUS
WHERE VARIABLE_NAME IN (
'Innodb_buffer_pool_wait_free',
'Innodb_log_waits',
'Slow_queries',
'Questions',
'Innodb_rows_read',
'Innodb_rows_write'
)
ORDER BY VARIABLE_NAME;
"
6.2 Prometheus + node_exporter 监控方案
安装node_exporter
# 下载node_exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xzf node_exporter-1.6.1.linux-amd64.tar.gz
cd node_exporter-1.6.1.linux-amd64
# 启动node_exporter
./node_exporter &
# 默认监听在9100端口
# 测试指标输出
curl http://localhost:9100/metrics | grep node_disk
关键磁盘IO指标(Prometheus)
# 磁盘利用率
100 * (node_disk_io_time_seconds_total{job="node"} / (interval_avg * node_disk_io_time_weighted_seconds_total{job="node"}))
# 或者使用rate计算
rate(node_disk_io_time_seconds_total[5m]) * 100
# IO延迟
rate(node_disk_read_time_seconds_total[5m]) / rate(node_disk_reads_completed_total[5m])
rate(node_disk_write_time_seconds_total[5m]) / rate(node_disk_writes_completed_total[5m])
# 吞吐量
rate(node_disk_read_bytes_total[5m])
rate(node_disk_written_bytes_total[5m])
# IOPS
rate(node_disk_reads_completed_total[5m])
rate(node_disk_writes_completed_total[5m])
# 队列深度(如果系统支持)
node_disk_io_queue_depth_sum / node_disk_io_queue_depth_count
Grafana仪表盘配置
{
"dashboard": {
"title": "Disk IO监控",
"panels": [
{
"title": "磁盘利用率 %util",
"targets": [
{
"expr": "rate(node_disk_io_time_seconds_total{device=\"sda\"}[5m]) * 100",
"legendFormat": "%util - {{device}}"
}
]
},
{
"title": "IO延迟 (ms)",
"targets": [
{
"expr": "rate(node_disk_read_time_seconds_total{device=\"sda\"}[5m]) / rate(node_disk_reads_completed_total{device=\"sda\"}[5m]) * 1000",
"legendFormat": "read await - {{device}}"
},
{
"expr": "rate(node_disk_write_time_seconds_total{device=\"sda\"}[5m]) / rate(node_disk_writes_completed_total{device=\"sda\"}[5m]) * 1000",
"legendFormat": "write await - {{device}}"
}
]
},
{
"title": "IO吞吐量 (MB/s)",
"targets": [
{
"expr": "rate(node_disk_read_bytes_total{device=\"sda\"}[5m]) / 1024 / 1024",
"legendFormat": "read - {{device}}"
},
{
"expr": "rate(node_disk_written_bytes_total{device=\"sda\"}[5m]) / 1024 / 1024",
"legendFormat": "write - {{device}}"
}
]
}
]
}
}
6.3 告警规则配置
AlertManager告警规则(Prometheus格式)
# /etc/prometheus/rules/disk_io_alerts.yml
groups:
-name:disk_io_alerts
rules:
# 磁盘利用率过高
-alert:DiskIOHighUtilization
expr:rate(node_disk_io_time_seconds_total[5m])*100>80
for:5m
labels:
severity:warning
annotations:
summary:"磁盘IO利用率过高"
description:"磁盘 {{ $labels.device }} 利用率超过80%,当前值: {{ $value }}%"
-alert:DiskIOCriticalUtilization
expr:rate(node_disk_io_time_seconds_total[5m])*100>95
for:1m
labels:
severity:critical
annotations:
summary:"磁盘IO利用率严重"
description:"磁盘 {{ $labels.device }} 利用率超过95%,当前值: {{ $value }}%"
# IO延迟过高
-alert:DiskIOHighLatency
expr:|
(
rate(node_disk_read_time_seconds_total[5m]) /
rate(node_disk_reads_completed_total[5m])
) * 1000 > 50
for:5m
labels:
severity:warning
annotations:
summary:"磁盘IO延迟过高"
description:"磁盘 {{ $labels.device }} 读延迟超过50ms,当前值: {{ $value }}ms"
# 磁盘空间不足
-alert:DiskSpaceLow
expr:(node_filesystem_avail_bytes/node_filesystem_size_bytes)<0.2
for:10m
labels:
severity:warning
annotations:
summary:"磁盘空间不足"
description:"挂载点 {{ $labels.mountpoint }} 剩余空间不足20%,当前: {{ $value | humanizePercentage }}"
# inode空间不足
-alert:DiskInodesLow
expr:(node_filesystem_files_free/node_filesystem_files)<0.1
for:10m
labels:
severity:warning
annotations:
summary:"inode空间不足"
description:"挂载点 {{ $labels.mountpoint }} inode剩余不足10%"
告警阈值调整建议
# 不同存储类型需要不同的阈值:
# 机械硬盘(HDD)
# %util > 70% 持续5分钟 → 告警
# await > 30ms → 告警
# SATA SSD
# %util > 80% 持续5分钟 → 告警
# await > 5ms → 告警
# NVMe SSD
# %util > 90% 持续5分钟 → 告警(NVMe可以更高利用率)
# await > 2ms → 告警
# 阈值需要结合业务基线调整
# 如果正常时%util就是60%,应该相应提高告警阈值
6.4 长期性能趋势分析
建立性能基线
# 正常业务时采集基线数据
mkdir -p /var/log/io_baseline
# 定期采集iostat数据
whiletrue; do
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
iostat -xcz 1 5 >> "/var/log/io_baseline/iostat_$(date +%Y%m%d).log"
sleep 300 # 每5分钟采集一次
done &
# 定期采集sar数据(需要sysstat)
# 确保sar数据收集开启
# RHEL/CentOS: 修改 /etc/default/sysstat 设置 ENABLED="true"
# 重启服务: service sysstat restart
# 查看历史IO数据
sar -d -p 1 5 # 设备IO历史
sar -b 1 5 # 缓冲区和IO传输统计
性能趋势分析
# 使用awk分析历史iostat数据
# 提取每日平均%util
awk '/^Device:/ {device=$2} /sda/ {sum+=$NF; cnt++} END {print "Avg util:", sum/cnt "%"}' /var/log/io_baseline/iostat_*.log
# 分析峰值时间分布
awk '/^Device:/ {device=$2} /sda/ && $NF>80 {print $1, $2, $NF}' /var/log/io_baseline/iostat_*.log
# 生成性能报告
cat > /usr/local/bin/io_report.sh << 'EOF'
#!/bin/bash
# IO性能周报生成脚本
REPORT_DIR="/var/log/io_reports"
mkdir -p "$REPORT_DIR"
DATE=$(date +%Y%m%d)
REPORT="$REPORT_DIR/io_report_${DATE}.txt"
echo"=== IO性能周报 ===" > "$REPORT"
echo"生成时间: $(date)" >> "$REPORT"
echo"" >> "$REPORT"
# 分析本周数据
for day in {1..7}; do
DAY=$(date -d "$day days ago" +%Y%m%d)
FILE="/var/log/io_baseline/iostat_${DAY}.log"
if [ -f "$FILE" ]; then
echo"--- $DAY ---" >> "$REPORT"
echo"平均%util: $(awk '/sda/ {sum+=$NF; cnt++} END {print (cnt?sum/cnt:"N/A")}' $FILE)" >> "$REPORT"
echo"峰值%util: $(awk '/sda/ {if($NF>max) max=$NF} END {print max}' $FILE)" >> "$REPORT"
echo"" >> "$REPORT"
fi
done
echo"报告生成: $REPORT"
EOF
chmod +x /usr/local/bin/io_report.sh
第七章 风险控制与回滚
7.1 生产环境操作风险评估
操作风险分级
# 风险等级1:低风险,可直接执行
# - 只读操作和查询
# - 监控和诊断
# - 配置文件查看
# 示例:iostat查看、pidstat监控、查看日志
# 风险等级2:中风险,建议在低峰期执行
# - 挂载选项调整
# - 内核参数临时调整(sysctl)
# - 服务重启
# 示例:remount、sysctl -w、service restart
# 风险等级3:高风险,需要审批和备份
# - 文件系统格式化
# - 分区调整
# - 内核参数永久修改
# - 存储配置修改
# 示例:mkfs、fdisk、修改sysctl.conf、RAID配置
# 风险等级4:极高风险,需要完整方案
# - 生产数据删除
# - 存储迁移
# - 底层硬件更换
# 示例:rm -rf数据、pvmove、硬盘热插拔
操作前必查清单
# 1. 确认操作对象
# - 设备/分区路径正确
# - 确认没有正在使用的数据
# - 确认有回滚方案
# 2. 数据备份
# - 重要数据全量备份
# - 配置文件备份
# - 数据库完整备份
# 3. 影响评估
# - 操作影响范围
# - 服务中断时间
# - 是否有备用方案
# 4. 通知相关方
# - 提前通知业务方
# - 通知监控告警组(避免误告警)
# - 通知相关团队
# 5. 执行窗口
# - 低峰期执行
# - 预留回滚时间
# - 多人确认
7.2 备份方案
配置文件备份
# 备份重要配置文件
BACKUP_DIR="/var/backup/config_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# MySQL配置
cp -a /etc/my.cnf "$BACKUP_DIR/"
cp -a /etc/mysql/ "$BACKUP_DIR/" 2>/dev/null
# Redis配置
cp -a /etc/redis/ "$BACKUP_DIR/" 2>/dev/null
# 系统内核参数
cp -a /etc/sysctl.conf "$BACKUP_DIR/"
cp -a /etc/security/limits.conf "$BACKUP_DIR/"
# udev规则
cp -a /etc/udev/rules.d/ "$BACKUP_DIR/"
# 挂载信息
cp -a /etc/fstab "$BACKUP_DIR/"
cp -a /etc/mtab "$BACKUP_DIR/"
echo"配置备份: $BACKUP_DIR"
数据备份脚本
#!/bin/bash
# 数据备份脚本示例
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/var/backup/data_${BACKUP_DATE}"
MOUNT_POINT="/data"
REMOTE_BACKUP="rsync://backup-server::data"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 1. 快照备份(如使用LVM)
# 检查VG是否有足够空间创建快照
vg_display | grep "Free PE"
# 创建LVM快照(需要VG有足够空间)
# lvcreate --size=10G --snapshot --name=data_snap vg_data/lv_data
# 2. rsync备份到本地
rsync -avh --progress "$MOUNT_POINT/""$BACKUP_DIR/local/"
# 3. 备份到远程(如需要)
# rsync -avh --progress "$BACKUP_DIR/" "$REMOTE_BACKUP/"
# 4. 清理旧备份(保留最近7天)
find /var/backup -type d -name "data_*" -mtime +7 -exec rm -rf {} \;
echo"备份完成: $BACKUP_DIR"
ls -lh "$BACKUP_DIR"
7.3 回滚方案
内核参数回滚
# 记录修改前的值
sysctl -a | grep -E "^(vm.|fs.|kernel.)" > /var/backup/sysctl_before_$(date +%Y%m%d).conf
# 修改后验证
sysctl -p
# 如果出问题,逐个恢复
# sysctl -w vm.dirty_ratio=20
# sysctl -w vm.dirty_background_ratio=10
# 或者完全恢复
# sysctl -p /var/backup/sysctl_before_20230511.conf
挂载选项回滚
# 查看当前挂载选项
mount | grep /data
# 如果需要重新挂载(回滚到默认选项)
umount /data
mount /dev/sdb1 /data
# 完全卸载和重新挂载
# 1. 确保没有进程在使用
lsof +D /data
# 2. 停止相关服务
# systemctl stop mysqld
# 3. 卸载
umount /data
# 4. 以原选项重新挂载
mount -o defaults,noatime /dev/sdb1 /data
# 5. 验证
mount | grep /data
df -h /data
文件系统修复
# EXT4文件系统修复
# 注意:修复前必须卸载文件系统!
umount /dev/sdb1
e2fsck -f /dev/sdb1
# 常见修复选项:
# -p: 自动修复(无需确认)
# -f: 强制检查(即使文件系统看起来正常)
# -y: 所有问题都自动回答yes
# 如果有坏道,使用-n找出坏道但不修复
e2fsck -n /dev/sdb1
# XFS文件系统修复
# XFS不能使用e2fsck,必须用xfs_repair
umount /dev/sdb1
xfs_repair /dev/sdb1
# xfs_repair选项:
# -L: 强制日志清零(最后手段,可能丢数据)
# -m: 指定内存限制
# -v: 详细输出
服务回滚
# MySQL回滚步骤
# 1. 停止MySQL
systemctl stop mysqld
# 2. 备份当前数据
cp -a /var/lib/mysql /var/backup/mysql_before_rollback
# 3. 恢复配置文件
cp -a /var/backup/my.cnf /etc/my.cnf
# 4. 恢复数据(如需要)
# 如果有全量备份:
# tar -xzf /var/backup/mysql_full_backup.tar.gz -C /var/lib/mysql/
# 5. 启动MySQL
systemctl start mysqld
# 6. 验证
mysql -e "SELECT VERSION();"
mysql -e "SHOW STATUS LIKE 'Uptime';"
7.4 灰度验证流程
配置变更灰度验证
# 1. 先在测试环境验证
# 所有配置变更先在测试环境验证通过
# 2. 单台机器验证
# 选择非核心业务机器先验证
# 应用监控观察5-10分钟
# 3. 扩大范围
# 确认无误后再扩大到更多机器
# 4. 全量生效
# 确认稳定后全部生效
# 验证命令示例:
# 检查服务状态
systemctl status mysqld
mysql -e "SELECT VERSION();"
# 检查连接数
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
# 检查查询性能
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
# 观察应用日志
tail -f /var/log/myapp/error.log
# 观察系统资源
top -b -n 1 | head -20
iostat -xcz 1 5
第八章 实战案例复盘
案例1:MySQL数据库响应延迟
问题背景某电商网站数据库服务器,MySQL 5.7,16核CPU,64GB内存,数据盘为RAID10(4块SSD)。问题现象:网站在促销期间响应缓慢,SQL执行时间从正常100ms增加到3000ms+,监控显示CPU的iowait达到45%。
排查过程
# 步骤1:确认iowait高
top -b -n 1
#%Cpu(s): 12.3 us, 6.5 sy, 0.0 ni, 35.7 id, 45.5 wa, 0.0 hi, 0.0 si, 0.0 st
# iowait 45.5%,确认是IO瓶颈
# 步骤2:定位磁盘
iostat -xcz 1
# Device: %util
# sdb 98.45 # 数据盘%util达到98%,确认是这块盘的问题
# 步骤3:定位进程
iotop -b -n 5 | grep -v "Total" | sort -k10 -rn | head -10
# mysqld占用了95%以上的IO
# 步骤4:分析MySQL IO特征
pidstat -d -p $(pgrep -x mysqld) 1 10
# 写IO为主,每秒写入量约80MB
# 步骤5:检查MySQL状态
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A10 "LOG"
# 发现Log sequence number增长很快,redo log写入频繁
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log%';"
# Innodb_os_log_written值很大,说明redo log写入量大
# 步骤6:检查innodb_flush_log_at_trx_commit
mysql -e "SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';"
# 值为1,每次事务提交都刷日志
# 步骤7:检查slow_query_log
mysql -e "SHOW VARIABLES LIKE 'slow_query_log';"
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
# 发现大量超过2秒的查询
根因分析
innodb_flush_log_at_trx_commit=1 导致每次事务提交都刷日志到磁盘- 促销期间事务量激增,大量redo log写入造成IO瓶颈
- 4块SSD的RAID10理论IOPS足够,但小事务频繁刷日志导致延迟累积
优化方案
# 方案1:调整刷日志策略(需要评估数据安全风险)
# 编辑my.cnf
[mysqld]
innodb_flush_log_at_trx_commit = 2 # 折中方案:每秒刷一次日志
# 注意:此方案最多丢失1秒的事务数据,需要业务确认可接受
# 方案2:增加redo log文件大小
[mysqld]
innodb_log_file_size = 4G # 增大redo log,减少检查点刷新
innodb_log_files_in_group = 4
# 方案3:优化应用事务逻辑
# - 合并小事务,减少事务数量
# - 使用批量插入
# - 非关键操作异步化
# 方案4:增加IO能力
# - 考虑使用电池备份的RAID卡缓存
# - 或升级到更高性能的NVMe SSD
优化后验证
# 1. 修改配置后重启MySQL
systemctl restart mysqld
# 2. 监控IO指标
iostat -xcz 1 # 观察%util是否下降
# 3. 观察SQL响应时间
mysql -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';"
# 确认慢查询数量下降
# 4. 监控redo log写入量
watch -n 1 'mysql -e "SHOW GLOBAL STATUS LIKE \"Innodb_os_log%\"";'
# 5. 业务验证
# 联系业务方确认响应时间改善
经验总结
- 数据库刷日志策略对IO影响巨大,需要在性能和数据安全间权衡
案例2:日志服务器写入阻塞
问题背景日志收集服务器,使用rsyslog收集多台应用服务器日志,写入到本地磁盘。问题现象:日志写入延迟增加,应用服务器开始堆积日志无法发送,监控显示磁盘%util持续在95%以上。
排查过程
# 步骤1:确认磁盘状态
iostat -xcz 1
# Device: %util await avgrq-sz
# sda 98.23 156.78 8.45 # 几乎满负荷
# 步骤2:分析IO类型
pidstat -d 1 10 | head -30
# rsyslogd进程写IO很高
# 步骤3:检查日志量
ls -lh /var/log/ | head -20
# 多个日志文件很大
# 查看当日日志增量
du -sh /var/log/app/*
# 某个应用日志单日增长50GB
# 步骤4:检查日志轮转配置
cat /etc/logrotate.conf
cat /etc/logrotate.d/rsyslog
# 发现问题:
# - 日志轮转没有配置compress,导致日志文件很大
# - 日志保留周期过长,占用大量空间
根因分析
/var/log 在机械硬盘上,大量小IO聚合效率低
优化方案
# 1. 优化日志轮转配置
cat > /etc/logrotate.d/app << 'EOF'
/var/log/app/*.log {
daily # 每天轮转
rotate 7 # 保留7天
compress # 压缩旧日志
delaycompress # 延迟压缩(保留最近一个不压缩)
missingok # 忽略丢失文件
notifempty # 空文件不轮转
create 0644 root root # 轮转后创建新文件权限
sharedscripts # 所有日志轮转后执行一次脚本
postrotate
/bin/kill -HUP $(cat /var/run/rsyslogd.pid 2>/dev/null) || true
endscript
}
EOF
# 2. 将日志目录迁移到SSD(如果可用)
# 检查是否有SSD可用
lsblk
# 如果有未使用的SSD /dev/sdb
# 迁移日志到SSD
mount -t xfs -o noatime,nodiratime /dev/sdb1 /var/log_ssd
rsync -avh /var/log/ /var/log_ssd/
mount --bind /var/log_ssd/app /var/log/app
# 3. rsyslog优化配置
cat >> /etc/rsyslog.conf << 'EOF'
# 启用内存队列,避免磁盘IO阻塞
$imjournalRatelimitInterval 1
$imjournalRatelimitBurst 5000
# 使用异步写入
$ActionFileEnableSync off
# 缓冲模式
$OutChannel diskqFile, /var/spool/rsyslog/diskq.bin, 10M
EOF
# 4. 创建rsyslog目录
mkdir -p /var/spool/rsyslog
chown syslog:syslog /var/spool/rsyslog
# 5. 重启rsyslog
systemctl restart rsyslog
优化后验证
# 1. 观察IO指标
iostat -xcz 1
# %util应该下降到50%以下
# 2. 检查日志轮转是否正常
logrotate -d /etc/logrotate.d/app # 调试模式查看是否正常
# 3. 检查rsyslog队列
ls -lh /var/spool/rsyslog/
# 4. 确认应用日志正常写入
tail -f /var/log/app/application.log
经验总结
案例3:高并发小文件场景
问题背景图片缓存服务器,存储大量小图片文件(约4-8KB/文件),共5000万文件。文件系统EXT4,存储在RAID5(12块4TB HDD)。问题现象:图片读取延迟高,CPU iowait达30%,单目录文件数过多导致ls和rm命令都很慢。
排查过程
# 步骤1:检查inode使用
df -i
# Filesystem IUse IFree IUse% Mounted on
# /dev/sdb1 50M 0 100% /data # inode用尽!
# 步骤2:查看目录结构
time ls /data/images/ | wc -l
# 单个目录有500万文件
# 步骤3:分析IO
iostat -xcz 1
# avgrq-sz约4KB(小文件特征明显)
# 步骤4:检查是否元数据瓶颈
# 小文件+大量文件导致元数据操作成为瓶颈
# 问题总结:
# 1. inode耗尽,无法创建新文件
# 2. 单目录文件数过多,元数据操作效率低
# 3. EXT4在小文件场景元数据开销大
解决方案
# 方案1:重做文件系统,扩大inode数量
# 注意:会丢失所有数据!
# 1. 全量备份(如果有空间)
mkdir -p /mnt/backup
rsync -avh /data/ /mnt/backup/
# 2. 卸载并重新格式化,指定更多inode
umount /data
# -i bytes-per-inode:每多少字节分配一个inode
# 默认是16384字节创建一个inode
# 如果平均文件4KB,可以设为4096
mkfs.ext4 -i 4096 /dev/sdb1
# 3. 重新挂载
mount /dev/sdb1 /data
# 4. 恢复数据
rsync -avh /mnt/backup/ /data/
# 方案2:使用XFS文件系统
umount /data
mkfs.xfs -f /dev/sdb1
mount /dev/sdb1 /data
# XFS优势:
# - inode动态分配,不预先占用空间
# - 大目录遍历性能更好
# - 随机写性能优于EXT4
# 方案3:优化目录结构(湖仓一体思路)
# 将大目录拆分成多级目录
# 创建哈希目录结构
mkdir -p /data/images/{0..9}/{0..9}/{0..9}
# 编写脚本计算目标目录
cat > /usr/local/bin/get_image_path.sh << 'EOF'
#!/bin/bash
# 根据文件ID计算存储路径
# 将文件ID的哈希值分散到多级目录
file_id=$1
hash=$(echo$file_id | md5sum | cut -c1)
dir1=${hash:0:1}
dir2=${hash:1:1}
dir3=${hash:2:1}
echo"${dir1}/${dir2}/${dir3}/${file_id}"
EOF
chmod +x /usr/local/bin/get_image_path.sh
# 方案4:使用对象存储替代本地文件系统
# 考虑使用MinIO、Ceph等分布式存储
# 优点:横向扩展、冗余可靠
# 缺点:需要改代码
优化后验证
# 检查inode使用
df -i
# IFree应该大幅增加
# 测试目录遍历性能
time ls /data/images/0/0/0/ | head -1000
# 测试创建文件性能
time touch /data/images/test_file_$$
# 检查XFS特性
xfs_info /data
# 监控IO改善情况
iostat -xcz 1
# avgrq-sz和await应该改善
经验总结
第九章 总结与检查清单
9.1 IO优化核心要点
理解IO性能三要素
IOPS × 平均IO大小 = 吞吐量
延迟 × 并发度 = 响应时间
选择合适的存储介质
- 顺序IO为主 → 大容量HDD或SSD,吞吐量优先
选择合适的文件系统
优化IO调度器
优化挂载选项
- 高性能场景 → noatime,nodiratime
优化内核参数
应用层优化
9.2 IO优化检查清单
新服务器部署检查
# [ ] 1. 存储规划
# - [ ] 根据IO特征选择存储类型
# - [ ] 合理分区(数据、日志、临时文件分开)
# - [ ] 文件系统选择(XFS或EXT4)
# [ ] 2. 调度器配置
# - [ ] SSD使用noop调度器
# - [ ] HDD使用deadline调度器
# - [ ] 验证配置生效:cat /sys/block/*/queue/scheduler
# [ ] 3. 挂载选项
# - [ ] 生产环境使用noatime
# - [ ] 数据目录合理设置选项
# - [ ] 验证:mount | grep /data
# [ ] 4. 内核参数
# - [ ] 根据业务调整脏页参数
# - [ ] 验证:sysctl -a | grep vm.dirty
# [ ] 5. 监控
# - [ ] 部署监控 exporter
# - [ ] 配置告警阈值
# - [ ] 建立性能基线
日常巡检检查
# [ ] 1. 磁盘使用率
# - [ ] df -h 检查空间使用 < 80%
# - [ ] df -i 检查inode使用
# [ ] 2. IO性能
# - [ ] iostat -xcz 1 检查%util < 80%
# - [ ] 检查await在正常范围
# [ ] 3. 进程IO
# - [ ] pidstat -d 1 检查异常IO进程
# - [ ] iotop 检查高IO进程
# [ ] 4. 系统日志
# - [ ] dmesg | grep -i error 检查硬件错误
# - [ ] 查看系统日志中的IO相关警告
# [ ] 5. 健康状态
# - [ ] smartctl -H 检查磁盘健康
# - [ ] RAID卡状态(如使用)
问题排查检查
# [ ] 1. 确认IO问题
# - [ ] top查看iowait是否高
# - [ ] iostat确认是磁盘问题
# [ ] 2. 定位瓶颈
# - [ ] iostat确认哪块磁盘
# - [ ] pidstat/iotop确认哪个进程
# [ ] 3. 分析根因
# - [ ] 查看IO类型(读/写)
# - [ ] 查看IO大小(avgrq-sz)
# - [ ] 查看队列深度(avgqu-sz)
# [ ] 4. 制定方案
# - [ ] 根据根因选择优化方向
# - [ ] 评估风险和影响
# - [ ] 准备回滚方案
# [ ] 5. 实施验证
# - [ ] 灰度验证
# - [ ] 监控指标改善
# - [ ] 业务验证正常
9.3 常见问题速查
9.4 推荐工具链
监控工具
# 基础监控
iostat # IO统计
pidstat # 进程级IO
iotop # 交互式IO监控
# 高级监控
sar # 历史数据分析
nmon/atop # 综合系统监控
# 监控平台
Prometheus + node_exporter # 指标收集
Grafana # 可视化
AlertManager # 告警
分析工具
# 基准测试
fio # 存储基准测试
bonnie++ # 文件系统测试
# 性能分析
blktrace # 块IO追踪
bpftrace # 高级IO分析
perf # 系统性能分析
故障排查
# 日志查看
dmesg # 内核日志
/var/log/messages # 系统日志
/var/log/kern.log # 内核详细日志
# 工具
lsof # 查看文件句柄
strace # 系统调用追踪
ltrace # 库调用追踪
附录:关键命令速查
iostat 常用命令
# 每秒刷新,显示CPU和设备
iostat 1
# 每2秒刷新一次,共5次
iostat 2 5
# 只显示设备,不显示CPU
iostat -d
# 显示扩展统计
iostat -x
# 显示MB为单位
iostat -m
# 查看特定设备
iostat -d sda sdb
# 查看特定时间段的统计
iostat -x 1 5 | awk '/^Device:/ {p=1} p'
iotop 常用命令
# 交互式显示
iotop
# 非交互式,一次性
iotop -b
# 非交互,每秒一次,共10次
iotop -b -n 10 -d 1
# 只显示有IO的进程
iotop -b -o
# 显示特定用户的IO
iotop -b -u mysql
pidstat 常用命令
# 每秒显示进程IO
pidstat -d 1
# 显示特定进程的IO
pidstat -d -p 12345 1
# 显示所有进程的IO,每秒一次
pidstat -d 1 1
# 显示扩展统计
pidstat -dx 1
fio 常用命令
# 4K随机读测试
fio --name=randread --ioengine=libaio --iodepth=4 --rw=randread --bs=4k --size=1G --runtime=60 --time_based=1 --filename=/dev/sdb1
# 顺序写测试
fio --name=seqwrite --ioengine=libaio --iodepth=16 --rw=write --bs=128k --size=2G --runtime=60 --time_based=1 --filename=/dev/sdb1
# 混合读写测试(70%读)
fio --name=mixrw --ioengine=libaio --iodepth=4 --rw=randrw --rwmixread=70 --bs=4k --size=1G --runtime=60 --time_based=1 --filename=/dev/sdb1
sysctl 常用参数
# 查看所有IO相关参数
sysctl -a | grep -E "^(vm.|fs.)"
# 临时修改dirty_ratio
sysctl -w vm.dirty_ratio=40
# 永久生效
echo"vm.dirty_ratio = 40" >> /etc/sysctl.conf
sysctl -p
# 查看当前值
sysctl vm.dirty_ratio