FAQ-7:磁盘与文件系统常见问题

预计阅读时间:13 分钟

📖 目录

FAQ-7:磁盘与文件系统常见问题(Q82-Q92)

问题速查表

问题章节
Q82: df 和 du 显示大小不一致?Q82
Q83: 如何查看磁盘 UUID?Q83
Q84: 如何在线扩展分区?Q84
Q85: 如何检查修复文件系统?Q85
Q86: No space but df shows free?Q86
Q87: 如何安全测试 fstab?Q87
Q88: 如何给磁盘做性能测试?Q88
Q89: 如何查看目录所在挂载点?Q89
Q90: 如何创建和挂载 ISO?Q90
Q91: 如何加密分区?Q91
Q92: 如何监控磁盘 SMART?Q92

Q82: df 和 du 显示大小不一致?

df 从文件系统层面统计(包含已删除但未释放的文件),du 从目录树统计。两者不一致通常有两个原因:已删除文件未释放、挂载点被遮挡。

# 原因一:进程持有已删除文件的句柄
lsof +L1 | grep deleted
# 修复:重启持有文件的进程

# 原因二:挂载点遮挡(子挂载点覆盖了父目录的部分空间)
findmnt -T /path/to/dir
# 如果 df 显示的挂载点与预期不同,检查是否有 bind mount

Q83: 如何查看磁盘 UUID?

# blkid:查看块设备的 UUID、文件系统类型等
blkid /dev/sda1
# 输出:/dev/sda1: UUID="a1b2c3d4-..." TYPE="ext4"

# 通过符号链接查看
ls -la /dev/disk/by-uuid/
# 每个 UUID 是一个符号链接,指向实际设备

Q84: 如何在线扩展分区?

# LVM 方式(推荐)
# 1. 扩展逻辑卷
sudo lvextend -L +10G /dev/vg0/lv0

# 2. 扩展文件系统
# ext4:
sudo resize2fs /dev/vg0/lv0
# xfs:
sudo xfs_growfs /dev/vg0/lv0

# 云环境方式(如 AWS EBS 扩容)
# 1. 在云控制台扩容磁盘
# 2. 扩展分区表
sudo growpart /dev/xvda 1
# 3. 扩展文件系统
sudo resize2fs /dev/xvda1

Q85: 如何检查修复文件系统?

# fsck:文件系统检查与修复
# 注意:必须先卸载分区,否则可能损坏数据

# 只检查不修复(安全)
sudo fsck -n /dev/sdb1

# 自动修复
sudo fsck -y /dev/sdb1

# 对根分区:在恢复模式或 Live CD 中运行
# 或在 /etc/fstab 中设置 fsck 在启动时自动检查

Q86: No space but df shows free?

磁盘空间充足但无法创建文件,通常是 inode(索引节点)耗尽。每个文件/目录/符号链接都占用一个 inode。

# 检查 inode 使用率
df -i
# Filesystem     Inodes  IUsed  IFree IUse% Mounted on
# /dev/sdb1     3276800 3276800     0  100% /data    ← inode 耗尽

# 找出 inode 占用最多的目录
find /data -xdev -printf '%h\n' | sort | uniq -c | sort -rn | head -5
# 输出示例:
# 2843612 /data/app/sessions   ← 每个会话一个文件,inode 被耗尽

# 修复:清理小文件
find /data/app/sessions -type f -mtime +30 -delete

Q87: 如何安全测试 fstab?

# 测试 fstab 挂载配置
sudo mount -a

# 验证 fstab 语法
sudo findmnt --verify

# 如果 fstab 配置错误导致无法开机:
# 在 GRUB 启动时选择 recovery mode
# 或添加内核参数 init=/bin/bash 以 root 进入
# 编辑 /etc/fstab 修复错误

Q88: 如何给磁盘做性能测试?

# dd 简单测试(顺序写)
# oflag=direct:绕过页缓存,测试真实磁盘性能
dd if=/dev/zero of=test bs=1M count=1024 oflag=direct

# fio 专业测试(随机读写)
# --name:测试名称  --rw=randrw:随机读写
# --bs=4k:块大小  --size=1G:测试文件大小
# --numjobs=4:并发线程数  --runtime=30:运行 30 秒
fio --name=test --rw=randrw --bs=4k --size=1G --numjobs=4 --runtime=30

Q89: 如何查看目录所在挂载点?

# df:查看目录所在的文件系统和挂载点
df /path/to/dir

# findmnt -T:更详细的挂载信息
findmnt -T /path/to/dir

Q90: 如何创建和挂载 ISO?

# 创建 ISO 镜像
genisoimage -o output.iso /path/to/directory

# 挂载 ISO(只读)
sudo mount -o loop file.iso /mnt

# 卸载
sudo umount /mnt

Q91: 如何加密分区?

# LUKS(Linux Unified Key Setup)加密
# 1. 格式化加密分区
sudo cryptsetup luksFormat /dev/sdb1

# 2. 打开(解密)加密分区
sudo cryptsetup open /dev/sdb1 secret
# 输入密码后,设备出现在 /dev/mapper/secret

# 3. 格式化并挂载
sudo mkfs.ext4 /dev/mapper/secret
sudo mount /dev/mapper/secret /mnt/encrypted

# 关闭加密分区
sudo umount /mnt/encrypted
sudo cryptsetup close secret

Q92: 如何监控磁盘 SMART?

# smartctl:S.M.A.R.T. 磁盘健康监控
# 安装:sudo apt install smartmontools

# 检查磁盘整体健康状态
sudo smartctl -H /dev/sda
# 输出:PASSED 或 FAILED

# 查看详细属性(关注 Reallocated、Pending、Uncorrectable)
sudo smartctl -a /dev/sda | grep -E "Reallocated|Pending|Uncorrect"
# Reallocated:重新分配的坏扇区数量
# Pending:等待重新分配的扇区
# Uncorrectable:无法纠正的错误

真实案例

案例 A:inode 耗尽——磁盘空间充足却无法创建文件

现象:Web 应用报错 No space left on device,但 df -h 显示磁盘还有 380GB 空闲。

$ df -h /data
Filesystem      Size  Used Avail Use%
/dev/sdb1        500G  120G  380G  24%    ← 空间充足

$ df -i /data
Filesystem     Inodes  IUsed IFree IUse%
/dev/sdb1     3276800 3276800     0  100%  ← inode 耗尽

根因:应用把每个用户会话存成独立小文件,累计 284 万个小文件耗尽了 inode。

修复:清理过期会话 find /data/app/sessions -type f -mtime +30 -delete,长期方案改用 Redis 存储会话。

案例 B:fstab 配置错误导致无法开机

现象:修改 /etc/fstab 添加 NFS 挂载后重启,系统进入 emergency mode。

根因:fstab 中 NFS 服务器 IP 写错,mount 超时导致 systemd 启动失败。

修复:在 GRUB 选择 recovery mode,或在内核参数添加 init=/bin/bash,进入后编辑 fstab 修复 IP。

案例 C:LVM 扩容后忘记 resize2fs 导致 df 仍显示旧大小

现象:执行 lvextend -L +50G 扩展了逻辑卷,但 df -h 仍然显示旧的磁盘大小。

# 逻辑卷已扩展
$ sudo lvs
  lv0  vg0  -wi-ao  100.00g

# 但文件系统大小没变
$ df -h /data
/dev/mapper/vg0-lv0   50G   32G   16G  67% /data    ← 还是 50G

# 需要扩展文件系统
$ sudo resize2fs /dev/mapper/vg0-lv0    # ext4
$ sudo xfs_growfs /data                  # xfs

根因:LVM 扩容只扩展了底层块设备,文件系统的大小需要单独调整。

修复:扩容后必须执行 resize2fs(ext4)或 xfs_growfs(xfs)。

预防措施

  • 同时监控 df -h(空间)和 df -i(inode),设置 80%/90% 告警阈值
  • 修改 fstab 前先用 sudo mount -a 测试,避免配置错误导致无法开机
  • 重要分区使用 LVM,便于在线扩展磁盘空间
  • 磁盘 SMART 定期检查,发现 Reallocated 扇区增长及时更换硬盘
  • 加密分区前务必备份数据,LUKS 加密不可逆
  • 云盘扩容后必须执行 growpart + resize2fs,仅在控制台扩容不够
  • 定期用 fio 做磁盘性能基线测试,发现性能下降及时排查
  • swap 空间建议设为物理内存的 1-2 倍,大数据应用可适当增加
  • 使用 lsblk -f 定期检查块设备状态,确认文件系统类型和挂载点
  • 磁盘故障预警:关注 /var/log/syslog 中的 I/O error 和 Medium Error 日志
  • NFS 挂载使用 soft,timeo=10 选项,避免 I/O 超时导致 D 状态进程
  • 云盘扩容前确认分区表类型(MBR/GPT),GPT 分区扩容方式与 MBR 不同
  • swap 文件权限必须设为 600,否则 mkswap 会拒绝创建
  • 文件系统类型选择:ext4 适合通用场景,xfs 适合大文件和高并发 I/O
  • 磁盘性能基线测试建议在部署初期执行一次,后续定期对比发现性能退化
  • 使用 fstrim / 定期回收 SSD 已删除文件占用的 TRIM 块

案例 D:ext4 磁盘空间接近满时性能急剧下降

现象:磁盘使用率超过 85% 后,写入速度从 200MB/s 骤降到 10MB/s,应用响应变慢。

# 检查磁盘使用率
$ df -h /data
Filesystem      Size  Used Avail Use%
/dev/sdb1       500G  440G   50G  88%    ← 接近满

# ext4 为 root 保留 5% 空间(默认)
$ tune2fs -l /dev/sdb1 | grep "Reserved block count"
Reserved block count:     13107200
Reserved GDT blocks:      1024

# 减少保留空间(非 root 分区可以调低)
$ sudo tune2fs -m 1 /dev/sdb1    # 保留 1%(原来是 5%)
$ df -h /data
Filesystem      Size  Used Avail Use%
/dev/sdb1       500G  440G   55G  80%    ← 可用空间增加

# 检查磁盘碎片率(ext4)
$ e4defrag -c /data
/defrag: /data   fragmentation score 0.25
# 超过 40% 需要整理

# 查看磁盘 I/O 状态
$ iostat -x 1 3
# 关注 %util 和 await 指标
# %util > 80% 表示磁盘接近饱和
# await > 10ms 表示 I/O 延迟较高

# 使用 fio 测试磁盘性能
$ fio --name=test --rw=write --bs=1M --size=1G --numjobs=1 --runtime=10
# 对比基线性能,发现性能下降

根因:ext4 默认为 root 保留 5% 空间(防止普通用户写满磁盘导致系统崩溃)。当使用率超过 80% 时,剩余空间碎片化严重,写入性能下降。文件系统需要连续块来存储大文件,碎片导致大量随机 I/O。

修复:减少保留空间 tune2fs -m 1,同时清理不需要的文件。使用 e4defrag 整理碎片。长期方案:扩容磁盘或迁移到更大分区。监控磁盘 I/O 指标,发现性能下降及时排查。使用 fio 做性能基线测试,建立性能参考标准。

案例 E:swap 空间耗尽导致系统卡死

现象:系统响应极慢,free -h 显示 swap 已用完,多个进程被 OOM Killer 终止。

# 检查 swap 使用
$ free -h
              total    used    free   shared  buff/cache  available
Mem:           16Gi    15Gi   256Mi    128Mi     800Mi      500Mi
Swap:         2.0Gi   2.0Gi      0B

# 查看 swap 使用最多的进程
$ for pid in $(ls /proc/ | grep -E '^[0-9]+$'); do
    swap=$(awk '/VmSwap/{print $2}' /proc/$pid/status 2>/dev/null)
    [ "$swap" -gt 0 ] 2>/dev/null && echo "$swap kB PID=$pid $(cat /proc/$pid/comm 2>/dev/null)"
  done | sort -rn | head -5

# 添加临时 swap 文件
$ sudo fallocate -l 4G /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile

# 永久添加到 fstab
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 调整 swappiness(减少 swap 使用倾向)
$ sudo sysctl vm.swappiness=10
$ echo "vm.swappiness=10" | sudo tee -a /etc/sysctl.conf

# 查看当前 swappiness
$ cat /proc/sys/vm/swappiness
60    ← 默认值

# 验证 swap 是否生效
$ swapon --show
NAME       TYPE      SIZE USED PRIO
/swapfile  file      4G   2G   -2

根因:物理内存不足,系统频繁使用 swap(磁盘模拟内存),导致严重性能下降(swap I/O 比内存慢 1000 倍)。当 swap 也耗尽时,OOM Killer 开始终止进程。数据库等内存密集型应用最容易触发此问题。监控 swap 使用是性能调优的基础工作。

修复:添加 swap 文件应急,优化应用内存使用。调整 vm.swappiness=10 减少 swap 使用倾向(默认 60)。长期方案:增加物理内存或优化应用架构。监控 swap 使用:watch -n 5 free -h,设置告警阈值。对于数据库服务器,建议 swappiness 设为 1-10。

案例 F:云盘扩容后 df 仍显示旧大小

现象:在 AWS/Aliyun 控制台将 EBS 云盘从 100GB 扩容到 200GB,但 df -h 仍然显示 100GB。

# 检查块设备大小
$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda   202:0    0  200G  0 disk    ← 控制台已扩容
└─xvda1 202:1   0  100G  0 part /   ← 分区还是 100G

# 扩展分区表
$ sudo growpart /dev/xvda 1
# OK: migrated /dev/xvda partition 1

# 扩展文件系统
$ sudo resize2fs /dev/xvda1    # ext4
# 或
$ sudo xfs_growfs /             # xfs

# 验证
$ df -h /
Filesystem      Size  Used Avail Use%
/dev/xvda1      200G  45G  155G  23%    ← 已扩容

# LVM 环境需要额外步骤
$ sudo lvextend -l +100%FREE /dev/vg0/lv0
$ sudo resize2fs /dev/vg0/lv0

# 检查文件系统一致性(扩容后建议运行)
$ sudo e2fsck -f /dev/xvda1    # ext4
# 或
$ sudo xfs_repair /dev/xvda1   # xfs

# 查看分区类型
$ sudo fdisk -l /dev/xvda
# 确认分区表类型(MBR 或 GPT)
# GPT 分区用 sgdisk 操作

# 云环境扩容脚本示例
$ sudo growpart /dev/xvda 1 && sudo resize2fs /dev/xvda1

根因:云平台扩容的是底层块设备,分区表和文件系统需要手动扩展。这是云环境的常见操作,控制台扩容只完成了一半工作。很多新手在这一步卡住,以为控制台操作就完成了。

修复:依次执行 growpart(扩展分区表)→ resize2fs/xfs_growfs(扩展文件系统)。LVM 环境需要额外执行 lvextend。扩展前用 lsblk 确认块设备大小已更新。扩容后建议运行文件系统检查确保一致性。

案例 G:RAID 降级后重建失败——坏块导致同步中断

现象:RAID1 镜像组中一块磁盘故障更换后,重建过程反复失败,/proc/mdstat 显示进度卡在 20%。

# 查看 RAID 状态
$ cat /proc/mdstat
md0 : active raid1 sdb1[1] sda1[0]
      1953513472 blocks super 1.2 [2/1] [_U]
      [=>...................]  recovery =  8.3% (96435200/1953513472) finish=320.5min speed=99840K/sec
      bitmap: 0/15 pages [0KB], 65536KB chunk

# 查看内核日志中的磁盘错误
$ dmesg | grep -i "error\|bad\|sector"
[12345.678] md/raid1:md0: sdb1: sector 96435300 read error
[12345.679]md: bad sector 96435300 on md0

# 标记坏块并跳过
$ sudo mdadm --manage /dev/md0 --fail /dev/sdb1
$ sudo mdadm --manage /dev/md0 --remove /dev/sdb1
$ sudo mdadm --manage /dev/md0 --add /dev/sdc1    ← 添加新盘

# 检查新盘 SMART
$ sudo smartctl -t long /dev/sdc
$ sudo smartctl -l selftest /dev/sdc

根因:新换的磁盘存在坏扇区,RAID 重建时读写到坏块区域导致同步中断。SMART 可以提前发现磁盘潜在问题。

修复:用 mdadm --fail 标记故障盘,--remove 移除,--add 添加新盘。重建前先用 smartctl -t long 全面检测新盘健康状态。RAID 阵列的磁盘质量直接影响数据安全。

案例 H:ddrescue 数据恢复——磁盘部分可读时的抢救策略

现象:硬盘出现坏道,dd 读取到坏扇区时卡死,需要抢救可读区域的数据。

# 用 ddrescue(GNU ddrescue)而非 dd
# -d:直接磁盘访问  -r3:重试 3 次
# log 文件记录恢复进度,可中断后继续
$ sudo ddrescue -d -r3 /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log

# 查看恢复进度
$ ddrescueview /mnt/backup/sdb.log
# 显示已恢复/未恢复的区域分布

# 第一遍跳过坏扇区,第二遍重试
$ sudo ddrescue -d /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log
$ sudo ddrescue -d -r3 /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log

# 从镜像文件恢复文件系统
$ sudo fsck -y /mnt/backup/sdb.img
$ sudo mount -o loop /mnt/backup/sdb.img /mnt/recover

根因:传统 dd 遇到坏扇区会卡住或反复重试,导致恢复过程无限期延长。ddrescue 设计用于数据恢复,第一遍跳过坏扇区读取所有可读数据,然后重试坏扇区区域。

修复:使用 ddrescue 替代 dd 做数据恢复。log 文件支持断点续传,中断后可继续。先恢复好区域的数据,再尝试坏扇区。恢复完成后用 fsck 修复文件系统一致性。

案例 I:btrfs 子卷快照导致空间未释放

现象:删除 btrfs 子卷中的大量文件后,df -h 仍然显示空间未释放。

# 检查子卷和快照
$ sudo btrfs subvolume list /
ID 256 gen 100 top level 5 path @
ID 257 gen 105 top level 5 path @home
ID 258 gen 110 top level 256 path @snapshots/backup-20260701

# 原因:快照保留了已删除文件的旧版本
# 快照是只读的,即使源文件删除,快照中仍占用空间

# 查看快照占用空间
$ sudo btrfs filesystem du -s @snapshots/*
     Total   Exclusive  Name
    5.2GiB      5.2GiB  @snapshots/backup-20260701

# 删除旧快照释放空间
$ sudo btrfs subvolume delete @snapshots/backup-20260701

# 验证
$ df -h /data
Filesystem      Size  Used Avail Use%
/dev/sdb1       500G  280G  220G  56%    ← 空间已释放

根因:btrfs 快照是子卷在某一时刻的只读视源,快照中的数据不会随源子卷的删除而释放。只有删除快照本身才能回收空间。这是 btrfs CoW(Copy-on-Write)特性的正常行为。

修复:定期清理旧快照 btrfs subvolume delete。用 btrfs filesystem du -s 查看各子卷/快照的实际空间占用。设置快照保留策略(如保留最近 7 天)避免空间累积。

延伸阅读

↑ 回到顶部