當 iostat 顯示磁碟 %util 100%、await 飆升,但 top/pidstat -d 卻看不出明顯大戶時,往往是大量小 IO、元資料操作或核心執行緒(如 kjournald、flush-*)在作祟。Linux 內核提供 vm.block_dump 參數,可將所有塊裝置讀寫事件實時導出至 dmesg,配合 awk 聚合即可秒級鎖定「真兇」。本文詳述開啟方法、輸出解讀、一鍵分析腳本與生產環境注意事項。
# 方法 1:sysctl(即時生效,重啟失效)
sysctl -w vm.block_dump=1
# 方法 2:直接寫入 /proc(同效)
echo 1 > /proc/sys/vm/block_dump
# 關閉
sysctl -w vm.block_dump=0
# 或
echo 0 > /proc/sys/vm/block_dump
作用範圍:全域,所有塊裝置(含 dm、md、loop、nvme)。
性能影響:極高 IO 時每秒產生大量日誌,可能佔用 CPU/磁碟,建議開啟 10-30 秒即關閉,或導出至專用日誌檔(dmesg -w | tee /tmp/block_dump.log)。
# 典型輸出
mysqld(7822): READ block 78196624 on dm-0
kjournald(529): WRITE block 211136 on dm-0
bash(8336): dirtied inode 7391146 (dmesg) on dm-0
systemd-journald(412): WRITE block 123456 on dm-1
python3(12345): READ block 987654 on dm-0
python3(12345): WRITE block 987655 on dm-0
欄位含義:
進程名(PID):發起 IO 的進程。READ/WRITE/dirtied:操作類型(dirtied = 標記 inode 髒頁,待回寫)。block 號:邏輯塊號(相對於該裝置起始)。on 裝置:目標塊裝置(dm-0、sda、nvme0n1 等)。(檔案名):dirtied inode 時顯示檔案名(若可解析)。# 統計當前 dmesg 緩衝區中 IO 事件最多的前 10 個進程
dmesg -T | awk -F'[()]' '/READ|WRITE|dirtied/ {print $1}' | sort | uniq -c | sort -rn | head -10
輸出範例:
1245 mysqld
312 kjournald
198 python3
87 systemd-journald
45 rsyslogd
#!/bin/bash
# analyze_block_dump.sh [duration_sec] [top_n]
# 需 root 權限;會自動開啟 block_dump、採樣、關閉、分析
DURATION=${1:-15}
TOP_N=${2:-20}
OUT=/tmp/block_dump_$(date +%s).log
echo "[$(date)] Enabling vm.block_dump=1 ..."
sysctl -w vm.block_dump=1 >/dev/null
echo "[$(date)] Capturing dmesg for ${DURATION}s ..."
dmesg -w -T > "$OUT" &
DMESG_PID=$!
sleep $DURATION
kill $DMESG_PID 2>/dev/null
echo "[$(date)] Disabling vm.block_dump ..."
sysctl -w vm.block_dump=0 >/dev/null
echo "=== Top $TOP_N processes by IO events ==="
awk -F'[()]' '/READ|WRITE|dirtied/ {print $1}' "$OUT" | sort | uniq -c | sort -rn | head -$TOP_N
echo -e "\n=== By device ==="
awk -F'on ' '/READ|WRITE|dirtied/ {split($2,a," "); print a[1]}' "$OUT" | sort | uniq -c | sort -rn
echo -e "\n=== By operation type ==="
awk '/READ/ {r++} /WRITE/ {w++} /dirtied/ {d++} END {print "READ:", r, "WRITE:", w, "DIRTIED:", d}' "$OUT"
echo -e "\n=== Sample raw lines (first 20) ==="
grep -E 'READ|WRITE|dirtied' "$OUT" | head -20
echo "Raw log saved to: $OUT"
現象:凌晨 2 點磁碟 IO 飆升,應用響應變慢。
block_dump 顯示:postgres(1234): WRITE block ... 佔 85%,伴隨 checkpointer 進程大量 dirtied。
根因:checkpoint_timeout 過短 + checkpoint_completion_target 過低,導致检查點衝刷集中。
修正:調大 checkpoint_timeout=15min、checkpoint_completion_target=0.9,平滑寫入。
現象:每小時整點 %util 瞬間 100%。
block_dump 顯示:logrotate(5678): dirtied inode ... 大量 WRITE block ... on dm-0,實為 rename()/unlink() 產生大量 inode/目錄項寫入。
修正:改用 copytruncate 或外部日誌收集(Filebeat/Fluentd),避免大量檔案操作。
現象:iostat 顯示 sda 繁忙,但應用讀寫不大。
block_dump 顯示:kswapd0/kworker/u16:0 大量 READ/WRITE on dm-1(swap 分區)。
根因:記憶體不足觸發換頁。
修正:擴容記憶體或限制容器 cgroup memory.max。
| 項目 | 說明 |
|---|---|
| 日誌緩衝區溢位 | 高 IO 時 dmesg 環形緩衝區可能覆蓋舊事件,建議 dmesg -w 實時導出檔案 |
| 性能開銷 | 每 IO 事件產生一次 printk,極高 IOPS 會明顯佔用 CPU,嚴禁長期開啟 |
| 容器環境 | 宿主機開啟可見所有容器 IO;容器內無權限開啟 |
| 替代工具 | iotop -o(需內核 CONFIG_TASK_DELAY_ACCT)、pidstat -d 1、bpftrace -e 'tracepoint:block:block_rq_issue { @[comm] = count(); }' (eBPF,零開銷、可過濾) |
掌握 vm.block_dump 這把「核級手術刀」,配合 dmesg/awk/bpftrace,即可在不安裝任何第三方工具、不重啟服務的前提下,於生產環境秒級定位 IO 熱點,從現象直達根因。