磁盘与文件系统 — CodeRoadMap磁盘与文件系统
这一课你将能做到
- 用 df、du 在 1 分钟内定位「磁盘满了」的元凶目录,并完成清理或扩容
- 理解挂载的概念,能把新磁盘分区、格式化并挂载到指定目录,写入 fstab 实现开机自动挂载
- 用 tar/gzip 完成备份归档,理解 swap 的作用,并知道 LVM 在线扩容的基本流程
为什么要学
磁盘满是线上最常见的事故之一:MySQL 写不进数据导致全站 500、Redis AOF 撑爆磁盘、日志日积月累吃光根分区。本站后端(Spring Boot + MySQL + Redis)的每一个组件都在持续写磁盘,作为运维者你必须会三件事:快速定位空间去向、安全清理、扩容防复发。
面试中「df 和 du 的区别」「磁盘满了怎么排查」「/dev/sda 和 /dev/sda1 是什么关系」「swap 满了怎么办」是标准题库。这一课也直接对应路线图中的 Disks、LVM、Swap、Archiving 节点。
先建立图像
从物理盘到可用目录的完整链路:
物理磁盘 /dev/sda(整块盘)
│ 分区(fdisk/parted)
├── /dev/sda1 ──格式化 ext4──→ 挂载到 / (根分区)
├── /dev/sda2 ──格式化 ext4──→ 挂载到 /var/log
└── /dev/sda3 ── swap 格式 ──→ 当作交换分区(虚拟内存)
云服务器常见形态:
/dev/vda 系统盘(50G)
/dev/vdb 数据盘(500G),挂载到 /data,MySQL 数据放这里
挂载的本质:把某个设备上的文件系统「嫁接」到目录树的某个目录上,
之后访问该目录 = 访问该设备。
df 和 du 的视角差异(高频考点):
df:站在「文件系统」角度,报告分区级别的总量/用量(查分区剩多少)
du:站在「目录」角度,逐个文件累加算大小(查哪个目录占的多)
df 满但 du 找不到大文件 → 有「已删除但仍被进程占用」的幽灵文件
跟着做
步骤 1:df——分区级空间总览
df -h
# Filesystem Size Used Avail Use% Mounted on
# /dev/vda1 50G 45G 2.5G 95% / ← 根分区快满了,告警!
# /dev/vdb1 500G 120G 380G 24% /data
df -i # 查看 inode 使用率(海量小文件会把 inode 耗尽)
df -hT # -T 同时显示文件系统类型(ext4/xfs)
步骤 2:du——定位空间元凶
# 从根开始逐层下钻(运维标准排查路径)
sudo du -h --max-depth=1 / 2>/dev/null | sort -hr | head
# 输出示例: 20G /var
# 12G /usr
# 8G /home
sudo du -h --max-depth=1 /var 2>/dev/null | sort -hr | head
# 输出: 15G /var/lib ← MySQL 数据在这
# 3G /var/log ← 日志
sudo du -h --max-depth=1 /var/lib/mysql | sort -hr | head
# 找到最大的库/表文件
# 直接找大文件(超过 500M)
sudo find / -type f -size +500M -exec ls -lh {} \; 2>/dev/null
步骤 3:磁盘满了的完整急救流程(本站实战)
# 1. 确认哪个分区满了
df -h
# 2. 逐层定位大户
sudo du -h --max-depth=1 /var 2>/dev/null | sort -hr | head
# 3. 常见可清理项(按安全性排序):
sudo journalctl --vacuum-size=500M # 裁剪 systemd 日志(安全)
sudo apt clean # 清 apt 缓存(安全)
sudo apt autoremove -y # 删旧内核和无用依赖(较安全)
sudo truncate -s 0 /var/log/myapp/app.log # 清空日志(保留文件,进程无需重启)
# 4. df 还是满?查幽灵文件(已删除但被占用)
sudo lsof +L1
# COMMAND PID USER SIZE/OFF NLINK PATH (deleted)
# java 1234 app 2.5G 0 /var/log/myapp/app.log (deleted)
# NLINK=0 表示文件名已删但进程还占着 → 重启对应进程释放
sudo systemctl restart codeforge
步骤 4:新磁盘分区、格式化与挂载
# 1. 确认新盘(假设是 /dev/vdb,没有分区)
lsblk
sudo fdisk -l /dev/vdb
# 2. 分区(交互式:n 新建 → p 主分区 → 一路回车用全盘 → w 写入)
sudo fdisk /dev/vdb
# 3. 格式化为 ext4
sudo mkfs.ext4 /dev/vdb1
# 4. 挂载到 /data
sudo mkdir -p /data
sudo mount /dev/vdb1 /data
df -h /data # 验证
步骤 5:fstab——开机自动挂载
mount 是临时的,重启失效。永久挂载要写 /etc/fstab:
# 用 UUID 而不是设备名(设备名重启后可能变化)
sudo blkid /dev/vdb1
# 输出:/dev/vdb1: UUID="a1b2c3d4-..." TYPE="ext4"
sudo nano /etc/fstab
# 追加一行:
# UUID=a1b2c3d4-... /data ext4 defaults 0 2
# 写完必须验证(fstab 写错会导致开机进不了系统!)
sudo mount -a # 模拟开机挂载,无输出即正确
步骤 6:swap——内存不够用磁盘顶上
# 查看 swap 状态
free -h
# total used free buff/cache available
# Mem: 3.8Gi 3.2Gi 100Mi 512Mi 400Mi
# Swap: 2.0Gi 1.8Gi 200Mi ← swap 快满了,危险信号
swapon --show
# 创建 2G swap 文件(云服务器默认常无 swap)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile # 权限必须 600,否则 swapon 拒绝
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # 开机启用
注意:swap 是应急缓冲,不是内存替代品。swap 持续高使用说明内存真的不够(该加内存了),因为磁盘比内存慢 10 万倍。
步骤 7:tar/gzip——备份与归档
# 打包压缩(备份 MySQL 数据目录、应用代码)
tar czf backup-$(date +%F).tar.gz /opt/codeforge/
# c=create z=gzip压缩 f=文件名
# 查看包内容不解压
tar tzf backup-2026-10-07.tar.gz | head
# 解压到指定目录
tar xzf backup-2026-10-07.tar.gz -C /tmp/restore/
# 排除某些目录(备份时排除日志和临时文件)
tar czf app.tar.gz --exclude='*.log' --exclude='tmp/' /opt/codeforge/
# 更高压缩率(换 xz,更慢但更小)
tar cJf backup.tar.xz /opt/codeforge/
步骤 8:LVM 一瞥——在线扩容的底气
LVM(逻辑卷管理)让磁盘空间可以像水池一样动态分配:
# 概念:物理卷 PV → 卷组 VG → 逻辑卷 LV
sudo pvdisplay # 物理卷
sudo vgdisplay # 卷组(看有没有 FREE 空间)
sudo lvdisplay # 逻辑卷
# 卷组有剩余空间时,给根分区在线加 10G(ext4 支持在线扩容,不中断服务)
sudo lvextend -L +10G /dev/ubuntu-vg/ubuntu-lv
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
df -h / # 已变大
Ubuntu Server 默认安装就是 LVM 布局,所以云盘扩容后通常只需 growpart + resize2fs 两条命令。
步骤 9:内存与磁盘监控速查
free -h # 内存 + swap 总览
vmstat 2 5 # 每 2 秒采样,看 si/so(swap 换入换出,非 0 说明内存在颠簸)
sudo apt install -y sysstat
iostat -x 2 # 磁盘 I/O:%util 接近 100% = 磁盘满载
讲清楚要点
- df 看分区,du 看目录:df 从文件系统元数据读统计(快),du 逐文件累加(慢但精确到目录)。两者数字对不上时,优先怀疑幽灵文件(lsof +L1)。
- 删日志用 truncate 而不是 rm:
truncate -s 0 app.log 清空内容但保留文件句柄,应用无需重启;rm 后进程继续持有已删 inode,空间不释放。
- fstab 写错可能开不了机:改完必须
mount -a 验证;云服务器还要注意 fstab 里别写死设备名,用 UUID。
- swap 权限必须 600:这是 mkswap/swapon 的强制安全要求,权限不对会拒绝启用。
- tar 选项没有
- 也行:tar czf 和 tar -czf 等价,前者是传统写法更常见。记忆法:czf 打包、xzf 解包、tzf 查看。
- inode 耗尽和磁盘满是两种故障:
df -i 看 inode。海量 session 文件、邮件队列会先把 inode 吃光,此时磁盘空间明明还有却写不进任何文件。
- ext4 支持在线扩容,缩容必须卸载:所以扩容放心扩,缩容基本不做(宁可重建)。
你可能会懵的地方
-
/dev/sda、/dev/vda、/dev/nvme0n1 有什么区别?
都是磁盘设备名。sda 是 SATA/SCSI 盘,vda 是 KVM 虚拟机的 virtio 盘,nvme0n1 是 NVMe 固态。云服务器常见 vda/vdb。分区是设备名加数字:vda1、nvme0n1p1。
-
为什么云盘在控制台扩容后,df 还是老大小?
控制台只扩了「块设备」,操作系统里的分区和文件系统还没扩。解决:sudo growpart /dev/vda 1(扩分区)+ sudo resize2fs /dev/vda1(扩文件系统)。
-
MySQL 数据目录能直接 tar 备份吗?
运行中直接打包会拿到不一致的数据(正在写入)。正确做法:用 mysqldump 逻辑备份,或 xtrabackup 热备,或先 FLUSH TABLES WITH READ LOCK。tar 适合备份应用代码和配置文件。
-
buffer/cache 占用高需要担心吗?
不需要。Linux 会把空闲内存当磁盘缓存加速读写,应用需要时立即释放。看内存够不够要看 free -h 的 available 列,不是 free 列。
-
挂载点目录里有文件,挂载后去哪了?
被「遮住」了,没丢。卸载后(umount)又能看到。所以挂载前确保挂载点目录是空的,避免混乱。
错法对照
| 错法 | 问题 | 正解 |
|---|
rm app.log 清日志 | 空间不释放,进程继续写已删 inode | truncate -s 0 app.log 或 : > app.log |
| fstab 写 /dev/vdb1 不写 UUID | 多盘机器重启后盘符漂移,挂错盘 | blkid 拿 UUID 写入 fstab |
| fstab 改完直接重启 | 写错导致开机进 emergency 模式 | 先 mount -a 验证 |
| du -sh /* 不带 sudo | 大量 Permission denied,统计不全 | sudo du -h --max-depth=1 / 2>/dev/null |
| swap 文件 chmod 755 | swapon 报「insecure permissions」拒绝启用 | chmod 600 /swapfile |
| 直接 tar 运行中的 MySQL 数据目录 | 备份出来的数据不一致无法恢复 | mysqldump 或 xtrabackup |
卡点急救
-
现象:MySQL 报 No space left on device 全站 500
按步骤 3 急救:journalctl 裁剪 → apt clean → 清旧日志 → 查幽灵文件。恢复后立即规划:日志接 logrotate、MySQL binlog 设过期时间(binlog_expire_logs_seconds)、数据盘扩容。
-
现象:df -h 还有空间,但写文件报 No space left
两种可能:inode 耗尽(df -i 确认,清小文件);或保留块被占满(ext4 默认给 root 保留 5%,普通用户写不进,用 root 清理)。
-
现象:umount 报 target is busy
有进程在用该目录(包括有人 cd 在里面)。解决:lsof /data 或 fuser -vm /data 找出占用进程,关闭后再卸载;实在不行 umount -l(惰性卸载)。
面试常问
-
df 和 du 的区别?df 显示满了但 du 找不到大文件怎么办?
df 报告文件系统级别用量(读超级块,快);du 遍历目录累加(慢)。df 满而 du 找不到,说明有「已删除但仍被进程打开」的文件:lsof +L1 找 NLINK=0 的文件,重启占用进程释放空间。这是真实生产高频故障。
-
磁盘满了的排查流程?
df -h 定位分区 → du -h --max-depth=1 逐层下钻找大目录 → 按安全性清理(journalctl 裁剪、apt clean、truncate 日志)→ lsof +L1 查幽灵文件 → df -i 排除 inode 耗尽 → 事后复盘:日志轮转、监控告警、扩容。
-
什么是 swap?swap 满了说明什么?
swap 是磁盘上模拟内存的交换空间,内存不足时把不活跃页换出到磁盘。swap 高使用 + si/so 非零(vmstat)说明物理内存严重不足,系统在「颠簸」,性能急剧下降,需要加内存或优化应用(如调小 JVM 堆)。
自测三问
- 如何清空调用中的日志文件且不影响写入它的进程?
- 新挂载的数据盘重启后消失了,可能漏了哪一步?正确的配置方法是什么?
free -h 显示 swap 用了 90%,vmstat 的 si/so 持续非零,说明什么?怎么处理?
小结一句话
磁盘排查的黄金路径:df 定位分区、du 找元凶、lsof +L1 抓幽灵文件;挂载记住 UUID 写 fstab,清理优先 truncate 而不是 rm。
延伸