Linux IO 性能瓶颈排查全攻略:从理论到实战的系统性解决方案
当你发现生产环境某些IO进程读写效率下降,例如:
MySQL查询耗时增高文件读写效率变慢此时我们就可以考虑Linux系统是否存在IO性能瓶颈了,而以下便是笔者整理的一套比较普适的IO性能瓶颈通用排查方法论,同时为了更好地复现这个问题,笔者也用Java写了一个多线程执行数据读写的程序,读者可以查看如下代码结合注释了解一下这个逻辑:
/**
* 启动磁盘I/O操作以模拟高I/O负载
* 通过创建多个I/O任务线程来模拟高磁盘I/O负载
*/
private static void startDiskIOOperations() {
log.info("开始高I/O磁盘操作...");
log.info("在另一个终端中运行 iostat -x 1 来监控磁盘利用率。");
// 创建固定线程数的线程池
executor = Executors.newFixedThreadPool(NUM_THREADS);
// 提交多个任务以连续写入磁盘
for (int i = 0; i < NUM_THREADS; i++) {
executor.submit(new IOTask(i));
}
log.info("磁盘I/O操作已启动,使用 {} 个线程", NUM_THREADS);
}
/**
* 执行连续写入操作以模拟高I/O的任务
* 该类负责执行磁盘I/O操作,通过不断写入和清空文件来模拟高I/O负载
*/
static class IOTask implements Runnable {
private final int taskId;
public IOTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
// 每个线程写入自己的临时文件
String filename = "/tmp/disk_io_test_" + taskId + ".tmp";
try (FileOutputStream fos = new FileOutputStream(filename)) {
log.info("线程-{} 正在写入 {}", taskId, filename);
// 连续将数据写入文件并在每次写入后清空文件
while (!Thread.currentThread().isInterrupted()) {
performDiskIOOperation(fos, taskId);
ThreadUtil.sleep(500);
}
} catch (IOException e) {
log.error("线程-{} 发生错误: {}", taskId, e.getMessage());
}
}
}
/**
* 执行磁盘I/O操作:写入指定大小的数据然后清空文件
* 该方法会连续写入数据到文件,然后清空文件内容,用于模拟高I/O负载
* @param fos 文件输出流
* @param taskId 任务ID
* @throws IOException IO异常
*/
private static void performDiskIOOperation(FileOutputStream fos, int taskId) throws IOException {
long startTime = System.currentTimeMillis();
// 写入数据(分块写入)
long bytesWritten = 0;
while (bytesWritten < WRITE_SIZE) {
fos.write(DATA);
bytesWritten += DATA.length;
}
fos.flush(); // 强制写入磁盘
// 清空文件内容
fos.getChannel().truncate(0);
long endTime = System.currentTimeMillis();
// 打印本次操作的耗时
log.info("线程-{} 完成一次写入和清空操作,耗时: {} ms", taskId, (endTime - startTime));
}
详解Linux系统IO性能问题排查通用方法论
1. 检查服务器负载当我们认为存在IO瓶颈时,首先要做的就是基于top指令查看当前服务器wa即CPU等待IO任务完成的占比,一般情况下20%以下算是一个比较合理的正常阈值,超过30%-40%则表明系统可能存在严重的IO瓶颈。以笔者的服务器为例,可以看到wa的值远大于正常范围,说明当前CPU基本处于等待IO任务完成的情况:
Tasks: 34 total, 1 running, 33 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.5 us, 2.6 sy, 0.0 ni, 5.3 id, 90.5 wa, 0.0 hi, 1.1 si, 0.0 st
%Cpu1 : 0.0 us, 2.2 sy, 0.0 ni, 24.9 id, 72.4 wa, 0.0 hi, 0.5 si, 0.0 st
%Cpu2 : 1.1 us, 0.6 sy, 0.0 ni, 0.6 id, 97.7 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.5 us, 2.7 sy, 0.0 ni, 16.8 id, 80.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 0.6 us, 1.7 sy, 0.0 ni, 0.0 id, 97.8 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 0.0 us, 3.9 sy, 0.0 ni, 18.8 id, 77.3 wa, 0.0 hi, 0.0 si, 0.0 st
2. 查看IO使用率明确系统存在IO性能瓶颈的情况下,我们就需要更进一步定位问题,以笔者为例,一般会执行iostat指令,如下所示,大意为:
-x:显示更多扩展信息(包括设备利用率、等待时间等)每1秒输出1次,持续输出iostat -x 1
从输出结果来看,对应sdd盘使用率%util(IO利用率)飙到100%且iowait达到了78.2%,很明显这块磁盘存在一些异常IO读写任务:
avg-cpu: %user %nice %system %iowait %steal %idle
0.0% 0.0% 1.0% 78.2% 0.0% 20.8%
Device r/s rMB/s rrqm/s %rrqm r_await rareq-sz w/s wMB/s wrqm/s %wrqm w_await wareq-sz d/s dMB/s drqm/s %drqm d_await dareq-sz f_await aqu-sz %util
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdc 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdd 4.00 0.04 0.00 0.00 122.25 10.24 171.00 190.81 1.00 0.58 3884.17 1.12 0.00 0.00 0.00 0.00 0.00 0.00 0.00 664.68 100.00
关键指标解读:
%util:设备利用率,接近100%表示设备繁忙,可能存在IO瓶颈r_await 和 w_await:平均读写请求等待时间,数值越高说明IO响应越慢aqu-sz:平均请求队列大小,数值较大说明IO请求堆积严重await:平均服务时间(读写等待时间之和)3. 明确定位IO进程基于上述过程我们已经明确sdd盘存在IO异常,此时我们就可以通过iotop来查看具体进程了,需要补充的是iotop默认是没有安装的,读者可以参考网上教程自行安装,以笔者的Ubuntu系统为例,对应的安装指令为:
sudo apt install iotop
最后键入sudo iotop -o查看正在执行IO操作的进程,此时就可以非常明确地看到笔者Java进程对应执行异常IO操作的线程和读写速率了:
Total DISK READ: 0.00 B/s | Total DISK WRITE: 142.99 M/s
Current DISK READ: 11.92 K/s | Current DISK WRITE: 336.21 M/s
TID PRIO USER DISK READ DISK WRITE> COMMAND
3253712 be/4 sharkchi 0.00 B/s 18.26 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-3]
3253713 be/4 sharkchi 0.00 B/s 18.26 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-4]
3253711 be/4 sharkchi 0.00 B/s 18.25 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-2]
3253715 be/4 sharkchi 0.00 B/s 18.25 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-6]
3253714 be/4 sharkchi 0.00 B/s 18.24 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-5]
3253710 be/4 sharkchi 0.00 B/s 17.50 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-1]
3253717 be/4 sharkchi 0.00 B/s 17.50 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-8]
3253716 be/4 sharkchi 0.00 B/s 16.74 M/s java -jar web-cache-1.0.jar --app.startup.method=1 [pool-2-thread-7]
4. 补充:其他有用的IO分析工具在实际排查中,除了上述工具外,还有其他一些有用的工具可以辅助分析:
pidstat -d 1:显示每个进程的IO统计信息iotop -a:按IO累计使用量排序vmstat 1:显示虚拟内存统计,包括IO相关指标lsof +D /path/to/directory:列出打开指定目录下文件的进程小结
我们来简单小结一下IO性能瓶颈的排查套路:
通过top命令查看%wa(iowait)指标,判断CPU是否存在异常等待IO的情况使用iostat -x 1查看IO使用率和响应时间等详细指标,定位具体磁盘设备使用iotop显示正在执行IO任务的进程和线程,明确问题程序结合其他工具如pidstat、vmstat等进行深入分析
THE END