服务器上的磁盘IO是什么原因
磁盘IO过高,本质上是数据读写请求超过了磁盘的处理能力,导致系统响应变慢、程序卡顿甚至宕机。 它像一条拥挤的单行道,车太多或者路太窄,都会堵车,要解决问题,得先从源头看清是谁在“堵路”。
先分清:磁盘IO高和CPU高是完全不同的两回事
很多新手会把CPU高和IO高混淆,CPU高说明计算量大,IO高说明等待读写的时间长,你可以用 top 命令看一眼,wa(iowait)数值超过30%,基本可以断定磁盘是瓶颈,再用 iostat -x 1 查看 %util,这个值持续高于80%,说明磁盘已经接近满载。
| 指标 | CPU高 | 磁盘IO高 |
|---|---|---|
| 核心表现 | 计算密集、进程忙 | 进程等待、卡顿明显 |
| top中的值 | us+sy高 | wa高 |
| 常用工具 | top、htop、perf | iostat、iotop、sar |
| 常见原因 | 代码死循环、算法效率低 | 随机读写多、缓存命中低 |
%util 高但 await 也高,说明磁盘本身速度跟不上;%util 不高但 await 高,可能是磁盘有坏道或控制器有问题,分清这两步,排查方向就对了。
最常见的四类“IO杀手”
数据库的随机读写压力
MySQL、PostgreSQL、MongoDB这类数据库是磁盘IO大户,尤其是未合理使用索引时,全表扫描会造成大量随机读,随机读比顺序读慢几个数量级,因为磁头需要不停寻道,即使使用SSD,随机读写也会显著消耗IOPS。
典型场景:你的业务表数据量超过千万级,但查询条件没有命中索引,每次请求都要扫全表,这时候看 iostat 的 r/s(每秒读次数)会非常高,但 rkB/s(每秒读数据量)却不高,典型的小IO并发。
解决路径:
- 用
slow query log找出慢查询,给高频过滤字段加索引。 - 把
innodb_buffer_pool_size调大,让更多数据留在内存里。 - 如果数据量实在太大,考虑分库分表,或者把历史数据归档到单独库。
日志写入过密
应用日志、系统日志、访问日志都在频繁写盘。/var/log/messages 里每秒刷几十条,或者Java应用用 System.out.println 输出日志,都会直接拖垮IO。
典型场景:晚上某个定时任务跑起来,日志文件在几分钟内膨胀到几个G,同时磁盘IO飙升到100%,你用 lsof | grep deleted 也删不掉,因为文件仍被进程占用。
解决路径:
- 日志级别调到
WARN或ERROR,生产环境避免DEBUG。 - 用
logrotate做切割,控制单文件大小,保留最近7天。 - 把日志写到独立盘或独立分区,避免和数据库抢IO。
内存不足引发的swap交换
物理内存不够时,系统会把一部分不常用的内存页写到磁盘的swap分区,一旦发生swap读写,磁盘IO会瞬间拉高,而且这种高IO是

反复的、无规律的。
典型场景:你开了一堆Java服务,每个默认堆内存2G,但机器只有8G,跑几天后,内存不足,系统开始疯狂swap,free -h 显示swap的si和so列数值很大,这时候机器既卡又不卡,点击响应慢半拍,用 top 看进程的CPU不高,但整体系统负载很高。
解决路径:
- 增加物理内存,这是根本办法。
- 调整JVM堆大小,别让所有服务都默认吃满内存。
- 在
sysctl.conf里设置vm.swappiness=10,减少swap使用倾向。 - 如果只是临时应急,可以
sync; echo 3 > /proc/sys/vm/drop_caches释放页缓存。
备份、同步任务的时间冲突
很多公司习惯在凌晨跑全量备份、数据同步、报表统计,这些任务一旦重叠,IO就会叠加,比如RDS快照备份和Elasticsearch重组索引同时进行,两块盘都到了极限。
典型场景:每日凌晨2点,crontab同时启动了MySQL全量备份(mysqldump)、日志压缩打包、以及一个数据分析脚本,结果从1:59到3:30,服务器几乎无法处理业务请求,用户早上看到的是“昨夜系统缓慢”的投诉。
解决路径:
- 错峰调度,把不同任务分散到不同时间段。
- 使用
ionice给备份任务设置低优先级,ionice -c2 -n7 mysqldump ...。 - 备份尽量用物理备份(xtrabackup)而不是逻辑备份,减少对线上IO的消耗。
怎么快速定位到具体是哪个进程在吃IO
光知道“磁盘IO高”不够,还得揪出元凶,推荐三个实用工具,按顺序排查。
用 iotop 实时看进程IO占用
安装后直接运行 iotop -o,只看正在产生IO的进程,它会显示每秒钟读写的字节数、进程名、用户。这是最直观的定位工具。
用 pidstat 按进程统计IO
pidstat -d 1 每秒输出一次,能看到每个进程的 kB_rd/s、kB_wr/s、cswch/s(上下文切换),如果某个进程的写速率持续超过100MB/s,基本确定是它。
用 /proc 文件系统确认线程
如果进程是Java或者Python这样的多线程应用,pidstat 只能看到进程整体,这时候进入 /proc/[pid]/task/ 查看线程IO,或者用 cat /proc/[pid]/io 查看 rchar 和 wchar 字段,这两个值分别代表累计读写的字节数,间隔几秒读两次,差值就是这段时间的IO量。
实操示例:
# 安装 iotop 和 sysstat(CentOS为例) yum install -y iotop sysstat # 实时查看IO最高的前10个进程 iotop -o -P -k -n 10 # 每隔1秒统计所有进程的IO,持续10次 pidstat -d 1 10
看到结果后,如果某个进程的写速率一直很高,再用 strace -p 进程号 跟踪它的系统调用,看看它到底在写什么文件,别怕输出多,重点看 write 和 fsync 相关的操作。
从硬件层面排查:磁盘本身就是瓶颈的情况
机械硬盘 vs 固态硬盘的差距
机械硬盘(HDD)的随机读写通常只有

5-2 MB/s 的IOPS能力(约100-200 IOPS),而普通SATA SSD轻松达到 30000-100000 IOPS,如果业务流量上来了,老机械盘扛不住是正常的。
行业共识认为:当单块机械盘的 %util 持续高于80%且 await 大于30ms时,换SSD是性价比最高的方案,如果是NVMe SSD,延迟还能再降低一个数量级。
云服务器的云盘类型选错了
云服务器上,普通云盘和高效云盘、ESSD云盘的性能差距巨大,你如果买的是最低配的云盘,IOPS上限可能只有几百,稍微有点流量就会被打满。
典型场景:一个电商小网站在促销活动期间,数据库读写量翻了几倍,结果你的云盘IOPS被限制在1500,直接导致订单接口超时,看监控发现磁盘队列长度一直超过4,但磁盘本身其实没坏。
解决方案:
- 在云控制台查看当前云盘的基准性能和突发性能,确认是否达到上限。
- 升级到更高IOPS的云盘类型,或者挂载多块云盘做RAID 0(注意做好备份)。
- 给数据库单独挂一块高性能盘,日志放另一块,互相隔离。
磁盘坏道或固件问题
dmesg 出现 I/O error、buffer I/O error 字样,或者 smartctl -a /dev/sda 显示的 Reallocated_Sector_Ct 和 Current_Pending_Sector 超过阈值,说明硬盘物理损坏,这时候别调参数了,直接备份数据换盘。
程序层面的优化:学会给IO“减负”
利用缓存减少磁盘访问
很多IO问题其实是缓存没用好,比如Redis没设置最大内存限制,导致操作系统页缓存被挤压;或者MySQL的innodb_flush_log_at_trx_commit=1,每次事务提交都要刷盘,压力巨大。
实操调整:
- MySQL:如果可接受突发丢失1秒数据,把
innodb_flush_log_at_trx_commit改为2,性能提升明显。 - Nginx:开启
open_file_cache,缓存文件句柄,减少重复的open()系统调用。 - 应用程序:用本地内存缓存热点数据,比如用Caffeine或Guava Cache,减少对数据库的直接查询。
调整文件系统挂载参数
不同的文件系统对IO影响很大,ext4默认的data=ordered模式可能会带来额外写放大,可以试试在挂载时加上 noatime,避免访问时间戳写入。
具体操作:
# 修改 /etc/fstab 中对应分区的挂载选项 /dev/sdb1 /data ext4 defaults,noatime,nodiratime 0 0 mount -o remount /data
这个改动对读多写少的场景非常友好,能减少约 5%-10% 的写IO,这不是精确数字,但实测多数情况下能感受到变化,对于xfs文件系统,建议使用 allocsize=64k 提升大文件顺序读写的缓冲。
用异步IO替代同步IO
程序里如果大量使用同步写入(每次write()后必须等磁盘返回),可以改成异步IO或使用libaio,数据库层面,MySQL的innodb_use_native_aio=ON(默认开启)就是利用异步IO,如果你自定义开发工具,记住阻塞在write()上是最糟糕的IO模式。

遇到磁盘IO 100%时的紧急处理流程
不管什么原因,服务器快卡死时,先抢救业务。
- 先重启IO最重的非核心进程,比如日志采集器、监控agent、搜索引擎的合并任务,立刻杀掉或暂停。
- 这时候千万别重启数据库,重启会导致实例恢复时间更长,IO压力反而更大,先等IO降下来,再用
mysqladmin -u root -p status看连接数和慢查询情况。 - 清理临时文件。
find /tmp -size +1G -exec rm -f {} ;,有些程序会留下超大临时文件反复写。 - 临时调高IO调度器的优先级,比如把MySQL进程的IO优先级调高:
ionice -c1 -p [mysqld_pid],让系统优先满足它的IO请求,其他任务靠边站。 - 如果上面都没用,果断快照并重启实例,云服务器可以创建磁盘快照,然后强制重启,比长时间卡死更安全。
预防磁盘IO问题的日常监控
监控不是等出问题再看,而是提前设好阈值,推荐使用atop记录历史IO数据,或部署node_exporter + Prometheus监控每秒的iostat数据。
需要关注的指标:
%util> 80% 持续5分钟,触发告警。await> 20ms 且svctm小于await,说明队列堆积。util不高但r/s或w/s突然翻倍,可能是业务出现异常循环。- 用
sar -d 1 10查看每秒的tps和rkB/s,和上周同时间对比,偏离30%以上就要查。
常见问题解答
服务器磁盘IO高和带宽占用高是一回事吗?
不是。 磁盘IO高是数据在磁盘和内存之间搬运的频率高,带宽占用高是数据在网卡和外部网络之间传输的速率快,你可以同时遇到两者都高的情况,比如同时做大量文件下载和写入,排查时用iftop看带宽,用iostat看IO,两者工具不一样,方向也不一样。
用SSD之后是不是就不会再有磁盘IO问题了?
不是。 SSD的随机读写能力强很多,但遇到大量小文件写入(比如每秒上千次创建、删除文件)或者单队列深度过高,照样会打满IO,SSD的耐久度有限,频繁写入会导致闪存寿命下降,反而更敏感,合理规划缓存、合并小写入,才是长久之计。
云服务器的磁盘IO和高防、CDN这些服务有关系吗?
没有直接关系。 高防和CDN处理的是网络层的流量和攻击,它们不会主动消耗你的磁盘IO,但如果是异常攻击流量导致后端程序疯狂写日志,那磁盘IO就间接升高了,所以遇到突发IO飙升,先看是不是有大量新的访问日志产生,再查网络入口。
磁盘IO问题的根源无非就是“写太多”“读太慢”“缓存太少”,把定位工具用熟,先看iostat确认瓶颈,再用iotop揪出进程,最后针对性调整缓存或迁移数据,大多数情况都能在十分钟内找到方向,记住一个原则:别让磁盘成为系统里最沉默的瓶颈,它只是不会说话,但不代表它不累。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/748094.html

