十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

磁盘IO打满排查实战:从慢SQL到日志风暴的五类典型案例

磁盘IO打满排查实战:从慢SQL到日志风暴的五类典型案例 磁盘IO打满这个话题在运维圈子里属于“看着简单、查起来要命”的那一类。CPU拉满了你能通过top一眼看到内存不够了free、vmstat敲下去基本就有数唯独磁盘IO业务表现是“慢”系统指标是iowait飙升可真要定位到是哪个进程、哪条SQL、哪个日志文件在搞事情往往得绕好几个弯。我这些年处理过大大小小几十次IO故障其中5个案例印象最深今天把它们拆开来讲顺便把背后沉淀下来的那套排查方法论完整梳理一遍。如果你也在维护数据库、应用服务器或者云上资源这套方法应该能帮你省掉不少弯路。1. 磁盘IO排查的整体思路与准备1.1 磁盘IO问题为什么“藏得深”CPU和内存的问题通常都能比较直接地对应到某个进程但磁盘IO涉及一整条链路应用层发起读写请求内核页缓存先承接一部分文件系统再决定如何落盘块设备层排队调度最后由磁盘或云存储完成物理读写。任一层出问题业务层感受到的都是“变慢了”“卡住了”很难直接判断是那一层在搞鬼。这就是磁盘IO问题的核心难点它不像CPU那样有清晰的进程对应关系更像一个被多层包装的“黑盒”。尤其是现代服务器普遍使用SSD和云盘底层并发能力很强你看到%util已经100%但不一定能直接看到是谁在发IO。所以排查的第一步不是盲目敲命令而是先建立一张“IO栈地图”知道从应用到硬件每一层该看什么指标再逐层下钻。另外IO问题经常是结果而非原因。比如内存不足触发swap导致系统频繁读写磁盘又比如某条SQL全表扫描把磁盘队列打满连正常写操作都被拖慢。如果你只盯着IO看很容易被表面指标带偏真正的问题反而被忽略。1.2 先搞懂 iostat 这些关键指标排查磁盘IOiostat是绕不开的第一个命令。我常用的参数是iostat -x 1每秒更新一次并输出扩展指标。刚开始看这块的人很容易被一堆字段搞晕其实核心就盯几个。%util是大家最常说的“磁盘使用率”但它并不是严格意义上的“磁盘忙了百分之多少”。它表示的时间段内设备处理IO请求所占用时间的百分比。对于机械盘%util接近100%通常意味着已经饱和但对于SSD因为有NCQ和多队列并发%util到100%并不一定代表性能见顶还要结合IOPS和吞吐量一起看。await是平均每次IO请求的处理时间包括在队列里等待的时间和真正处理的时间。这个指标非常敏感如果平时的await在2-3毫秒突然涨到几十毫秒甚至上百毫秒基本可以确定磁盘IO存在瓶颈。svctm这个指标很多人会看但它只是平均服务时间在并发场景下会失真参考价值有限我更习惯用await和%util交叉判断。还有两个容易被忽略的指标avgrq-sz平均每次IO大小和r/s、w/s每秒读/写次数。如果 avgrq-sz 很小比如只有几KB说明大量小文件的随机读写如果很大比如几百KB甚至更大通常是顺序读写。随机小IO和顺序大IO对磁盘的影响完全不同前者更考验IOPS后者更考验带宽区分清楚才能对症下药。1.3 排查工具箱哪些命令必须会用排查IO问题我不会只依赖某一条命令而是几个工具组合着来从全局到局部逐层收窄范围。iostat负责全局观察确认是不是IO问题瓶颈在哪个盘。top和vmstat负责看整体资源情况尤其是iowait和swap状态。iotop是定位进程IO使用情况的利器可以看到哪个进程在大量读盘或写盘。pidstat -d也能按进程统计IO比iotop更适合做采样记录。定位到进程之后需要进一步看它到底在操作什么文件lsof -p PID可以列出进程打开的文件特别适合查日志文件、临时文件strace -p PID可以跟踪系统调用看看是不是在某次读写上卡住了。如果是数据库或者复杂应用还得结合应用本身的日志和监控来看。更深层的排查比如定位IO在块设备层的具体行为可以使用blktrace或者perf但这类工具成本较高我一般只有在前面几层都排查不出来时才动用。最后还有dmesg和smartctl用来检查内核日志和硬盘健康状态毕竟硬件故障也是IO问题的高发原因之一。这套工具链组合起来基本覆盖了从“到底是不是IO问题”到“具体是哪个进程在干什么”的完整路径。2. 案例一一条慢SQL把数据库IO打到100%2.1 现场现象接口响应突然飙到2秒那次故障发生在下午的业务高峰期。订单查询接口平时响应在20毫秒左右监控突然告警p99耗时长到了2秒以上。业务方反馈用户查询订单记录越来越慢后台管理界面也在转圈。我登录数据库服务器先敲了iostat -x 1看了一眼sda的%util直接顶到接近100%await从正常的5毫秒涨到了200毫秒而且持续降不下来。但CPU使用率并不高user和system合计才20%多iowait却涨到40%。这个组合很有特征性不是CPU算不动而是CPU在等IO返回。从业务感知来说数据库查询变慢通常只有两类可能要么锁等待要么IO阻塞。当时先排除了锁因为show processlist里没有大量 Waiting for table metadata lock 之类的会话。那就重点查IO是谁吃掉的。2.2 定位过程从系统指标一路挖到SQL第一步用iotop看进程mysqld进程的磁盘读速率飙到了100MB/s以上基本锁定是MySQL在读盘。但MySQL读盘也有两种可能正常业务查询的数据不在内存里需要去磁盘读或者有人跑了一条全表扫描的SQL把脏数据全扫了一遍。第二步回到MySQL内部查看当前的活跃会话。我先用了show full processlist发现确实有一条SQL已经跑了快90秒还在不断执行。那条SQL是一个订单明细的列表查询条件里有member_id和create_time但执行计划里没有命中任何索引typeALL典型全表扫描。表数据量是800万行别以为800万不多关键是把800万行数据全部读出来每一行都要回主键索引取整行数据这就产生了大量随机IO。第三步用explain确认判断explain select ... from order_detail where member_idxxx and create_time between ...key字段为空rows预估至少800万Extra里还能看到 Using where; Using filesort。全表扫描加上排序等于既在扫数据又在临时文件里排序IO压力自然翻倍。这里要解释一下为什么全表扫描会打满IOMySQL InnoDB的数据是按聚簇索引组织的主键就是索引结构。member_id上没有索引数据库只能从主键索引的第一个叶子节点开始挨个扫过去每读一条记录就做一次比较。对于机械盘来说随机读的IOPS本身只有100到200左右现在要扫描几百万行IO队列必然被打满即使底层是SSD这种海量随机读也会把请求队列塞满让所有正常查询跟着遭殃。2.3 修复动作加索引与SQL改写缺一不可问题定位清楚接下来就是止血和根治。先止血kill掉那条慢查询让数据库IO立刻降下来。这一步在业务高峰时很关键因为慢SQL占着大量IO其他所有查询都在排队不杀掉的话加索引的操作都可能反被拖垮。再根治给 order_detail 表加上组合索引。我当时没有直接在生产库上执行 ALTER TABLE因为表有1.2亿行数据直接DDL会锁表并且长时间写入会拖垮复制。我选择用pt-online-schema-change在低峰期做在线变更对业务无感知地完成建索引ALTER TABLE order_detail ADD INDEX idx_member_create_time (member_id, create_time)。索引加完之后还要推动研发改写SQL。原SQL用了SELECT *把很多用不到的字段也读出来我建议改成只查需要的列减少每次IO传输的数据量。同时页面接口原本是分页查询但因为排序字段没进索引导致每次翻页都重新扫描排序改成索引覆盖排序后这条SQL的范围从“全表扫描加文件排序”变成了“索引范围扫描索引排序”耗时从90秒降到30毫秒左右。改完之后再观察 iostat%util从100%降到了20%上下接口p99也回到了正常的几十毫秒。这个案例里有一个细节值得一提加索引前我先在从库上执行了explain验证索引是否生效确认走了idx_member_create_time才在线上执行。生产中永远不要在没验证过的前提下直接对核心大表做变更。2.4 从案例一沉淀下来的排查经验这个案例给我最深的教训是数据库IO打满时别急着调内核参数、换硬件先去看SQL。数据库IO问题的优先级排序我后来基本固定为慢SQL 锁等待 内存/缓冲池配置 硬件故障。其中慢SQL占比极高很多看似神秘的高IO问题最后都落到某条没走索引的查询上。另一个经验是要区分“紧急止血”和“根治”。生产故障现场第一要务是恢复业务kill掉异常查询、做请求限流、切流量到从库这些都是止血手段但止血之后必须花时间做根因分析。如果只是把慢查询杀了就完事同样的SQL过一会儿还会再冒出来而且每次冒出来都更难处理。还有一点加索引不能只看“有没有索引”还要看区分度。区分度很低的字段比如性别、状态这种几个值就覆盖全表的字段加了等于没加优化器也不会走。真正需要优先加索引的是高区分度的筛选字段比如 member_id、order_no这类字段才能把扫描范围快速缩小。3. 案例二应用日志“写穿”磁盘3.1 现场现象流量没涨IOutil却居高不下第二次典型案例是一台Java应用服务器。业务流量跟前一天相比没有明显变化但磁盘IO util从下午开始持续在80%到100%之间徘徊服务的响应时间也跟着上升虽然没有完全不可用但用户侧已经开始感受到卡顿。我登录机器后先看iostat -x 1确认问题不在数据库因为这台服务器上面的应用是纯无状态服务请求进来处理完就返回理论上IO量不大。再看df -h磁盘使用率只有60%说明不是磁盘满了。但iostat的 wKB/s 高得离谱明显是某个进程在疯狂写盘。当时我心里有个初步怀疑大概率是日志写入量异常。因为无状态应用正常情况下不会产生如此高的磁盘写入除非有人在打印日志而且打印的内容还特别多。3.2 定位过程iotop、du、lsof交叉验证先用iotop采样了几秒看到Java进程PID 31245的磁盘写速率在50MB/s以上其他进程基本可以忽略。这就把范围缩到了单个应用进程。接下来要看这个Java进程到底在写什么文件。我用了两条路径交叉验证。第一条是du -sh /home/app/logs、du -sh /var/log这类目录级别排查发现应用日志目录的增长速度肉眼可见几秒钟内就能看到变化。第二条是直接用lsof -p 31245 | grep -i -E log|trace|out列出该进程打开的所有文件结果看到一个日志文件的偏移量已经增长到70多GB而且还在持续增长。顺着日志配置查下去根因浮出水面这个应用的日志框架用的Logback某天有同事为了排查线上问题把某个组件包下的日志级别改成了DEBUG然后忘了改回来。DEBUG级别会打印大量细节日志包括每次请求的参数、响应体、耗时等流量不高的时候还好一旦流量是正常水平写量就完全失控。还有一个配置问题日志文件原本配了按大小滚动切割但maxFileSize没有写对单位日志组件默认按字节解析导致切割条件永远不成立单文件无限增长。两个问题叠加直接把磁盘IO打满了。3.3 修复动作切日志级别、改切割策略因为Logback支持动态修改日志级别我第一时间通过应用的JMX端口重新把那个包的日志级别切回了INFO写量肉眼可见地降下来了。但已经写出来的70GB日志文件还躺在磁盘上而且没有马上处理。这里有个容易踩坑的点如果直接用rm删掉日志文件应用进程仍然持有这个文件的句柄磁盘空间不会立刻释放而且新日志会继续写入到那个已删除的inode上导致文件系统空间显示正常但磁盘IO仍然被占用。正确的做法是先确认进程打开的文件路径对日志文件做truncate -s 0清空这样句柄还在空间会释放应用也不会报错。等日志写完、进程重启后再彻底删除即可。随后我修正了Logback的滚动配置把maxFileSize明确写成500MB同时加了maxHistory15和totalSizeCap20GB避免历史日志无限堆积。另外把日志输出改成了异步模式使用AsyncAppender这样业务线程打印日志时不用等磁盘落盘IO消耗不再直接阻塞请求处理。清理完成后再看 iostat磁盘写速率从50MB/s降到不到1MB/s服务响应恢复正常。这次故障从发现到处理完前后大约半小时但真正排查定位的时间占了三分之二。如果一开始就想到去检查日志级别可能五分钟就能解决。3.4 后续预防日志治理的几条硬规矩日志写入风暴这类问题根因往往是“管理缺失”而不是“技术复杂”。这几个预防措施我后来基本在每套系统里都会推动落地。第一日志量必须纳入监控不只监控磁盘空间使用率还要监控日志目录的写入速率或增长趋势。空间满了会告警但空间还没满时写入速率早就异常了趋势监控能提前发现。第二日志级别要管住。生产环境禁止临时开DEBUG如果确实需要debug现场需要走变更流程并且设置一个自动恢复的定时任务比如通过日志框架的定时重载配置让DEBUG级别最多维持15分钟就自动回滚。第三滚动切割策略要测试。很多人配置了RollingFile但是因为日志框架版本差异、时间格式写错、触发条件设置不合理导致切割一直不生效。建议上线前做一次日志生成测试确认切割后的文件按预期命名和保留。打日志本身是一件“平时不觉得重要出事才知道踩坑”的事。给生产代码加的每行日志都要有“删除它的一天”的自觉。4. 案例三凌晨备份任务和业务抢IO4.1 现场现象每天定时出现的IO尖峰第三个案例比较典型因为故障是有规律出现的。一台业务服务器每隔几天就会出现一次IO尖峰时间在凌晨2点半左右持续到4点。业务方反馈低峰期虽然访问量不大但早上偶尔还会有IO残留偏高的情况影响白天的正常体验。我查看监控曲线发现磁盘IO util在每天凌晨固定时段都会冲到100%白天大部分时间都很正常。这种“规律性尖峰”几乎是定时任务在作祟。先查了crontab -l看到几条定时任务其中有一条用 rsync 每天凌晨2:30执行把源服务器上的大目录同步到本地。命令写得比较简单rsync -av /data/source/ /backup/没有任何限流。4.2 定位过程通过任务时间对齐找出 rsync结合IO监控曲线和crontab的启动时间几乎可以确定那条rsync任务就是IO尖峰的元凶。但为了严谨我还是做了两步验证。第一步确认任务确实在同步大量数据。我手动跑了一次rsync -av --dry-run /data/source/ /backup/输出显示有几百万个文件。注意rsync在做同步之前需要对源端和目标端的每个文件做stat对比判断哪些文件需要传输。文件数量越多这个“遍历对比”阶段消耗的元数据IO就越大。第二步在故障时间段执行pidstat -d 1抓取进程IO数据看到rsync进程的读写速率非常夸张而且把其他进程的IO都挤掉了。之所以会对系统影响这么大是因为rsync默认全速执行它会尽量打满磁盘和网络带宽完全不会顾及业务进程。问题确认后还要考虑为什么同步几百万个文件会把IO打满这个小文件场景下rsync对每个文件都要同时读源端和读目标端的元数据元数据操作本身是随机小IO密度极高。再加上目标存储和源存储之间的网络带宽也被占满网络层的等待会进一步拉高IO等待时间。4.3 修复动作错峰与限流才是关键这个案例的修复方案主要围绕三个方面。首先是错峰执行。定时任务默认2:30执行但同一时间还有另一台机器在做全量备份两台机器的IO高峰叠在一起对共享存储的压力非常大。我把任务时间从凌晨2:30调到了4:30避开其他备份任务和业务高峰。这个改动看似简单实际效果立竿见影。其次是限流。给 rsync 命令加上带宽限制参数rsync -av --bwlimit5000 /data/source/ /backup/这里的5000单位是KB/s约等于5MB/s。这样即使同步任务启动也不会再抢占全部带宽和IO。另外加上--partial --inplace让断点续传和原地写入减少临时文件产生的额外IO。最后是给任务设置IO优先级。使用ionice命令ionice -c2 -n7 rsync -av --bwlimit5000 ...。-c2表示Best-effort调度类-n7是其中最低优先级意味着只有当系统有其他IO空闲时同步任务才会真正执行。但这里要特别说明ionice对I/O调度器有要求传统CFQ调度器下生效明显现代内核搭配SSD时默认使用 none 或 mq-deadlineionice可能就失效了。所以在SSD环境下我更依赖--bwlimit和任务错峰来限制影响。修改完成后我连续观察了一周IO尖峰消失了rsync任务也能正常在低峰期执行完。同步任务还是那个任务只是学会了“让路”。4.4 定时任务类问题的排查套路定时任务引发的IO问题特点是“规律性强、时间点明确、持续时长固定”。遇到这类问题我建议按下面的套路排查。先把故障时间窗口和服务器上的定时任务列表做对齐。crontab -l看当前用户的定时任务ls /etc/cron.d/、/etc/crontab看系统任务还要检查 systemd timersystemctl list-timers。很多时候问题不是Linux cron而是某个应用框架内部的调度任务比如Java的XXL-Job、Quartz这类任务也要纳入考虑。对齐时间之后用pidstat -d 1、iotop在故障时段抓进程IO确认是哪个任务的进程在消耗IO。如果没有现成监控我有时会写一个临时脚本在故障时段每分钟采样一次把高IO进程的PID和命令名记录下来事后分析用。解决定时任务IO问题时心里要有个原则不是所有任务都必须“越快越好”。备份、同步、数据分析这类离线任务完全可以牺牲一部分速度来换取对在线业务的影响最小化。错峰、限流、降优先级这三个手段几乎能解决绝大多数定时任务引发的IO问题。5. 案例四云盘硬件故障导致的IO抖动5.1 现场现象找不到高IO进程磁盘却频繁停顿第四个案例让我印象很深因为它的表现和前面几个完全不一样。一台云主机运行时磁盘IO出现规律不一的抖动每隔几分钟就会卡一下每次持续几秒到几十秒。业务侧的表现是请求偶尔超时但过一会又自动恢复。我登录服务器后用iostat -x 1观察发现磁盘util会突然跳到接近100%但很快又降下来。奇怪的是在这个段期间我用iotop和pidstat -d抓取进程IO没有一个进程的IO使用量明显高。这就很反常磁盘指标异常却没有对应的“IO大户”进程。如果磁盘上找不到吃IO的进程问题通常有几种可能系统层的内核线程在做清理或刷新存储硬件本身有问题请求不断重试云盘底层所在的物理存储出现了热迁移、拥塞或硬件降级。这时候就需要跳出进程层去检查系统日志和硬件健康状况。5.2 定位过程内核日志和SMART信息给出实锤先从内核日志入手。执行dmesg -T | grep -i -E error|fail|timeout|I/O很快发现大量类似blk_update_request: I/O error, dev vda的记录。这就不是应用层能解释的问题了而是块设备层在报告读写错误。再看系统日志/var/log/messages同样能看到virtio驱动或者SCSI层的报错。云主机使用的常用虚拟化方案块设备通常走 virtio 驱动驱动层报错一般意味着底层物理存储有问题。我还尝试运行smartctl -a /dev/vda查看磁盘健康信息。如果云厂商提供了裸设备映射这条命令可以看到SMART属性比如 Reallocated_Sector_Ct重映射扇区数量、Current_Pending_Sector待定扇区这些数值如果持续增长说明盘片正在出现物理坏道。不过云盘这种虚拟化存储不一定把SMART信息透传出来如果查不到就需要借助云厂商的监控数据。云厂商后台的监控曲线显示这台云盘的若干指标出现周期性峰值而且正好对应业务卡顿时间窗口。结合 iostat、dmesg、厂商监控三方面的证据基本可以确定问题指向云盘底层存储而不是应用或操作系统。5.3 修复动作快照备份与迁移换盘硬件故障类的修复策略和前面几个案例完全不同核心不是调参数而是“数据安全优先尽快迁移”。我先给云盘打了一个快照确保数据可恢复。这一步在任何存储故障场景下都是最优先的别等到盘彻底坏了再想起备份。然后我联系云厂商支持确认了底层存储异常反馈结果是需要对云盘进行迁移。迁移过程通常可以在线操作但对业务影响不确定。所以我在业务低峰期做了迁移计划先把服务切到另一台备用实例上再对故障盘做卸载和更换。整个过程大约持续了一个小时中间有一次分钟级的中断但业务侧因为有重试机制用户几乎无感知。更换完成后我再持续观察了两天iostat、业务响应都非常平稳没有再出现异常抖动。这次故障给我的体会是云盘的IO问题往往比物理机更难排查因为你看不到物理磁盘只能依赖厂商监控和系统日志。遇到“进程IO正常磁盘却持续异常”的情况优先怀疑硬件层。5.4 区分“应用IO问题”与“存储IO问题”经过这个案例我总结出一个很重要的判断方法先把IO问题分为“应用层发起的高IO”和“存储层本身的故障”。最直观的区分方式就是看进程级IO数据。如果iotop、pidstat -d能明确看到某个进程在大量读写说明是应用层问题继续往SQL、日志、任务方向排查。如果所有进程IO都很低磁盘却持续报错、util异常那就要重点检查存储层。系统日志是关键。dmesg里如果出现I/O error、blk_update_request、virtio_blk报错那基本可以定性为硬件或驱动层问题。同时关注await指标如果是负载过高导致的IO慢await和%util通常都高但如果是硬件故障常常会出现await很高但%util并不稳定的情况因为请求在持续重试、等待超时。对云上业务来说云盘故障尤其需要注意数据冗余。单块云盘即使有做RAID或备份也要在业务架构层面保留跨节点冗余比如数据库用一主一从、应用层做无状态化。这样单盘故障时可以直接切换不用等迁移完成才能恢复业务。6. 案例五swap抖动把IO“伪装”成了磁盘问题6.1 现场现象iowait高磁盘util却不高第五个案例发生在中间件服务器上。这个机器跑的是一个Java中间件进程平时非常稳定突然有一天业务方反馈调用延迟抖动明显一会儿快一会儿慢导致上游服务经常超时重试。我登录机器后看了top立刻注意到iowait值很高持续在20%以上理论上说明磁盘子系统有压力。但奇怪的是我再用iostat -x 1观察%util只有30%左右读写量都不大。这就出现了一个矛盾iowait很高说明CPU很多时间在等IO返回但磁盘的读写负载又不高那么等的是什么IO这种场景下我的第一反应已经不是看磁盘了而是怀疑swap在作祟。页面交换swap的读写也会被计为IO但它的特点是数据量不一定大而是随机的、不连续的所以即使磁盘利用率不高等待时间却被拉得很长。6.2 定位过程free、vmstat、top三兄弟揭真相我立刻跑了free -h看到物理内存的available已经不足1GBswap used从原来的几百MB涨到了几GB。继续看vmstat 1siswap in和soswap out两个字段的值一直在跳动而且数值不小持续表明系统在频繁地换入换出内存页。vmstat输出的几个关键字段我平时都会同时看r运行队列过高说明CPU繁忙b阻塞进程数如果持续大于0说明进程在等IO这个场景下特别明显si和so非零时说明内存和磁盘之间在不停交换。在top里按内存排序我看到了那个Java进程RES涨得很高已经接近物理内存上限。再结合时间点看进程内存从几天前开始就一直在增长中间没有回落这很像内存泄漏的表现。我用了jstat -gcutil PID 1000观察JVM堆使用发现老年代使用率随时间不断上升GC之后也降不下来。为了进一步确认是哪个对象在泄漏我抓了一份heap dump用MAT分析最后定位到是某个自定义缓存类把所有查询结果都放进了 ConcurrentHashMap而且没有设置过期策略。流量一上来这个map就无限增长最终把整个JVM堆占满触发频繁GCGC线程在回收对象时会大量扫描内存内存不足时又触发swap于是一连串问题都体现在IO指标上。6.3 修复动作优化内存分配与止损这个案例的修复分两步走。止损优先。我当时通过JMX接口把那个缓存类的清理线程执行频率临时调高马上释放了一部分内存。同时把JVM参数里的-Xmx从接近物理内存上限的值降下来预留出足够内存给操作系统。这里要注意-Xmx不是越大越好物理内存总共64GB你给JVM分配60GB那操作系统和其他进程就没空间了只能靠swap硬撑反而把性能拖垮。合理的做法是根据业务负载预估堆需求同时保证操作系统有10%-20%的富余内存。根治修复是在代码层面做的给缓存加了一个基于时间和数量的淘汰策略超过阈值就自动清理同时限制了单个key的超时时间。修改发布之后内存增长曲线稳定下来不再逼近上限。还有一个临时手段是调整swap策略。Linux 的vm.swappiness参数控制内核倾向于使用swap的程度默认值通常是60偏高。对数据库、中间件这类延迟敏感型业务我会建议设置得低一些比如sysctl vm.swappiness10。但要注意这只降低了使用swap的倾向并不能解决内存根本不足的问题真正靠的还是合理的内存规划和泄漏修复。6.4 内存与IO的关联别被表象带偏这个案例本身不是“磁盘问题”却以IO问题的面貌出现。iowait高、业务卡顿如果你一直围着磁盘转可能很久都定位不到真正的内存泄漏。这就是为什么我在排查IO问题时永远会把vmstat、free放在和iostat同等重要的位置。内存和IO是一对隐性联动内存不足导致进程换页换页产生磁盘读写磁盘读写导致IO等待IO等待让业务请求变慢业务超时又触发更多重试和对象创建进一步加剧内存压力。整个循环一旦形成系统会像是“中毒”一样越来越慢。所以排查IO问题时要记住一句话磁盘IO指标只是一个信号真正的原因可能在内存、可能在CPU、可能在SQL也可能纯粹是硬件故障。带着全局视角看问题比手里拿着再多的工具都重要。7. 可复用的排查方法论与速查表7.1 一张速查表先盯这5个指标指标常用命令健康参考异常含义%utiliostat -x 1低于70%机械盘更低持续接近100%说明磁盘负载很重awaitiostat -x 1数毫秒级别几十毫秒以上说明IO响应明显变慢si/sovmstat 1长期为0或接近0持续非零说明内存不足swap频繁iowaittop/vmstat低于20%持续高于30%说明IO子系统是瓶颈wKB/siostat -x 1与业务量匹配无业务时仍有大量写入多为异常写这5个指标覆盖了“是不是IO问题”“是读还是写”“有没有swap参与”三个维度。我建议在入口处固定用这组命令做第一轮判断iostat -x 1、vmstat 1、top三屏数据拉在一起看基本能确定方向。7.2 五个案例的定位手段对照表案例核心现象定位工具根因类型快速止损根治手段数据库IO接口变慢%util100%iostat→iotop→慢日志全表扫描SQLkill慢SQL加索引SQL改写日志风暴流量平稳写IO高iotop→lsof→duDEBUG日志切割失效切日志级别日志治理异步输出定时任务固定时间IO尖峰crontabpidstatrsync全速同步手动停止任务错峰--bwlimitionice硬件故障无高IO进程磁盘抖动dmesgsmartctl云监控云盘底层故障快照切换实例迁移换盘冗余架构swap抖动iowait高磁盘不忙freevmstattop内存泄漏触发swap临时清理缓存修复泄漏内存规划把这五个案例放在一起可以看到虽然根因不同但排查路径是一致的先确认是不是IO问题、再定位进程、然后深挖到具体操作、最后回到根因并解决。这就是方法论的价值它比某个具体的命令更有通用性。7.3 五步排查法从全局到根因的闭环第一步全局观察。执行iostat -x 1、vmstat 1、top确认当前是不是IO瓶颈同时记录等待时间、IO util、swap情况判断方向。第二步进程定位。用iotop、pidstat -d找到消耗IO的进程或线程。如果找不到明显进程往硬件方向排查看dmesg、smartctl、云监控。第三步细节深挖。对定位到的进程用lsof、strace、/proc/PID/io甚至应用自身的慢日志、GC日志确认它在干什么是SQL查询、日志写入、文件同步还是内存换页。第四步根因验证。结合业务变更记录、定时任务、近期发布、硬件告警验证你推断的根因是否成立。比如慢SQL加索引后用explain确认执行计划是否变化日志级别修改后用iostat确认写速率是否下降。第五步解决与留档。先止血再根治最后把问题现象、定位过程、解决步骤整理成文档并把关键指标加入监控。没有文档和监控的排查经验很难变成团队的资产。7.4 我踩过的坑几条独家避坑提醒最后分享几个我在实际排查中踩过或见过的坑都属于“不遇到一次就想不到”的细节。第一不要在IO打满时盲目重启服务器。重启确实会让IO指标归零但也会抹掉现场很多线索就丢了。更严重的是如果问题出在数据不一致或磁盘文件系统损坏重启反而可能让情况更糟。除非业务完全不可用否则先花几分钟保留现场比如保存dmesg、iostat、top和进程列表的输出。第二不要只盯一个指标下结论。只看%util容易忽视swap只看iowait容易误判硬件只看iotop容易漏掉瞬时IO。多个指标交叉验证才能接近真相。第三每次IO问题先问一句“最近改了什么”。很多故障不是“突然发生”的而是某次发布、某个配置变更、某个新任务上线后出现的。变更记录往往是排查效率最高的切入点比对着内核参数反复猜测要快得多。第四IO问题排查需要保留现场记录。哪怕只是把iostat -x 1 10的输出存成文件把当时的 processlist 截图都可能成为事后复盘的关键证据。我现在每处理一个故障都会建一个目录把命令输出、截图、时间线、分析过程都放进去几天后再回看很多细节会更清晰。磁盘IO打满这个问题确实不是靠一条命令就能解决的。但只要脑子里有“IO栈地图”手里有正确的工具组合心里有一套从全局到根因的排查流程大部分问题都能在半小时内定位到方向。上面这些案例和方法是我踩了无数次坑之后整理出来的希望能在你手忙脚乱的深夜少走几步弯路。下次再遇到 IO 告警别急着慌乱先把 iostat、vmstat、top 三屏数据拉齐再顺着流程走答案往往就藏在细节里。
返回列表