我做过几年服务器运维,印象最深的一次事故不是服务崩溃,也不是数据丢失,而是一个再普通不过的提示:No space left on device。那台机器上跑着业务数据库,日志和临时文件把磁盘写满了,应用直接挂掉。排查到最后,罪魁祸首是一份没人清理的调试日志,滚动了几十天攒了几十个G。从那天起,我养成了定期看磁盘的习惯,也把常用的查看和清理命令整理成了一套固定打法。这篇博文就围绕 Linux 服务器的磁盘查看与清理,把常用命令、判断逻辑和踩过的坑一次性讲透,适合刚接手服务器运维的新人,也适合想把手头命令用得更明白的开发者。
磁盘管理这东西,看着简单,翻来覆去就那么几个命令,但真到现场救火的时候,命令顺序、参数选择和判断依据才是关键。比如 df 显示满了,你第一反应该去查哪里?日志清理用 find 直接删还是用 logrotate 处理?删完文件空间没释放,又是怎么回事?这些细节光靠记命令是记不住的,得理解了底层原理才能真正用得顺手。这篇文章会把原理、命令、实战过程串起来讲,保证你看完能直接用起来。
1. 磁盘满会对服务器造成什么影响
很多人以为磁盘满了最多就是写不进新文件,影响不大。实际上,磁盘满导致的连锁反应非常可怕,尤其是对数据库、消息队列这类依赖文件落盘的服务。数据库在运行过程中要写 redo log、binlog、临时排序文件,一旦磁盘没有可用空间,写入操作会直接失败,轻则事务回滚,重则数据库进程异常退出,甚至因为无法写 redo 导致崩溃恢复失败,整个实例起不来。我见过一个案例,一台 MySQL 从库磁盘被慢查询日志撑满,从库 IO 线程直接停止,主从延迟飙到几个小时,最后只能重建从库。所以磁盘空间不是"等满了再清"的问题,而是必须日常监控、提前干预。
除了数据库,日志服务、监控采集器、消息队列这类常驻进程对磁盘空间同样敏感。Kafka 的日志段文件写满磁盘后,broker 会自动下线,分区副本失去 leader,整个集群的读写都会报错。监控脚本写日志不轮转,几天就能把机器打满。更隐蔽的是,磁盘满还会影响系统本身的稳定性。Linux 的很多临时文件和匿名内存页都要依赖 swap 和 /tmp 目录,写不进去会导致进程卡死,甚至连 ssh 登录都会异常缓慢。所以磁盘空间是服务器稳定性的地基,地基塌了,上面什么都白搭。
从运维角度看,磁盘查看和清理的核心目标其实有三个:一是搞清楚当前磁盘的使用状况,知道"哪里满了、谁占了、增长趋势如何";二是安全地释放空间,避免误删生产数据;三是建立一套日常巡检和自动清理机制,让磁盘永远不成为突发事故点。接下来的内容就围绕这三件事展开。
2. 磁盘查看命令:先定位问题再动手
2.1 用 df 看全局:哪块分区快满了
df 是 Disk Free 的缩写,作用是查看文件系统的总容量、已用空间、可用空间和挂载点。我几乎每次排查磁盘问题都先用它,因为它能在一秒内告诉你"问题是不是出在磁盘上、是哪块盘的事"。
# 以人类可读的格式查看所有挂载点 df -h # 查看指定目录所在文件系统的使用情况 df -h /var/lib/mysql # 查看 inode 使用情况(容易被忽略) df -idf 输出里需要重点看 Used% 那一列,超过 80% 就该警惕了,超过 90% 基本属于危险区。这里有个小知识点:df 显示的是文件系统级别的使用量,不是目录级别的。比如你有多个分区,/ 满了但 /data 还有空间,那你往 /data 写东西不会有问题,但系统日志、临时文件如果写在 / 下的 /var、/tmp,照样会把根分区撑满。所以排查时先确定"满的是哪个挂载点",再针对那个挂载点做深入分析。
再说说 df -i。inode 是文件系统用来记录文件元数据的结构,每个文件或目录至少占用一个 inode。如果文件数量超多,会出现 inode 耗尽的情况,df -h 明明显示还有几百 G,但系统告诉你"no space left"。这种问题常见于小文件特别多的目录,比如邮件队列、缓存目录、消息队列的分区文件。我踩过一次:一个 RabbitMQ 节点因为消息积累产生了上亿个小文件,磁盘剩余 200G,但 inode 满了,节点直接拒绝接收新消息。所以日常巡检最好把 df -h 和 df -i 都加上。
2.2 用 du 精确计算:找出到底谁占了大头
df 告诉你文件系统满了,du 则告诉你目录和文件各自占用了多少空间。两者配合,才能完成"定位"的闭环。du 的原理是遍历目录树,统计每个文件的大小并汇总,所以对大目录执行会比较慢,生产环境建议控制扫描深度。
# 查看当前目录下所有子目录和文件的大小(一层) du -h --max-depth=1 /var | sort -hr # 仅统计指定目录的总大小 du -sh /var/log # 找到 / 下占用最大的前 10 个目录 du -x -h --max-depth=1 / 2>/dev/null | sort -hr | head -10这里有几个实用细节。sort -hr 的作用是按人类可读的容量大小倒序排列,避免你用 sort -n 时看到 10G 排在 9M 前面的尴尬。du -x 表示不跨文件系统统计,这很重要,因为 /proc、/sys、/dev 这些虚拟文件系统如果被 du 扫进去,你会看到一堆奇怪的数字,而且扫描速度极慢。2>/dev/null 是为了屏蔽权限不足产生的错误输出,因为有些目录比如 /root 普通用户根本进不去,报错信息会干扰结果。
单看 du 输出还不够,时间一长目录层级深了,一层层看很费劲。我习惯在排查阶段先写一条命令,直接找出指定目录下最大的十几个文件:
# 找出 / 下所有大于 500M 的普通文件 find / -type f -size +500M -exec ls -lh {} \; 2>/dev/null | awk '{print $5, $9}' | sort -hr | head -20这条命令会把超过 500M 的文件挨个列出来,适合快速发现那些"巨无霸"文件。注意 find 遍历整盘会比较耗时,内网机器倒还好,公网高负载机器建议在业务低峰期执行,或者配合 timeout 限制一下时间。
2.3 用 iostat 看 IO:磁盘满和 IO 忙是两回事
经常有人把"磁盘满了"和"磁盘很忙"混为一谈,这是两个维度的问题。磁盘满说的是空间耗尽,磁盘忙说的是读写 IO 压力大。有时候 df 显示空间充足,但业务依旧卡得要命,这时候该看的是 iostat。
# 安装 sysstat 包后执行 iostat -x 1 5iostat -x 输出里重点看 %util、rkb/s、wkb/s、await 这些字段。%util 持续接近 100% 说明磁盘接近饱和,await 数值过高说明 IO 请求排队严重。不过要注意,机械盘和 SSD 的正常指标不一样,机械盘 %util 维持在 80% 以上基本就满载了,SSD 因为支持并发,%util 高不一定代表性能瓶颈,得结合读写延迟一起判断。
磁盘空间和 IO 两个维度都要看,是因为它们会互相影响。比如某台机器的日志疯狂刷写,短时间内不会把盘写满,但会让磁盘 IO 飙升,拖慢所有业务的读写;反过来磁盘快满时,文件系统为了分配块可能要频繁做碎片整理和元数据更新,也会加剧 IO 延迟。所以排查服务器"慢"的问题时,df 和 iostat 我通常连着一起看。
2.4 用 lsof 和 find 揪出隐藏的"磁盘黑洞"
有时候 du 扫完发现,明明删了一堆文件,df 显示可用空间却一点没变,这时候你大概率遇到"文件被删除但进程仍然占用"的情况。这是运维面试里常考的一个知识点,Linux 下进程打开的文件,即使从目录里 unlink 掉了,只要进程没有关闭文件描述符,占用的磁盘块就不会释放。
# 找出正在占用已删除文件(deleted)的进程 lsof | grep deleted # 只看某个目录下处于 deleted 状态的文件 lsof +L1 /var/loglsof +L1 表示列出 link count 为 0 的文件,也就是那些已经被删除但还被进程持有的文件。看到输出后,你需要根据 PID 去判断是哪个进程,然后决定是重启进程还是 reload 服务。还有一种情况是进程本身没删文件,但某些无良程序用临时文件占着空间没清,这就要靠 find 按大小和时间去搜了。
find 在磁盘清理中的用途非常多,除了搜大文件,还能按修改时间找老日志:
# 找出 /var/log 下 7 天前修改过且大于 100M 的文件 find /var/log -type f -mtime +7 -size +100M -ls # 找出 /tmp 下超过 3 天没动过的文件 find /tmp -type f -atime +3 -ls需要注意的是,find 按 -mtime 找文件时用的是"天"粒度,-mtime +7 表示超过 7 天,不是 168 小时整,精度要求高时可以用 -mmin 按分钟定位。
3. 磁盘清理命令:安全的释放空间才是关键
3.1 日志类文件清理:优雅轮转优先,暴力删除兜底
服务器上最占磁盘的空间,十有八九是日志。Java 应用、Nginx、数据库、系统本身都在写日志,如果没人管,日志文件会一路涨下去。处理日志我建议的顺序是:先看有没有 logrotate 配置,没有就考虑压缩和分割,最后才用 find 删除。
logrotate 是 Linux 自带的日志轮转工具,配合 cron 每天执行一次,能让日志按大小或天数滚动,并保留指定份数。一个典型的 Nginx 日志配置长这样:
cat /etc/logrotate.d/nginx /var/log/nginx/*.log { daily rotate 14 compress delaycompress missingok notifempty create 640 nginx adm sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 $(cat /var/run/nginx.pid) fi endscript }这里的 daily 表示每天轮转一次,rotate 14 表示保留 14 份日志,compress 表示对旧日志做 gzip 压缩,delaycompress 和 postrotate 是配合 Nginx 重新打开日志文件的处理。配置完可以用logrotate -d /etc/logrotate.d/nginx做一次预演,确认不会出错再手动强制执行。
如果日志没有配置轮转,文件已经涨得很大了,直接清空它往往比删除更稳妥。直接 rm 掉一个正在被进程写入的日志文件,磁盘空间会释放,但进程的文件描述符还指向那个被删的 inode,日志会继续写入一个已删除/不可见的空间,直到进程重启。更麻烦的是,有些进程在写入时会检测文件是否存在,看不到文件就报错。所以正确做法是用 truncate 或: >截断文件:
# 清空日志内容但保持文件句柄不变 truncate -s 0 /var/log/nginx/error.log # 或者用 shell 重定向方式 : > /var/log/nginx/error.log至于历史日志的批量清理,可以这样按时间和大小过滤后删除:
# 删除 /var/log 下 30 天前、文件名带 .log 的压缩日志 find /var/log -name "*.log.*" -type f -mtime +30 -delete # 只列出要删的文件,确认无误后再去掉 -delete find /var/log -name "*.log.*" -type f -mtime +30 -exec ls -lh {} \;在删生产环境日志前,我永远会先跑一遍只列不删的命令,把结果刷一遍屏确认没有任何重要文件混在里面,再真正执行。这一步看着多余,实际上能救命的次数比你想的多。
3.2 包管理与缓存清理:apt 和 yum 都很能攒垃圾
长期运行的 Linux 服务器,包管理器会留下大量缓存和不再需要的软件包依赖,这些是常见的清理对象。
Debian 系(Ubuntu、Debian)常用命令:
# 清理 apt 下载缓存 apt clean # 清理已卸载软件的过期缓存 apt autoclean # 自动移除不再需要的依赖包 apt autoremove --purge -y # 查看哪些缓存占用最多 du -h /var/cache/aptRedHat 系(CentOS、Rocky、AlmaLinux)常用命令:
# 清理 yum 缓存 yum clean all # 清理旧内核之外的包缓存(注意版本差异) dnf clean all # 删除旧内核,保留当前版本 package-cleanup --oldkernels --count=2这里要提醒一句,apt autoremove 和 package-cleanup 这类命令有自动删除依赖的能力,虽然大多数时候安全,但偶尔会因为依赖关系判断出错,把某些软件运行需要的库也一起干掉。所以我在执行之前会先加--dry-run或--assume-no看看它会删哪些包,确认没有业务依赖再真正操作。
包管理器之外,Docker 运行时产生的镜像、容器、构建缓存才是真正的大户。常年跑 Docker 的服务器,甚至会出现镜像垃圾占掉了几十 G 的情况。下面是几组高频率用的命令:
# 查看 docker 各对象的磁盘占用 docker system df # 一键清理悬空镜像、停止的容器、无用的网络和构建缓存 docker system prune -af --volumesdocker system prune 加-af只清理 "dangling" 的镜像,不会动正在使用的镜像,相对安全。--volumes会连未使用的数据卷一起清掉,这个要特别小心,因为某些容器在重建过程中,数据卷可能临时表现为"未使用",一旦被清就找不回来了。清理 Docker 卷前,建议先docker volume ls -f dangling=true看一眼,再决定要不要连卷一起清。
3.3 journald 日志清理:systemd 时代的隐藏大户
很多人习惯用journalctl查日志,却不知道 journald 默认会把日志写到 /var/log/journal 目录,而且默认配置下 journal 文件只增不减,磁盘占用很容易从几十 M 涨到几个 G。我接手过一台跑了半年的机器,journal 目录直接占了 30G。
清理 journald 日志,不建议直接 rm 掉 journal 目录下的文件,那样容易破坏 journal 索引。正确做法是通过 journalctl 的命令来清理:
# 保留最近 3 天的日志,其余删除 journalctl --vacuum-time=3d # 保留最新 200M 的日志目录 journalctl --vacuum-size=200M # 仅保留最近 2000 条日志 journalctl --vacuum-files=10不过治本的方法是去调整 /etc/systemd/journald.conf 里的 SystemMaxUse 参数,把日志体积限制在固定范围。比如:
SystemMaxUse=200M MaxRetentionSec=3day改完配置后执行systemctl restart systemd-journald生效。这样可以防止日志再次无节制增长,比每次手动清要省心得多。
3.4 清理 temp 与 core dump:容易被忽略的临时文件
/tmp 目录里的临时文件,理论上系统重启会清,但服务器可不是天天重启的。长时间运行的机器,/tmp 里攒下的临时文件、安装包、解压缓存,也能堆积不少空间。很多程序运行时会往 /tmp 写临时文件,比如 Java 的 java.io.tmpdir、PHP 的 session 文件、编译器的中间产物。清理 /tmp 比较稳妥的方式是找 7 天或 30 天以上的旧文件处理:
# 找出 /tmp 下 7 天前修改过的所有文件 find /tmp -type f -mtime +7 -delete还有一类文件叫 core dump,就是程序崩溃时内核转储的核心转储文件,通常以 core.xxx 形态出现在进程工作目录或系统指定目录下。一个 core 文件动辄几百 M 甚至几个 G,如果程序频繁崩溃,core dump 会迅速耗尽磁盘。用 sysctl 查看当前配置:
# 查看 core dump 生成方式 sysctl -a | grep core_pattern如果 core 文件都堆积在固定目录,定期清理即可。更合理的方式是在生产环境直接限制或关闭 core dump,比如在 /etc/security/limits.conf 里设置:
* hard core 0关于要不要关闭 core dump 存在争议,调试时需要它,生产环境一般不建议关,但可以用 systemd-coredump 的配置把 core 文件大小和保留期限都限制住,避免变成磁盘炸弹。这里建议根据业务需要权衡,别一刀切。
4. 实操演示:一次完整的磁盘排查与清理过程
光讲命令容易散,我拿一次真实场景里的排查过程做个演示。假设一台运行 Nginx + Java 应用的服务器,监控报警说根分区使用率超过 90%,我需要在不影响业务的情况下安全地把使用率降到 70% 以下。
第一步,确认现状。登录服务器后先跑 df -h 和 df -i,看清哪些挂载点满了、inode 是否有风险。假设输出显示/已用 92%,/data 还有大量空间,那重点就锁定在根分区,不用去管 /data。
第二步,扫描根目录占用。用 du 从根开始往下逐层定位:
cd / du -x -h --max-depth=1 2>/dev/null | sort -hr | head -10这一步的输出通常会直接暴露出大块头,比如 /var 占了 40G,/home 占了 30G,/usr 占了 20G。接下来钻到 /var 里继续往下定位:
du -x -h --max-depth=1 /var 2>/dev/null | sort -hr | head -10第三步,当定位到 /var/log 下的某个具体目录后,用 find 看看里面有哪些过期大文件:
find /var/log -type f -size +50M -ls find /var/log -name "*.gz" -type f -mtime +30 -exec ls -lh {} \;这时候如果发现有一堆 30 天前的压缩日志,就可以执行清理。清理动作我习惯分两步:先列后删,先告诉自己要删哪些,再次一确认。
第四步,清理完成后再跑 df -h 验证,确保使用率降下来了。这个循环看起来简单,但实际执行中会碰到一个很典型的问题:文件删了,可用空间没涨。
第五步,排查空间未释放的原因。用 lsof 找已删除但仍被占用的文件:
lsof | grep deleted比如输出里有nginx (deleted),说明 Nginx 还在写旧日志文件的 inode,空间被它拽着不放。此时要么nginx -s reload让它重新打开日志文件,要么直接 restart 进程,问题就解决了。
这个完整的排查流程,从 df 到 du 到 find 到 lsof,每一步都有明确的判断依据,不会无头苍蝇一样乱删。用同样的打法,不管磁盘问题出在日志、缓存还是临时文件,都能很快定位。我平时给团队培训也按这个流程讲,新手照着做基本能处理 90% 的磁盘告警。
5. 常见问题与排查技巧实录
5.1 为什么 df 显示已满,du 加起来却对不上
这是新手问得最多的一个问题。df 统计的是文件系统块的使用情况,包括元数据、保留块、已删除但未释放的块;而 du 统计的是目录树里实际文件的大小总和。两者本来就不该完全相等。遇到明显差异,先查 deleted 文件占用,再查是否有挂载点覆盖了原目录。比如你有一个目录 /data,里面原来存了很多文件,后来你把一块新盘挂载到了 /data,那么新盘上的文件会出现在 du 里,但旧目录占用的空间被"/" 挂在底下,df / 才会显示,df -h /data 看不到。这种情况最容易让人觉得"空间对不上"。
5.2 truncate 和 rm 到底该用哪个
简单说,普通归档日志用 rm,正在被进程写入的日志用 truncate。rm 是删文件,释放 inode 和块;truncate 是改变文件大小,保留 inode 和文件句柄。服务端日志文件正在写入时,rm 之后进程不会立刻感知,反而可能导致日志继续写入已删除的 inode,造成空间不释放且看不到日志内容。truncate -s 0 则能清空内容,同时保证进程后续写入正常。但如果业务依赖日志文件的存在性来触发某些逻辑,truncate 也可能引发问题,比如某些程序打开文件后按 offset 写入,文件被截断后 offset 并不会复位,可能出现奇怪的写入行为。这种情况最好先重启应用再清日志,或者用 logrotate 正常轮转。
5.3 删文件很慢怎么办
大量小文件删除时,find -delete 如果遇到目录结构复杂的情况,速度会很慢,因为每个文件删除都要更新父目录的元数据。遇到过几百万个小文件要清理的场景,find 跑了半小时还没完。这时候可以换思路处理:如果整个目录都可以丢,直接把目录 mv 到临时位置再异步删除,等于瞬间释放空间,后台慢慢删。比如:
mv /var/log/old_messages /tmp/old_messages_$$ nohup rm -rf /tmp/old_messages_$$ &mv 操作在同一个文件系统内是改目录项,瞬时完成,df 会立刻看到空间释放,真正占用的块等在后台逐步删除。这个方法在清理海量缓存文件时非常实用,能减少对在线业务的影响。
5.4 怎么判断哪些文件能删
判断标准其实就三条:是否是业务核心数据、是否在业务写入路径上、是否有备份或可重新生成。日志可以压缩保留,包缓存可以清,core 文件可以删,但数据库数据文件、共享存储文件、配置类文件绝对不建议动。我给的建议是,没有把握的文件一律先列出来给团队确认,不要因为"省事"直接删。再就是坚持"先备份再删除"的原则,对于不太确定的大文件,先 mv 到一个临时目录观察几天,确认业务无异常后再从临时目录删除,这是成本最低的安全绳。
6. 磁盘监控与自动化:从救火到防火
最后说说怎么让磁盘问题不再需要"救火"。运维做的事,能自动化就自动化,磁盘检查也一样。我建议至少做两层:一层是系统级的定时清理,另一层是监控告警。
系统级清理可以用 cron 挂脚本。比如每天凌晨 3 点执行一次日志压缩、包缓存清理和 journald vacuum:
0 3 * * * /usr/local/bin/disk_maintain.sh脚本里可以做几件事:用df -h检查整体使用率,超过阈值就执行journalctl --vacuum-size=100M,清理 apt/yum 缓存,再用 find 清理 7 天前的 /tmp 文件和 30 天前的压缩日志。脚本执行完输出一份结果日志,方便事后追溯。注意脚本里的清理动作要加日志和异常捕获,避免某次误操作把不该删的删了而无从查起。
监控告警层面,最基本的思路是定期采集 df 的数据,超过阈值触发通知。实操中可以用简单的 shell 加 curl 把数据推给企业微信、钉钉或自建监控平台,也可以用 Prometheus 的 node_exporter 直接暴露磁盘指标,在 Grafana 上配置告警规则。我推荐配置两级阈值,比如使用率超过 80% 给普通通知,超过 90% 给紧急告警,避免阈值过低导致告警疲劳,也避免阈值过高导致问题发现太晚。
磁盘增长趋势分析也很有价值。如果一台机器磁盘使用率每周稳定增加 2%,那即使现在还安全,几个月后必然出事。把 df 数据按天记录到 Prometheus 或简单的时序日志里,观察增长曲线,就能提前规划扩容或清理策略,而不是等告警响了才急急忙忙开干。
我这里再分享一个小技巧:给关键目录单独做配额或者规划单独的挂载点,比如 /var/log 独立挂一块盘,日志把盘打满也不会拖垮根分区。数据库目录、应用数据目录、日志目录分盘存放,是降低磁盘问题影响面的有效手段。新装机时就该这么规划,比以后迁移要省太多力气。
在我个人实践中,磁盘管理做得好不好,不在于你记了多少命令,而在于你什么时候该看磁盘、看到什么程度该做什么动作、这些动作会不会带来副作用。把 df、du、find、lsof 这几个命令用熟,配合日志轮转和监控告警,服务器磁盘基本不会成为你的突发事故点。最后再补充一句,清理磁盘时永远留一手,拿不准就先备份再删,这习惯我用到现在还没吃过亏。