一、背景
监控发现,服务器的 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的执行结果:如下图:可以看到读写都大幅下降了。
