Linux磁盘明明满了,du却找不到大文件?真正吃空间的可能不是普通文件

一、df 显示 100%,du 却看不到“大文件”
在Linux服务器运维里,有一种问题特别容易让人误判:
df -h
看到 / 根分区已经 100%:
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 100G 100G 0 100% /
于是你马上执行:
du -sh /*
结果发现每个目录加起来好像也没多少,根本找不到那个“吃掉几十 GB 空间的大文件”。
这时候很多人第一反应是:是不是 du 不准?是不是磁盘坏了?是不是系统被入侵了?
其实大多数情况下,不是 du 不准,而是磁盘空间被一些“普通 du 看不到的东西”占用了。
二、先说结论:du 查不到大文件,常见有 6 类原因
| 现象 | 常见原因 | 典型场景 |
|---|---|---|
| df 满了,du 看不到 | 已删除文件仍被进程占用 | Nginx / PHP / MySQL / Java 日志被删但进程未重启 |
| du 目录大小正常,但 df 仍高 | mount 挂载点覆盖了原目录 | 原目录下面有旧文件,被新磁盘挂载遮住 |
| df 显示空间不足,但文件不多 | inode 用完 | 小文件、缓存、Session、邮件队列过多 |
| 根分区增长很快 | Docker overlay2、容器日志 | Docker、宝塔、面板环境常见 |
| du 查不到快照占用 | LVM / Btrfs / ZFS 快照 | 自动快照、备份快照没清理 |
| 普通用户写不进,root 还能写 | ext4 保留空间 | ext4 默认给 root 预留一部分空间 |
这篇文章不讲太多概念,我直接按真实运维现场的排查顺序来写。
三、适用服务器场景与配置参考
这类问题在香港服务器、美国服务器、跨境电商独立站、WordPress 网站、接口服务器、日志型业务上都很常见。尤其是业务访问量上来以后,日志、缓存、数据库 binlog、备份文件都会快速膨胀。
以一台常见香港物理服务器为例:
| 项目 | 配置 |
|---|---|
| CPU | AMD EPYC 4585PX,16核32线程 |
| 内存 | 64GB DDR5 |
| 系统盘 | 960GB NVMe SSD |
| 带宽 | 100M BGP,含 25M CN2 直连 |
| 系统 | Ubuntu 22.04 LTS / CentOS 7.x |
| 适用业务 | WordPress、外贸独立站、API接口、图片站、企业官网、轻量电商 |
如果是日志量比较大的业务,比如高并发接口、下载站、短视频站、游戏后端,我更建议:
| 项目 | 推荐配置 |
|---|---|
| CPU | AMD EPYC 7402P,24核48线程 |
| 内存 | 128GB DDR4 |
| 系统盘 | 960GB NVMe SSD |
| 数据盘 | 2 × 1.92TB NVMe 或 4TB SSD |
| 带宽 | 100M / 1G BGP,按业务流量选择 |
| 建议分区 | /、/data、/var/log 尽量拆分 |
很多磁盘满的问题,不是服务器性能不够,而是系统盘、日志盘、数据盘没有规划清楚。
四、第一步:先确认到底是哪个分区满了
不要一上来就 du -sh /*,先看分区。
df -hT
重点看这几列:
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 100G 98G 0 100% /
/dev/nvme1n1 ext4 900G 300G 600G 34% /data
如果是 / 满了,排查根分区。
如果是 /data 满了,排查业务数据。
如果是 /var 单独分区满了,重点看日志、缓存、数据库。
很多人犯的第一个错误是:看到磁盘满了,就在整个系统里乱找大文件。实际上你要先知道满的是哪个挂载点。
五、原因一:文件已经被删除,但进程还在占用
这是最常见,也是最容易被忽略的情况。
比如 Nginx 访问日志 /var/log/nginx/access.log 有 30GB,你直接执行:
rm -f /var/log/nginx/access.log
从目录里看,文件确实没了。
但是如果 Nginx 进程还没有释放这个文件句柄,Linux 内核仍然认为这个文件占用的磁盘空间还在。
所以你会看到:
du -sh /var/log/nginx
目录不大。
但:
df -h
磁盘还是满的。
排查命令
lsof | grep deleted
如果输出类似:
nginx 1234 www-data 5w REG 259,2 32212254720 /var/log/nginx/access.log (deleted)
mysql 2231 mysql 7w REG 259,2 12884901888 /var/lib/mysql/mysql-bin.000123 (deleted)
说明这些文件已经从目录中删除了,但进程还在占用。
解决方法
如果是 Nginx:
systemctl reload nginx
如果 reload 不释放,可以重启:
systemctl restart nginx
如果是 PHP-FPM:
systemctl restart php-fpm
Ubuntu 环境可能是:
systemctl restart php8.1-fpm
如果是 MySQL,要谨慎,不要直接乱杀进程。建议先确认业务低峰期,再执行:
systemctl restart mysqld
或:
systemctl restart mysql
更稳妥的处理方式
不要直接删除正在写入的日志文件,而是清空它:
: > /var/log/nginx/access.log
或者:
truncate -s 0 /var/log/nginx/access.log
这样文件句柄还在,空间也能释放。
六、原因二:挂载点覆盖了原目录,du 看不到底层旧文件
这个问题在加数据盘、挂载 /data、迁移网站目录时很常见。
比如你原来在 /data 目录里放了 200GB 文件。
后来你把一块新硬盘挂载到了 /data:
mount /dev/nvme1n1 /data
这时候 /data 下面看到的是新硬盘里的内容,原来根分区 /data 目录下那 200GB 文件被“遮住”了。
你执行:
du -sh /data
看到的是新磁盘里的内容,不是被遮住的旧文件。
但这些旧文件仍然占着根分区空间。
排查思路
先看挂载情况:
findmnt
或者:
mount | column -t
重点看 /data、/www、/home、/var/lib/mysql、/var/www/html 是否是挂载点。
解决方法
需要在维护窗口操作。
先停业务:
systemctl stop nginx
systemctl stop php-fpm
卸载挂载点:
umount /data
然后查看原目录:
du -sh /data/*
如果发现里面有旧文件,确认无用后再清理:
rm -rf /data/old-backup/*
然后重新挂载:
mount /data
这个问题不能盲目操作,尤其是生产环境,建议先备份和确认挂载关系。
七、原因三:inode 用完了,不是容量用完了
有时候 df -h 看起来空间还有,但系统仍然提示:
No space left on device
这可能不是磁盘容量满,而是 inode 满了。
inode 可以简单理解为“文件数量指标”。哪怕每个文件只有 1KB,只要文件数量特别多,也可能把 inode 用完。
查看 inode
df -i
示例:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2 6553600 6553000 600 100% /
如果 IUse% 是 100%,说明 inode 满了。
常见目录
/var/lib/php/session
/tmp
/var/tmp
/var/spool/postfix
/var/cache
/www/wwwroot/某些缓存目录
查找小文件数量最多的目录
for i in /*; do echo $i; find $i -xdev -type f 2>/dev/null | wc -l; done
进一步排查:
find /var -xdev -type f | cut -d/ -f1-4 | sort | uniq -c | sort -nr | head
清理示例
清理 7 天前的 PHP session:
find /var/lib/php/session -type f -mtime +7 -delete
清理临时目录:
find /tmp -type f -mtime +3 -delete
注意,不要直接 rm -rf /tmp/*,有些服务可能正在使用临时文件。
八、原因四:Docker 容器日志或 overlay2 吃满磁盘
如果服务器上跑了 Docker,磁盘满了但你用普通方式找不到明显大文件,建议重点查:
/var/lib/docker
尤其是:
/var/lib/docker/containers
/var/lib/docker/overlay2
查看 Docker 占用
docker system df
查看容器日志大小:
find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \;
如果看到几十 GB 的 json 日志文件,就很明确了。
清空容器日志
truncate -s 0 /var/lib/docker/containers/*/*-json.log
设置 Docker 日志限制
编辑:
vim /etc/docker/daemon.json
加入:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "200m",
"max-file": "3"
}
}
重启 Docker:
systemctl restart docker
注意,重启 Docker 会影响容器业务,生产环境要安排维护窗口。
九、原因五:systemd journal 日志占用过大
Ubuntu 22.04、Debian、CentOS 7.x 都可能遇到 journal 日志增长的问题。
查看占用:
journalctl --disk-usage
如果显示:
Archived and active journals take up 12.0G in the file system.
就说明 journal 占用不小。
清理 journal 日志
保留最近 7 天:
journalctl --vacuum-time=7d
限制最大 1GB:
journalctl --vacuum-size=1G
设置长期限制
编辑:
vim /etc/systemd/journald.conf
建议配置:
SystemMaxUse=1G
SystemKeepFree=2G
MaxRetentionSec=7day
重启服务:
systemctl restart systemd-journald
十、原因六:数据库 binlog、慢日志、错误日志膨胀
很多网站服务器磁盘满,并不是网站文件变大,而是数据库日志把磁盘吃满。
常见目录:
/var/lib/mysql
/www/server/data
宝塔环境经常在:
/www/server/data
查看 MySQL 目录:
du -sh /var/lib/mysql/*
或:
du -sh /www/server/data/*
常见大文件:
mysql-bin.000001
mysql-bin.000002
slow.log
error.log
查看 binlog
进入 MySQL:
SHOW BINARY LOGS;
查看当前使用的 binlog:
SHOW MASTER STATUS;
清理旧 binlog
例如清理到某个时间之前:
PURGE BINARY LOGS BEFORE '2026-05-01 00:00:00';
或者清理到某个文件之前:
PURGE BINARY LOGS TO 'mysql-bin.000120';
不要直接 rm -f mysql-bin.*,这样可能导致 MySQL 状态不一致。
设置保留时间
MySQL 8:
binlog_expire_logs_seconds=604800
表示保留 7 天。
MySQL 5.7:
expire_logs_days=7
十一、原因七:ext4 保留空间导致普通用户写不进去
如果服务器使用 ext4 文件系统,默认可能会保留 5% 空间给 root。
在 100GB 系统盘上,5% 也就是 5GB。
在 1TB 数据盘上,5% 就是 50GB。
这会导致普通用户或服务进程提示磁盘满,但 root 看起来还能操作。
查看保留空间
先找分区:
df -hT
例如是:
/dev/nvme0n1p2
查看:
tune2fs -l /dev/nvme0n1p2 | grep -i "Reserved block"
调低保留比例
如果是数据盘,可以调低到 1%:
tune2fs -m 1 /dev/nvme0n1p2
如果是纯数据盘,甚至可以设为 0:
tune2fs -m 0 /dev/nvme1n1
系统盘不建议随便设为 0,至少保留一点空间,避免系统在异常情况下完全不可操作。
十二、推荐排查顺序:不要一开始就乱删文件
遇到“磁盘满但 du 查不到大文件”,我一般按这个顺序排查。
1. 看哪个分区满了
df -hT
2. 看 inode 有没有满
df -i
3. 查已删除但仍被占用的文件
lsof | grep deleted
如果服务器没有 lsof:
yum install -y lsof
或:
apt install -y lsof
4. 限制在当前文件系统内查大目录
du -xh / --max-depth=1 | sort -h
注意这里的 -x 很重要,它表示不跨文件系统。
继续深入:
du -xh /var --max-depth=1 | sort -h
du -xh /www --max-depth=1 | sort -h
du -xh /data --max-depth=1 | sort -h
5. 查大文件
find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
6. 查 Docker
docker system df
find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \;
7. 查 journal
journalctl --disk-usage
8. 查数据库日志
du -sh /var/lib/mysql/*
du -sh /www/server/data/*
十三、生产环境处理建议:先止血,再根治
1. 先释放安全空间
可以优先处理这些相对安全的内容:
journalctl --vacuum-size=1G
find /tmp -type f -mtime +3 -delete
truncate -s 0 /var/log/nginx/access.log
truncate -s 0 /var/log/nginx/error.log
但数据库文件、业务上传文件、网站目录不要乱删。
2. 再处理占用源头
比如日志太大,就加 logrotate。
Nginx 日志轮转示例:
/var/log/nginx/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
systemctl reload nginx >/dev/null 2>&1 || true
endscript
}
3. 最后做磁盘规划
如果业务已经稳定增长,不建议长期靠清理维持。
对于香港服务器上的网站业务,我更推荐这样拆:
| 分区 | 用途 | 建议 |
|---|---|---|
/ |
系统、基础服务 | 80GB - 150GB |
/www |
网站程序 | 单独数据盘或大容量分区 |
/data |
上传文件、业务数据 | SSD / NVMe |
/var/log |
日志 | 可独立分区,避免拖垮系统盘 |
/backup |
备份 | 不建议和业务盘放一起 |
十四、不同业务的配置建议
1. 普通企业官网 / WordPress 站
推荐配置:
| 项目 | 建议 |
|---|---|
| CPU | Intel Xeon E-2334 / AMD EPYC 4585PX |
| 内存 | 32GB - 64GB |
| 硬盘 | 960GB NVMe SSD |
| 带宽 | 100M BGP,含 25M CN2 |
| 系统 | Ubuntu 22.04 LTS |
重点优化:
- 控制 Nginx 日志保留周期;
- WordPress 缓存目录设置自动清理;
- 数据库 binlog 不要无限保留;
- 上传目录和备份目录不要放在系统盘。
2. 跨境电商 / 外贸独立站
推荐配置:
| 项目 | 建议 |
|---|---|
| CPU | AMD EPYC 4585PX,16核32线程 |
| 内存 | 64GB DDR5 |
| 硬盘 | 960GB NVMe SSD + 1.92TB SSD |
| 带宽 | 100M BGP + CN2 优化 |
| 适合场景 | WooCommerce、Magento、Shopify 外部落地页、ERP接口 |
重点优化:
- MySQL 慢日志限制大小;
- 图片缓存目录独立;
- 备份不要和网站目录混放;
/var/log建议单独监控。
3. 高并发接口 / 日志量大的业务
推荐配置:
| 项目 | 建议 |
|---|---|
| CPU | AMD EPYC 7402P,24核48线程 |
| 内存 | 128GB |
| 硬盘 | 2 × 1.92TB NVMe SSD |
| 带宽 | 300M / 1G BGP |
| 系统 | Ubuntu 22.04 LTS |
重点优化:
- 日志异步写入;
- 接入 ELK / Loki / ClickHouse;
- 本机只保留 3 - 7 天日志;
- 按天切割日志,避免单文件过大;
- 关键目录单独挂载,防止根分区被写满。
十五、一个比较完整的排查脚本
可以临时保存为:
vim disk-check.sh
内容如下:
#!/bin/bash
echo "===== 1. Disk Usage ====="
df -hT
echo
echo "===== 2. Inode Usage ====="
df -i
echo
echo "===== 3. Top Level Directory Usage ====="
du -xh / --max-depth=1 2>/dev/null | sort -h
echo
echo "===== 4. Deleted But Open Files ====="
lsof | grep deleted | head -50
echo
echo "===== 5. Large Files Over 500MB ====="
find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null | sort -k5 -h
echo
echo "===== 6. Journal Usage ====="
journalctl --disk-usage 2>/dev/null
echo
echo "===== 7. Docker Usage ====="
docker system df 2>/dev/null
echo
echo "===== 8. Docker Logs ====="
find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \; 2>/dev/null
执行:
chmod +x disk-check.sh
./disk-check.sh
这个脚本不会删除文件,只用于排查,适合先看现场情况。
十六、我不建议的几个操作
遇到磁盘满,千万不要急着执行这些命令:
rm -rf /var/log/*
rm -rf /tmp/*
rm -rf /var/lib/mysql/mysql-bin.*
rm -rf /var/lib/docker/overlay2/*
这些操作很容易导致服务异常、数据库损坏、容器无法启动。
正确做法是:先确认文件来源、确认是否正在被进程占用、确认是否可以安全清理,再处理。
十七、总结:du 查不到,不代表磁盘没有被占用
Linux 服务器磁盘满了,但 du 查不到大文件,最常见的不是系统异常,而是以下几类问题:
- 文件被删除了,但进程还在占用;
- 挂载点覆盖了旧目录;
- inode 被小文件耗尽;
- Docker 容器日志或 overlay2 占用过大;
- systemd journal 没有限制;
- MySQL binlog、慢日志、错误日志持续膨胀;
- ext4 保留空间导致普通用户无法写入。
真正专业的处理方式,不是看到大文件就删,而是先用:
df -hT
df -i
lsof | grep deleted
du -xh / --max-depth=1
find / -xdev -type f -size +500M
把问题定位清楚。
对于网站业务、跨境电商、接口服务和高并发应用来说,磁盘规划和日志策略比临时清理更重要。尤其是香港服务器这类承载外贸站、WordPress、API、图片站的业务环境,建议一开始就把系统盘、数据盘、日志目录、备份目录分开设计,这样即使某个业务日志突然暴涨,也不至于把整台服务器拖到无法登录、数据库无法写入的状态。