
先說說這個標題背后的真實場景。絕大多數(shù)人搜這句話的時候服務器磁盤已經(jīng)快滿了或者某個業(yè)務目錄增長異常需要立刻定位到底哪個文件夾在吃空間。我自己處理過的磁盤告警少說也有幾十次從一句話的需求出發(fā)實際上要解決的是“快速定位占用源頭并安全清理”這一整套動作。這篇文章就把我在 Linux 服務器上查看文件夾占用空間、定位大目錄、排查空間異常的所有常用方法和踩坑記錄整理出來適合剛接觸運維的開發(fā)者也適合需要臨時上服務器救火的業(yè)務同學。1. 需求拆解一句話標題背后其實是三件事1.1 你真正想知道的是什么“查看某文件夾占用空間大小”這句話實在太籠統(tǒng)了實踐里它至少包含三層需求第一層是想知道某個目錄總共有多大比如 /var 已經(jīng) 50G 了這通常是看一眼就能回答的問題。第二層是定位這個大目錄里到底哪個子目錄或者哪個文件最占空間這個才是排查磁盤告警的真正核心。如果只是知道 /var 有 50G 卻不知道里面是什么等于沒查。第三層是判斷整個磁盤分區(qū)還剩多少可用空間因為有時候你看到的文件加起來并不大磁盤卻滿了這說明空間掛在別的地方比如其他目錄、隱藏文件、或者被刪除但仍被進程占用的文件。很多人一上來就敲du -sh 目錄名這個命令沒問題但它只能回答第一層問題。而真正難處理的是第二層和第三層這也是這篇文章的重點。1.2 兩條技術路線到底怎么選Linux 下查看空間占用有兩條完全不同的技術路線必須區(qū)分清楚。dudisk usage走的是目錄樹它從指定路徑開始逐個遍歷目錄項和文件累加每個文件實際占用的磁盤塊大小。它回答的問題是“這個文件夾里的所有內(nèi)容加起來占了多少磁盤空間”。dfdisk free走的是文件系統(tǒng)元數(shù)據(jù)直接讀取超級塊里的統(tǒng)計信息打印整個文件系統(tǒng)分區(qū)的總?cè)萘?、已用量和剩余量。它回答的問題是“這個分區(qū)還有多少空間可用”。兩者看似差不多實際經(jīng)常不一致原因就是統(tǒng)計維度不同。后面第 4 節(jié)我會專門展開講兩者不一致的幾種情況。先記住一個原則看單個目錄或者整個目錄樹的大小用du看磁盤分區(qū)整體剩余空間用df兩個必須配合使用。2. du 命令的核心參數(shù)與統(tǒng)計原理2.1 為什么 du 顯示的數(shù)值和 ls 的 size 不一樣這是我被問過最多的問題。在某個目錄里執(zhí)行l(wèi)s -lh看到一個文件顯示 100M但du -h 文件名卻顯示 104M 或者 98M很多人就懵了。原因在于ls -l顯示的是文件的“邏輯大小”也就是文件內(nèi)容的實際字節(jié)數(shù)而du統(tǒng)計的是文件在磁盤上“物理占用”的塊大小。文件系統(tǒng)是按照塊block來分配空間的一個塊通常是 4K。哪怕一個文件只有 1 個字節(jié)它也要占一個完整的 4K 塊du就會按 4K 來算。反過來如果是稀疏文件文件中間有大量空洞文件邏輯大小可能很大但實際分配的塊很少du統(tǒng)計出來的就比ls -l小很多。所以當你在博文或者文檔里看到du的結(jié)果和網(wǎng)頁上某個文件標注的大小不一樣不要覺得是命令用錯了這本身就是 Linux 文件系統(tǒng)分配機制的正常表現(xiàn)。你要做的是理解du統(tǒng)計的是“占磁盤空間”而不是“文件有多長”。2.2 必須記住的常用參數(shù)組合du的參數(shù)不少但日常排查其實只需要記住我下面列出的這幾個組合基本覆蓋了絕大多數(shù)場景。先給出最基本的一些參數(shù)說明參數(shù)作用使用場景-hhuman-readable以 K/M/G 為單位顯示幾乎所有場景-ssummarize只顯示指定目錄的總大小精確查看單個目錄-aall同時顯示目錄和文件需要看到具體文件時--max-depthN最多遞歸 N 層子目錄定位大目錄時最常用-c最后顯示所有目錄的總計統(tǒng)計多個目錄的和--excludePATTERN排除匹配模式的文件/目錄跳過日志或緩存目錄-x不跨越文件系統(tǒng)邊界避免統(tǒng)計到掛載的其他磁盤用的時候我建議直接按這樣的組合去記# 查看單個目錄總大小 du -sh /var # 查看當前目錄下所有一級子目錄的大小并排序 du -h --max-depth1 . | sort -hr # 如果想連文件一起顯示 du -ah --max-depth1 . | sort -hr # 統(tǒng)計多個指定目錄的總大小 du -sch /var/log /tmp /home注意du -h --max-depth1和du -sh *執(zhí)行結(jié)果其實差不多但前者更規(guī)范。du -sh *的問題在于它會把 command-line arguments 展開后才去統(tǒng)計如果當前目錄下文件特別多會有一瞬間的參數(shù)列表過長風險而且用*會漏掉隱藏文件這一點后面還要單獨說。2.3 用 sort 把結(jié)果按大小排序du默認輸出是不排序的目錄一多根本看不出來誰最占空間。所以必須和sort配合。du -h --max-depth1 /var 2/dev/null | sort -hr | head -20sort -hr的意思是按人類可讀的數(shù)值4K、100M、2G 這種格式逆序排序。這里有一個關鍵細節(jié)如果你直接用sort -h它會正確識別 K/M/G 單位的大小關系但如果用sort -r普通排序會把帶 G 的和帶 M 的混在一起排錯。我第一次用就犯過這個錯誤以為 10M 比 2G 大其實是把字符串當字典序排了。另外2/dev/null一定要加上因為du在遍歷過程中遇到?jīng)]有權限的目錄會輸出du: cannot read directory之類的報錯這些錯誤會混進輸出里干擾排序。3. 一套可以直接抄作業(yè)的排查流程3.1 第一步用 df 判斷整體情況所有磁盤空間排查都應該先從df開始而不是直接du。因為你需要先搞清楚到底是哪個分區(qū)滿了確認優(yōu)先級。df -h df -idf -h是查看磁盤容量使用率df -i是查看 inode 使用率。我多次遇到一種情況磁盤容量顯示還有 20G 空余但業(yè)務報錯“No space left on device”這時候df -i會發(fā)現(xiàn) inode 已經(jīng) 100% 占滿了。inode 是文件系統(tǒng)里記錄文件元數(shù)據(jù)的索引節(jié)點小文件特別多的目錄比如郵件隊列、緩存目錄、容器鏡像層最容易把 inode 耗盡。判斷順序應該是先df -h看容量再用df -i查 inode兩個都正常再繼續(xù)往下走。3.2 第二步從根目錄逐層下鉆定位整體空間都確認了之后進入定位大目錄的環(huán)節(jié)。第一次先看根目錄下所有一級目錄的大小因為一般不會一上來就有針對性的目錄猜測先看全局狀態(tài)du -h --max-depth1 / 2/dev/null | sort -hr | head -20輸出結(jié)果里通常會看到/var、/usr、/home里有一個特別大于是進入對應目錄再次執(zhí)行du -h --max-depth1 /var 2/dev/null | sort -hr | head -20就這樣一層一層往下鉆。這個過程我自己的經(jīng)驗是“每次只看一級目錄”不要試圖一步到位直接du -h --max-depth3 /那樣的輸出會非常長且雜亂因為最大深度 3 會把幾百個目錄全部列出來。你反而會被撲面而來的信息淹沒沒法判斷重點。逐層下鉆雖然多敲幾次命令但每次都只需要關注 top 10 的目錄思路非常清晰。到某一層出現(xiàn)大量平鋪的文件時再改用du -ah把文件也顯示出來直接定位到具體文件du -ah --max-depth2 /var/log 2/dev/null | sort -hr | head -203.3 第三步千萬別漏了隱藏文件和掛載點這一步是新人最容易踩的坑。du -h --max-depth1統(tǒng)計的是當前目錄下所有條目但如果你用du -sh *這種方式shell 的*默認不會匹配以.開頭的隱藏文件和目錄。比如/home下用戶的.cache目錄、/var/spool下可能有隱藏目錄這些全都會被漏掉。結(jié)果就是排查了半天明明某文件夾占用很高但統(tǒng)計結(jié)果卻對不上。所以我特別建議你執(zhí)行下面的互補命令專門檢查隱藏目錄的大小du -h --max-depth1 /要查的目錄 2/dev/null | sort -hr du -sh /要查的目錄/.[!.]* 2/dev/null第二條命令里的.[!.]*匹配的是以點開頭但第二個字符不是點的條目也就是隱藏目錄和隱藏文件但不包括.和..。它的作用就是專門揪出被普通通配符漏掉的隱藏文件。遇到過最典型的一次某臺服務器/root下面看起來什么都沒有結(jié)果du -sh /root/.*一查一個.maildump隱藏目錄占了 80G就是某個異常任務一直在往里面寫臨時文件。另外還要注意掛載點。du默認會穿越文件系統(tǒng)邊界繼續(xù)統(tǒng)計比如某個目錄下面掛了一個 NFS 或者另一塊數(shù)據(jù)盤du會把掛載點里的內(nèi)容也算到當前目錄頭上導致這里顯示的占用不代表它占用了你當前這個分區(qū)。這時候要加-x參數(shù)跳過其他文件系統(tǒng)du -x -h --max-depth1 / 2/dev/null | sort -hr3.4 找到大文件之后的清理驗證定位到具體的大文件之后不要沖動地直接rm。先確認這個文件的用途特別是日志文件有些進程會持續(xù)寫入你手動刪掉了但進程還持有文件句柄磁盤空間并不會釋放。確認要清理之后在刪除前記錄一下刪前后空間情況df -h /var du -sh /var/log rm -f /var/log/某個大日志 df -h /var du -sh /var/log前后對比一下能確認空間是否真的釋放了若刪除了但df顯示可用空間沒有增長則說明文件被某個進程占用了具體處理方式我在第 4 節(jié)細說。4. du 和 df 顯示不一致的深層原因4.1 文件被刪除但進程仍持有句柄這是生產(chǎn)環(huán)境最經(jīng)典的問題你在某個目錄里看到一個大文件執(zhí)行rm刪掉了目錄空間統(tǒng)計也正常了但df -h一看整個分區(qū)的可用空間沒有任何變化還是滿的。原因在于 Linux 的文件刪除機制。rm刪除的只是對文件的一個目錄項鏈接但如果有一個進程正打開著這個文件文件仍然存在于磁盤上只是不再有路徑可以訪問到它。那個進程不關閉這個文件句柄空間就永遠不會釋放。遇到這種情況用lsof找出持有已刪除文件的進程lsof L1L1會列出“鏈接數(shù)小于 1”的文件也就是被刪除但仍被打開的文件。輸出里找到進程 PID 后要么重啟該進程要么讓服務方重新加載配置文件。如果是一臺臨時服務器急著要空間確認進程可以重啟就直接重啟服務。千萬不要為了釋放空間去kill -9一個你不知道是什么的進程先看lsof輸出里進程名再判斷能不能重啟。文件被刪除但空間不釋放的問題在日志類服務、數(shù)據(jù)庫、消息隊列上特別常見尤其是一些不會自動logrotate的進程日志被手動刪了但 fd 還握著磁盤空間憑空消失幾十 G。4.2 根文件系統(tǒng)保留塊被算進“已用”里我用df -h查看/分區(qū)時經(jīng)??吹揭呀?jīng)用了 95% 以上但du -h --max-depth1 /把所有目錄加起來也沒那么大中間就差了那么幾個 G。這里面的主要原因之一是 ext4 等文件系統(tǒng)默認會預留 5% 的空間給 root 用戶用來防止系統(tǒng)因為空間耗盡而無法正常啟動。這 5% 的保留塊在df里是算在“已用”里面的但du是完全感知不到這部分空間的。所以當你的根分區(qū)使用率顯示 97%但du統(tǒng)計的總和只有 92% 左右不用太驚訝這 5% 是文件系統(tǒng)層面的保留。如果你確實需要釋放這部分空間用下面的命令調(diào)整保留比例tune2fs -m 1 /dev/sda1但不建議在生產(chǎn)服務器上隨意調(diào)整尤其是根分區(qū)。這 5% 空間在極端情況下是救命的數(shù)據(jù)盤的保留比例調(diào)低一些倒是無妨。4.3 掛載點、硬鏈接和稀疏文件的影響再補充幾種導致du和df不一致的情況。掛載點嵌套是最常見的誤判來源。你明明清理了/data下的一堆文件/data所在分區(qū)空間卻沒變化那是因為/data其實是一個獨立掛載的分區(qū)你在/data下清理的文件并不屬于你df -h時看的那個分區(qū)。硬鏈接會讓du重復計算。同一個 inode 的同一個文件被硬鏈接到多個目錄路徑下du在遍歷每個路徑時都會把這份文件的大小加一遍導致總體統(tǒng)計結(jié)果比真實占用偏大。統(tǒng)計時若想按真實文件系統(tǒng)占用算可以用-P參數(shù)不跟隨符號鏈接但硬鏈接本身造成的重復統(tǒng)計沒有參數(shù)能完全規(guī)避。還有一個容易忽略的是 NFS 掛載目錄的st_nlink顯示異常。某些網(wǎng)絡文件系統(tǒng)上du會遍歷到無窮無盡的目錄循環(huán)直接導致du卡死甚至報錯所以遇到 NFS 目錄建議用du -x或者直接跳過不要硬跑。稀疏文件的例子一個 10G 的文件實際磁盤占用可能只有 1G因為文件內(nèi)部有很多空洞。du統(tǒng)計的是實際分配的塊ls -lh顯示的是邏輯大小差距巨大是正常的你不要疑惑是不是統(tǒng)計錯了。5. 更高效的工具與實用技巧5.1 用 ncdu 做交互式排查如果你完全不想一層層敲命令或者排查的是一個幾百 G 的大目錄用ncdu體驗會好很多。它是基于ncurses的交互式磁盤占用分析工具安裝特別簡單# Debian/Ubuntu apt install ncdu # CentOS/RHEL yum install ncdu然后直接執(zhí)行ncdu /var它會先把目錄結(jié)構(gòu)掃描一遍然后進入交互界面直接按大小排序展示所有子目錄和文件。你可以用上下鍵移動按回車進入子目錄按d刪除當前項按g重新掃描。這個工具在做臨時上臺機器快速排查時確實很順手所見即所得。不過ncdu也有短板它默認不會掃描掛載點之外的文件系統(tǒng)對于跨分區(qū)的目錄結(jié)構(gòu)你需要在啟動時加參數(shù)或者分多次掃描。另外它掃描大目錄時耗時會比直接du -h --max-depth1長一些因為它是完整遍歷所有子目錄。如果只是想快速看一級目錄大小我還是推薦命令行du sort的組合。5.2 常用別名與一鍵腳本排查的空間問題時命令敲多了就希望能少敲幾個字。我習慣在自己的 shell 配置里加幾個別名這里直接分享給你alias duddu -h --max-depth1 2/dev/null | sort -hr alias dufdu -sh 2/dev/null alias dfhdf -hT這樣每次排查只需執(zhí)行dud /var dud /home dfh如果你經(jīng)常需要在不同服務器上處理磁盤問題我建議寫一個簡單的一鍵定位腳本比如下面這樣的bigfind.sh#!/bin/bash # 使用方式./bigfind.sh /var TARGET${1:-/} du -h --max-depth1 $TARGET 2/dev/null | sort -hr | head -20 echo 文件系統(tǒng)使用情況 df -hT $TARGET這個腳本會先列出指定目錄下最大的 20 個子目錄再顯示對應分區(qū)的整體情況一次執(zhí)行就能同時完成“目錄內(nèi)定位”和“分區(qū)整體判斷”兩個任務。5.3 用 --exclude 和 -t 過濾掉不關心的目錄在實際的日志服務器上最容易占空間的通常是固定路徑比如/var/log、/tmp、某個應用緩存目錄。如果你只是想找出那些不常規(guī)的大目錄可以用--exclude把已知的大目錄跳過du -h --max-depth1 / --exclude/proc --exclude/sys --exclude/data --exclude/var/cache 2/dev/null | sort -hr | head -20這樣可以把旅行加速的重點放在還沒排查過的目錄上。另外還有一個-t參數(shù)只顯示大于指定大小的目錄比如du -h -t 1G / 2/dev/null | sort -hr只輸出超過 1G 的目錄對某些磁盤容量超大的服務器來說也能減少噪音。5.4 針對日志目錄的特殊排查技巧日志目錄是磁盤占用的重災區(qū)。它的特點是子目錄多、文件名帶日期、且單個文件可能動輒幾十 G。排查日志目錄時我更喜歡直接列文件而不是列目錄ls -lhS /var/log/*.logls -lhS可以直接按文件大小從大到小列出所有日志文件比先du再逐個看文件名更直觀。但在日志目錄里發(fā)現(xiàn)有超大日志文件時并不建議直接對這個目錄做rm操作。正確做法是確認該日志對應的服務優(yōu)先考慮# 查看日志服務是否支持配置 max size 和 rotation # 例如 rsyslog 的配置文件 /etc/logrotate.d/ # 針對某個具體 log 可以手動觸發(fā) logrotate logrotate -f /etc/logrotate.d/rsysloglogrotate可以在保留最新日志的前提下壓縮和輪轉(zhuǎn)歷史日志避免磁盤空間被一個單調(diào)增長的日志文件徹底占滿。在日志文件還在增長的場景下手動rm只是治標不治本重點是把輪轉(zhuǎn)策略配置好。6. 常見問題速查表與實操心得6.1 高頻問題與解決方案現(xiàn)象可能原因排查命令處理建議du -sh 某目錄結(jié)果很大但df -h分區(qū)明明還很空該目錄在掛載的其他分區(qū)上或存在跨文件系統(tǒng)的目錄df -h 目錄名、mount | grep 目錄名確認目錄所在分區(qū)使用du -x跳過其他文件系統(tǒng)刪除了大文件df -h可用空間沒增加文件被進程持有句柄lsof L1重啟對應進程或讓進程關閉文件句柄磁盤容量顯示沒滿但系統(tǒng)報 No space left on deviceinode 耗盡df -i統(tǒng)計小文件數(shù)量遷移或清理大量小文件所在目錄du統(tǒng)計所有目錄總和比df已用量小很多文件系統(tǒng)保留塊、已刪除未釋放空間、隱藏文件被遺漏tune2fs -l /dev/sdaX、du -sh /.*根據(jù)情況處理刪除隱藏目錄或檢查保留塊ncdu顯示某目錄非常大但du -sh顯示不大稀疏文件或符號鏈接循環(huán)ls -lh、ls -l檢查 inode根據(jù)文件類型對應處理/proc、/sys目錄不能直接 du虛擬文件系統(tǒng)排除這兩個目錄正?,F(xiàn)象不要嘗試清理6.2 我踩過的一些坑和總結(jié)的經(jīng)驗踩坑經(jīng)歷先說說。有次幫同事排查一臺編譯機磁盤 100% 滿所有常規(guī)命令都查不出哪里大。我du --max-depth1逐層看每個目錄都 正常但加起來才用了不到 60%。最后用lsof L1才發(fā)現(xiàn)是一個被刪除的編譯臨時文件被某個還開著的進程一直占住足足 40G。從那以后我排查磁盤問題一定會把lsof L1納入標準流程而不是最后實在查不出來才想到。還有一次是磁盤沒滿但某個服務頻繁宕機查了半天才發(fā)現(xiàn)是/var目錄下有一個隱藏的.cache目錄被程序無限寫入小文件把 inode 耗盡了。所以我現(xiàn)在df -i也基本是必查項。給幾點個人的實操建議第一沒有把握不要刪文件。寧可先du確認內(nèi)容看到路徑名里帶tmp、cache、log等明顯標識再根據(jù)自己對業(yè)務的了解決定是否清理。第二清理大日志時推薦truncate而不是rm。對正在寫入的日志文件執(zhí)行truncate -s 0 文件名能把文件清空但不改變文件路徑和打開的文件句柄相比rm后重新創(chuàng)建要優(yōu)雅得多尤其是服務進程并不支持動態(tài)重新打開日志文件的情況。第三謹慎使用rm -rf。每次敲rm -rf之前我都強制自己先cd到絕對路徑再操作不要用相對路徑不要帶通配符的模糊匹配去刪。磁盤滿了很急但越急越要冷靜。第四如果你的服務器有監(jiān)控系統(tǒng)建議對根分區(qū)和幾個常用分區(qū)設置空間使用率告警例如 85% 警告、92% 緊急避免每次都是被動的“磁盤滿了才發(fā)現(xiàn)”。這個話題說到底就是把du、df、lsof這三板斧用好再配合一層層下鉆的排查思路。我不太習慣寫那種華麗的技術總結(jié)只分享一個最樸素的心得——遇到磁盤問題別慌先看分區(qū)再看目錄最后看文件句柄按這個順序走大多數(shù)問題都能很快定位。下一次你再看到磁盤告警的時候直接按這篇文章的流程操作就行。