系统IO负载拉满,定位罪魁祸首之一次完整线上故障复盘

一、背景

监控发现,服务器的 io time 达到97.3%,如下图所示,磁盘压力极大,影响了程序运行的稳定性;正常是不要超过70%的阈值为最佳。

二、问题排查

2.1 查看磁盘io进程占用情况

通过pidstat命令查看磁盘io进程占用情况

bash 复制代码
pidstat -d 1
bash 复制代码
[root@a1203 UData]# pidstat -d 1
Linux  xxxxxxxxx.x86_64 (a1203)   04/09/2026      _x86_64_        (xx CPU)

08:26:58 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:26:59 AM     0       735      0.00      3.88      0.00  auditd
08:26:59 AM   999     19499  34143.69   1732.04      0.00  mysqld
08:26:59 AM     0    104806      0.00    139.81     85.44  sshd

08:26:59 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:00 AM     0       705      0.00     36.00      0.00  jbd2/vdb-8
08:27:00 AM   999     19499  39504.00    820.00      0.00  mysqld
08:27:00 AM   999     25406      0.00     20.00      0.00  mongod
08:27:00 AM     0     81645      0.00      4.00      0.00  java
08:27:00 AM     0     82118      0.00      4.00      0.00  java
08:27:00 AM     0     94659      0.00    348.00      0.00  java

08:27:00 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:01 AM   999     19499  51520.00    468.00      0.00  mysqld

08:27:01 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:02 AM   999     19499  39104.00    800.00      0.00  mysqld
08:27:02 AM     0     82118      0.00     16.00      8.00  java
08:27:02 AM     0    104806      0.00    144.00     88.00  sshd

08:27:02 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:03 AM     0       705      0.00     28.00      0.00  jbd2/vdb-8
08:27:03 AM   999     19499  40608.00    420.00      0.00  mysqld

08:27:03 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:04 AM   999     19499  51632.00   2388.00   1020.00  mysqld
08:27:04 AM     0     82118      0.00      4.00      0.00  java

08:27:04 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:05 AM     0       705      0.00     52.00      0.00  jbd2/vdb-8
08:27:05 AM     0       735      0.00      4.00      0.00  auditd
08:27:05 AM   999     19499  40624.00    200.00      0.00  mysqld
08:27:05 AM     0     94659      0.00    340.00      0.00  java
08:27:05 AM     0    104806      0.00    144.00     88.00  sshd

08:27:05 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:06 AM   999     19499  40928.00    228.00      0.00  mysqld

08:27:06 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:07 AM   999     19499  49184.00   1184.00   1020.00  mysqld
08:27:07 AM     0     82118      0.00     32.00     16.00  java

08:27:07 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:08 AM     0       705      0.00     48.00      0.00  jbd2/vdb-8
08:27:08 AM     0       735      0.00      4.00      0.00  auditd
08:27:08 AM     0     19226      0.00      8.00      0.00  dockerd
08:27:08 AM   999     19499  43104.00    116.00      0.00  mysqld
08:27:08 AM     0    104806      0.00    144.00     88.00  sshd

08:27:08 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:09 AM     0       705      0.00      4.00      0.00  jbd2/vdb-8
08:27:09 AM     0     19226      0.00      4.00      0.00  dockerd
08:27:09 AM   999     19499  57088.00   1352.00   1020.00  mysqld

08:27:09 AM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s  Command
08:27:10 AM     0       705      0.00     68.00      0.00  jbd2/vdb-8
08:27:10 AM   999     19499  42176.00    552.00      0.00  mysqld
08:27:10 AM   999     25406      0.00     20.00      0.00  mongod
08:27:10 AM     0     94659      0.00    340.00      0.00  java

可以看到:PID 19499 的mysqld进程是唯一的磁盘 IO 大户,完美对应了之前 vdb 盘 97% 的利用率,所有问题根源彻底实锤。

典型的随机读打满 HDD:MySQL 的全表扫描、无索引查询、慢 SQL,会产生海量小随机读,直接把 HDD 的 IOPS(极限 100-200)干到 2400+,导致 % util 100%

2.2 查看mysql的慢查询

可以通过如下sql查看系统负载最高的SQL,如果发现大多都是Sleep,则表明没有实时执行的sql

sql 复制代码
select * from `performance_schema`.processlist where time > 1 order by time desc;

可以通过查看历史执行耗时最高的 SQL,定位慢查询

sql 复制代码
SELECT * FROM performance_schema.events_statements_summary_by_digest 
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;

历史sql查询结果如下:

🚨终于抓到真凶了!!!

就是这三条 XXL-JOB 定时任务 SQL

把 MySQL 磁盘 IO 打到 100%!!!


2.2.1 真凶 SQL 1(最狠!)

sql 复制代码
SELECT `id` FROM `xxl_job_log` 
WHERE ! ((trigger_code in (0,200) and handle_code=0) OR (handle_code=200)) 
AND alarm_status=0 
ORDER BY id ASC LIMIT 1000
问题:
  • 全表扫描
  • 无索引
  • xxl_job_log 日志表巨大 1.3个G了已经,条数为:1,541,191(154万条)
  • 频繁执行

2.2.2 真凶 SQL 2

sql 复制代码
SELECT t.id FROM xxl_job_log t
LEFT JOIN xxl_job_registry t2 
ON t.executor_address = t2.registry_value
WHERE t.trigger_code=200 
AND t.handle_code=0 
AND t.trigger_time <= ?
AND t2.id IS NULL
问题:
  • 联表查询
  • 无索引
  • 扫全表
  • 定时疯狂跑

2.2.3 真凶 SQL 3

sql 复制代码
SELECT COUNT(handle_code) ...
FROM xxl_job_log
WHERE trigger_time BETWEEN ? AND ?
问题:
  • 统计查询
  • 无索引
  • 大量 IO

三、结论

这三条 XXL-JOB 自带的查询,没有索引,
定时疯狂执行 → 全表扫描 → 磁盘 IO 100% → 业务接口卡死、空响应!


四、解决方法

4.1 方法1:直接给xxl_job_log 表加索引

最快、最有效

sql 复制代码
USE xxl_job;

CREATE INDEX idx_trigger_handle ON xxl_job_log(trigger_code, handle_code);
CREATE INDEX idx_alarm_status ON xxl_job_log(alarm_status);
CREATE INDEX idx_trigger_time ON xxl_job_log(trigger_time);
CREATE INDEX idx_executor_address ON xxl_job_log(executor_address);

加完索引立刻生效!

  • MySQL 不再全表扫描
  • 磁盘 IO 从 99% → 5% 以下

4.2 方法2:备份并新建xxl_job_log空表

先备份xxl_job_log表;

然后清空xxl_job_log表;

空表当然查询很快了。

五、处理结果回归

如上图所示,在处理完之后,io直线下降了。中间小峰值是备份数据时导致。

再看下pidstat -d 1的执行结果:如下图:可以看到读写都大幅下降了。

END

相关推荐
menggb074 个月前
查看数据库mysql的慢查询
慢查询
聆风吟º4 个月前
openEuler 25.09 性能测评:6.6 内核环境部署与内存磁盘性能监控的指南
性能测试·openeuler·磁盘io·系统评测·内存性能
奔跑吧邓邓子4 个月前
【C语言实战(72)】C语言文件系统实战:解锁目录与磁盘IO的奥秘
c语言·文件系统·目录·开发实战·磁盘io
刘某的Cloud6 个月前
磁盘-IO
linux·运维·系统·磁盘io
小Tomkk6 个月前
如何在 MySQL 中实现慢查询监控
mysql8.0·慢查询
冲上云霄的Jayden6 个月前
Ubuntu 磁盘 I/O 监控完全指南
ubuntu·vmstat·磁盘监控·iostat·磁盘io·iotop·dstat
hh真是个慢性子6 个月前
mongodb慢查询优化 速度欻欻滴~
数据库·mongodb·性能优化·慢查询
GM_8287 个月前
【Go项目基建】GORM框架实现SQL校验拦截器(完整源码+详解)
sql·golang·拦截器·gorm·慢查询·持久层基建
岚天start8 个月前
Linux sar命令详细使用指南
linux·运维·服务器·负载·sar·磁盘io·sysstat