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 st2. 查看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等進行深入分析



























