文章目录
-
- 引言:磁盘问题为什么难排查
- 整体知识框架
- [一、IO 栈:数据从进程到磁盘的完整路径](#一、IO 栈:数据从进程到磁盘的完整路径)
- [二、分区方案:MBR/GPT 的本质区别与工具选择](#二、分区方案:MBR/GPT 的本质区别与工具选择)
-
- [2.1 MBR vs GPT:不只是大小限制](#2.1 MBR vs GPT:不只是大小限制)
- [2.2 三种分区工具的使用场景](#2.2 三种分区工具的使用场景)
- [2.3 分区对齐:一个容易忽略的性能陷阱](#2.3 分区对齐:一个容易忽略的性能陷阱)
- 三、LVM:弹性存储的核心原理与实操
-
- [3.1 三层模型:PV / VG / LV](#3.1 三层模型:PV / VG / LV)
- [3.2 基础操作:创建、查看、扩容](#3.2 基础操作:创建、查看、扩容)
- [3.3 LVM 快照:写时复制的数据结构](#3.3 LVM 快照:写时复制的数据结构)
- [3.4 Thin Provisioning:超额分配的利与弊](#3.4 Thin Provisioning:超额分配的利与弊)
- [四、IO 性能分析:从数字到根因](#四、IO 性能分析:从数字到根因)
-
- [4.1 iostat:指标背后的内核含义](#4.1 iostat:指标背后的内核含义)
- [4.2 iotop:定位到具体进程](#4.2 iotop:定位到具体进程)
- [4.3 iowait:被误解最多的指标](#4.3 iowait:被误解最多的指标)
- [4.4 完整的 IO 排查流程](#4.4 完整的 IO 排查流程)
- [五、df/du 联合分析:空间用在哪里](#五、df/du 联合分析:空间用在哪里)
-
- [5.1 df 与 du 差异的根本原因](#5.1 df 与 du 差异的根本原因)
- [5.2 du 使用技巧:快速找到大文件目录](#5.2 du 使用技巧:快速找到大文件目录)
- 六、文件描述符耗尽:一个被忽略的"磁盘"问题
-
- [6.1 现象与诊断](#6.1 现象与诊断)
- [6.2 调整 fd 上限](#6.2 调整 fd 上限)
- 七、联合分析场景:一次完整的磁盘问题排查
- 总结
引言:磁盘问题为什么难排查
CPU 高了看 top,内存涨了看 free,但磁盘问题往往更隐蔽------服务响应变慢,但 CPU 和内存都正常;日志写入突然卡顿,iostat 的 %util 看起来也不高;扩容之后空间又莫名其妙不够了。
原因在于磁盘问题涉及三个独立的层次:存储层 (怎么组织物理空间)、文件系统层 (怎么管理文件)、IO 栈层(数据从进程到磁盘经历了什么)。这三层之间的问题互相影响,但诊断工具各自只照亮一个层面,需要联合起来分析。
本文的核心思路是:先理解 IO 栈的结构,再看 LVM 如何在存储层提供弹性,最后建立一套"从现象到根因"的联合分析框架------不是命令清单,是排查思路。
整体知识框架
磁盘与存储管理
IO 栈结构
VFS 虚拟文件系统
Page Cache 缓存层
块设备层
调度器队列
驱动与物理介质
分区方案
MBR vs GPT 对比
fdisk/gdisk/parted 选择
分区对齐原则
文件系统格式化
LVM 体系
PV/VG/LV 三层模型
写时复制快照原理
在线扩容实操
Thin Provisioning
IO 性能分析
iostat 指标解读
iotop 进程级定位
iowait 的真正含义
blktrace 深度追踪
文件描述符
fd 耗尽现象
lsof/ulimit 诊断
内核参数调整
一、IO 栈:数据从进程到磁盘的完整路径
在学任何磁盘工具之前,先理解数据流动的路径------诊断问题时,知道"卡在哪一层"比知道"用什么命令"更重要。
命中
未命中
HDD
SSD/NVMe
用户进程 write 系统调用
VFS 虚拟文件系统层
Page Cache 命中?
直接写入 Page Cache 返回
分配新 Page 写入 Cache
内核后台 pdflush/writeback 异步刷盘
通用块层 Generic Block Layer
IO 调度器 CFQ/deadline/noop/mq-deadline
设备驱动层
存储类型
磁盘寻道 + 旋转等待 + 传输
Flash 控制器 FTL 转换 + 写入
这张图解释了几个常见的"奇怪现象":
为什么 write 返回了但数据还没真正写到磁盘? 因为默认情况下 write 只是写进 Page Cache 就返回了,内核异步刷盘。如果进程写完就退出,而内核还没来得及刷盘,机器掉电数据就丢失了------这是 fsync() 存在的原因。
为什么 %util 不高但服务还是慢? %util 反映设备的使用率,但如果 IO 调度器队列里堆积着大量等待的请求(await 高),单位时间的设备利用率可能并不高,但每个请求的等待时间却很长。
为什么 SSD 比 HDD 快这么多? HDD 每次随机 IO 都需要物理寻道(7200rpm 的磁盘寻道时间约 4-8ms),SSD 通过 FTL 层将逻辑地址映射到 Flash 块,随机 IO 没有寻道代价(约 0.1ms 级别)。这也解释了为什么 HDD 的 IO 延迟对随机读写极其敏感,而 SSD 相对容忍。
二、分区方案:MBR/GPT 的本质区别与工具选择
2.1 MBR vs GPT:不只是大小限制
GPT 分区表
MBR 分区表
局限性催生
MBR 主引导记录 512B
最多 4 个主分区
最大支持 2TB 磁盘
分区信息存在单一位置
EFI 保护 MBR
GPT 头部 + 128 个分区表项
最大支持 18EB 磁盘
分区信息有备份副本
每个分区有 GUID 唯一标识
GPT 相比 MBR 的实质性差异不只是"支持更大的磁盘",关键在于:
- 分区表冗余:GPT 在磁盘末尾保存了分区表的完整副本,MBR 损坏了数据会难以恢复,GPT 可以从备份恢复分区表
- 分区数量:MBR 最多 4 个主分区(或 3 主 + 1 扩展),GPT 默认支持 128 个分区,不需要"扩展分区"这个折中方案
- 唯一标识 :每个 GPT 分区有 UUID,不会因为磁盘添加顺序变化而导致
/dev/sdb1变成/dev/sdc1,/etc/fstab用 UUID 挂载更安全
服务器实践原则:2TB 以上的磁盘必须用 GPT;有 UEFI 固件的机器引导盘需要 GPT + EFI 分区;2TB 以下的数据盘两者都可以,但 GPT 更推荐。
2.2 三种分区工具的使用场景
| 工具 | 适用场景 | 特点 |
|---|---|---|
fdisk |
MBR 分区、老设备、脚本化操作 | 交互式,最广泛可用 |
gdisk |
GPT 分区专用 | fdisk 的 GPT 版 |
parted |
两者兼容,支持 GB 级参数、脚本调用 | 非交互友好,LVM 扩容常用 |
实际操作流程(以 GPT + parted 为例):
bash
# 1. 查看磁盘整体情况
lsblk -f # 显示磁盘、分区、文件系统、挂载点
fdisk -l /dev/sdb # 显示分区表详情
# 2. 使用 parted 创建 GPT 分区(适合脚本化)
parted /dev/sdb --script \
mklabel gpt \
mkpart primary ext4 1MiB 100% \
align-check optimal 1 # 验证分区对齐
# 3. 格式化
mkfs.ext4 -L data-disk /dev/sdb1
# 4. 获取 UUID 后写入 fstab(重启后自动挂载)
UUID=$(blkid -s UUID -o value /dev/sdb1)
echo "UUID=$UUID /mnt/data ext4 defaults,noatime 0 2" >> /etc/fstab
# 5. 挂载并验证
mount -a
df -h /mnt/data
2.3 分区对齐:一个容易忽略的性能陷阱
现代磁盘(HDD 和 SSD)的物理扇区是 4096 字节(4K),但对外报告的仍是 512 字节的逻辑扇区。如果分区起始位置不是 4K 的整数倍,每次写入都会跨越两个物理扇区,需要"读-改-写"三步操作,性能会下降 30-50%。
parted 的 align-check optimal 1 命令会验证分区对齐状态------如果返回 1 aligned,则对齐正常;返回 1 not aligned 则需要调整起始位置。现代版本的 parted 在创建分区时默认会对齐到 MiB 边界(1MiB = 256 个 4K 扇区),这已经满足所有常见磁盘的对齐要求。
三、LVM:弹性存储的核心原理与实操
3.1 三层模型:PV / VG / LV
LV 逻辑卷
VG 卷组
物理层
/dev/sdb1
PV 200GB
/dev/sdc1
PV 300GB
/dev/sdd1
PV 500GB
vg_data
总容量 1000GB
可用 600GB
lv_app
100GB
/app
lv_db
200GB
/var/lib/mysql
lv_log
100GB
/var/log
LVM 把物理磁盘抽象成一个存储池(VG),再从池里划分出逻辑卷(LV)给文件系统使用。这个抽象层带来三个传统分区没有的能力:
- 在线扩容:LV 不够用了,从 VG 里划拨更多空间过来,文件系统在线扩展,服务不需要停
- 跨磁盘合并:VG 可以横跨多块物理磁盘,LV 的实际数据条带化分布在各 PV 上
- 快照:对 LV 做快照,在不停服务的情况下得到一个一致性的数据副本
LVM 的核心单位是 PE(Physical Extent),默认 4MB。所有的空间分配都以 PE 为粒度,就像内存管理里的内存页。
3.2 基础操作:创建、查看、扩容
bash
# ─── 初始化物理卷 ────────────────────────────────────────────
pvcreate /dev/sdb1 /dev/sdc1
pvs # 查看 PV 信息(简洁)
pvdisplay /dev/sdb1 # 详细信息
# ─── 创建卷组 ────────────────────────────────────────────────
vgcreate vg_data /dev/sdb1 /dev/sdc1
vgs # 查看 VG 信息
vgdisplay vg_data
# ─── 创建逻辑卷 ──────────────────────────────────────────────
lvcreate -L 100G -n lv_app vg_data # 固定大小
lvcreate -l 50%FREE -n lv_db vg_data # 使用 50% 可用空间
lvs # 查看 LV 信息
# ─── 在 LV 上创建文件系统并挂载 ─────────────────────────────
mkfs.xfs /dev/vg_data/lv_app
mount /dev/vg_data/lv_app /app
在线扩容(最常用操作):
bash
# 场景:/var/lib/mysql 所在 LV 空间不足,需要扩容 50GB
# 步骤 1:确认 VG 有足够可用空间
vgs vg_data
# VFree 列要 >= 50GB,否则需要先添加新 PV
# 步骤 2:扩展 LV
lvextend -L +50G /dev/vg_data/lv_db
# 步骤 3:扩展文件系统(不同文件系统命令不同)
# ext4:
resize2fs /dev/vg_data/lv_db
# xfs(xfs 只支持扩大,不支持缩小):
xfs_growfs /var/lib/mysql
# 验证
df -h /var/lib/mysql
整个过程 MySQL 不需要停止------LV 扩展是元数据操作,文件系统扩展 resize2fs/xfs_growfs 在挂载状态下操作是安全的。
向 VG 添加新磁盘(扩容前提):
bash
# 新增了一块磁盘 /dev/sdd,初始化并加入现有 VG
pvcreate /dev/sdd
vgextend vg_data /dev/sdd
# 此后 VG 可用空间增加,可以继续 lvextend
3.3 LVM 快照:写时复制的数据结构
LVM 快照是一个容易被误解的功能,多数文章只说"用来备份",但不解释它为什么可以在不停服务的情况下得到一致性副本。
快照元数据表 快照 LV (snap_db) 原始 LV (lv_db) 应用进程 快照元数据表 快照 LV (snap_db) 原始 LV (lv_db) 应用进程 快照创建瞬间:元数据表为空,不复制数据 快照保留了创建时刻的完整镜像 原始 LV 继续正常使用 写入数据块 Block 检查 未修改,执行写前复制 将 Block 标记 Block 写入新值到 Block
关键原理 :快照创建时,只记录"快照时刻 LV 的元数据",不复制任何数据。当原始 LV 上的某个数据块第一次被修改时,LVM 先把该块的旧值复制到快照空间,再写入新值。这就是"写时复制"(Copy-on-Write)。
这意味着:
- 快照创建是瞬间完成的(只是元数据操作)
- 快照初始占用空间很小,随着原始 LV 上的数据变化而增长
- 快照大小需要预估原始 LV 在备份期间会发生多少变化,预留足够空间,否则快照满了会自动失效
备份使用流程:
bash
# 1. 创建快照(5GB 快照空间,用于备份期间的写时复制缓冲)
lvcreate -L 5G -s -n snap_db /dev/vg_data/lv_db
# 2. 挂载快照(只读方式,得到备份时刻的一致性视图)
mkdir /mnt/snap_db
mount -o ro /dev/vg_data/snap_db /mnt/snap_db
# 3. 备份数据(快照提供一致性视图,原始 LV 正常运行)
tar -czf /backup/mysql-$(date +%Y%m%d).tar.gz -C /mnt/snap_db .
# 4. 备份完成,删除快照
umount /mnt/snap_db
lvremove /dev/vg_data/snap_db
3.4 Thin Provisioning:超额分配的利与弊
普通 LVM 创建 LV 时必须明确声明大小,从 VG 中占用实际空间。Thin Provisioning 允许创建"容量超过物理空间"的逻辑卷:先创建一个 Thin Pool,再从中创建 Thin Volume,Thin Volume 声明的大小可以超过 Pool 的实际容量------前提是实际使用量不超过 Pool。
bash
# 创建 Thin Pool(100GB 物理空间)
lvcreate -L 100G -T vg_data/thin_pool
# 从 Pool 里创建超额 Thin Volume(声明 50GB,但实际按需分配)
lvcreate -V 50G -T vg_data/thin_pool -n lv_thin_app
lvcreate -V 50G -T vg_data/thin_pool -n lv_thin_db
# 两个 LV 声明合计 100GB,但如果实际用量不超过 100GB,没有问题
# 监控 Pool 使用率(关键!超出会导致所有 Thin LV 变为只读)
lvs -a -o name,lv_attr,lv_size,pool_lv,data_percent
生产使用风险 :Thin Pool 耗尽时,所有依赖这个 Pool 的 Thin Volume 会变成只读,服务直接挂掉。所以使用 Thin Provisioning 时必须设置告警(data_percent > 80% 时报警)并定期扩容 Pool。容器环境(Docker 的 devicemapper 驱动)大量使用 Thin Provisioning,这也是 Docker 磁盘使用超预期的根本原因之一。
四、IO 性能分析:从数字到根因
4.1 iostat:指标背后的内核含义
iostat 是磁盘性能分析的第一站,但它的字段容易被误读。
bash
# 每 2 秒刷新一次,显示 x 模式(扩展统计)
iostat -xz 2
典型输出:
Device r/s w/s rMB/s wMB/s rrqm/s wrqm/s r_await w_await svctm %util
sda 10.5 45.2 0.41 1.78 0.20 12.50 4.23 28.50 0.82 22.8
nvme0n1 120.0 380.0 15.0 47.5 0.00 0.00 0.15 0.12 0.08 40.2
关键字段解读:
| 字段 | 含义 | 排查关注点 |
|---|---|---|
r/s / w/s |
每秒完成的读/写请求数(IOPS) | 对比磁盘标称 IOPS 上限 |
rMB/s / wMB/s |
读写吞吐量 | 对比磁盘标称带宽上限 |
rrqm/s / wrqm/s |
每秒合并的读/写请求数 | 高值说明顺序 IO,调度器在合并请求 |
r_await / w_await |
读/写请求的平均等待时间(ms) | 核心指标,HDD 正常 <20ms,SSD <1ms |
svctm |
平均服务时间(已弃用,不再准确) | 忽略此字段 |
%util |
设备使用率 | 容易误读,见下方说明 |
%util 为什么会撒谎 :%util 表示"采样周期内设备处于活跃状态的时间比例",对 HDD 有意义(因为 HDD 同一时刻只能处理一个请求),但对支持队列深度(QD)的 NVMe SSD,%util 100% 并不意味着设备饱和------设备可能在并行处理 32 个请求而总吞吐量还远没到上限。
更可靠的饱和判断方式:
bash
# 查看 IO 队列长度(通过 /proc/diskstats 计算)
# 或者用 iostat -x 看 aqu-sz(平均队列长度)
iostat -x 2 | grep -E "Device|sda|nvme"
# aqu-sz > 1(HDD)或 > 8(NVMe)时,考虑设备已接近饱和
4.2 iotop:定位到具体进程
当 iostat 显示磁盘 IO 很高,但不知道是哪个进程导致的,iotop 是下一步工具。
bash
# 只显示有实际 IO 的进程(过滤掉 0 IO 的进程)
iotop -o
# 批处理模式(适合脚本或日志)
iotop -o -b -n 5 # 采样 5 次后退出
iotop -o -b -d 2 # 每 2 秒采样
# 如果没有 iotop,用 /proc 手工查
# 找到 IO 最多的进程 PID
for pid in /proc/[0-9]*; do
awk '/^(read_bytes|write_bytes)/{sum+=$2}END{print sum, FILENAME}' \
"$pid/io" 2>/dev/null
done | sort -rn | head -10
iotop 输出中的关键列:
DISK READ/DISK WRITE:进程当前的磁盘 IO 速率SWAPIN:从交换空间读取的比例(高值说明内存压力)IO>:当前 IO 优先级,0/4是默认的 best-effort 调度 class
4.3 iowait:被误解最多的指标
iowait 出现在 top 和 vmstat 里,含义是"CPU 在等待 IO 完成时的空闲时间百分比"。
没有可运行进程
有进程在等 IO
意味着
意味着
不一定意味着
CPU 空闲
为什么空闲?
idle 空闲
iowait
iowait 高意味着什么?
至少有一个 CPU 在空闲
有进程正在等待 IO 完成
磁盘真的是瓶颈
例如:进程在等网络 IO
也会计入 iowait
单线程 IO 程序在单核上
iowait 会高,但磁盘不忙
iowait 高不等于磁盘是瓶颈 ,需要结合 iostat 的 await 和吞吐量一起看:
bash
# 联合分析示例
# 终端 1:持续监控 CPU
vmstat 2
# 终端 2:持续监控磁盘
iostat -xz 2
# 如果 iowait 高 + iostat await 高 + 吞吐量接近磁盘上限
# → 磁盘确实是瓶颈
# 如果 iowait 高 + iostat await 正常 + 吞吐量不高
# → 可能是进程在等网络 IO,或者单线程程序的 IO 等待
4.4 完整的 IO 排查流程
HDD await >50ms
SSD await >5ms
await 正常
顺序写入大文件
随机小 IO 高频
读 IO 异常高
服务响应变慢
iostat -xz 2 观察
await 是否异常?
确认磁盘延迟问题
查其他方向:CPU/网络/内存
iotop -o 定位进程
是哪类 IO?
可能是日志/备份占满带宽
数据库/缓存 IO 压力
Page Cache 失效 或 冷启动
检查日志轮转配置
考虑 ionice 降低优先级
检查查询是否走全表扫描
考虑 buffer pool 大小
检查内存是否不足导致 Cache 频繁淘汰
五、df/du 联合分析:空间用在哪里
df 和 du 是最常用的空间分析工具,但两者报告的数字有时候差异很大------这是一个生产中真实存在的困惑。
5.1 df 与 du 差异的根本原因
bash
# 常见困惑:df 显示 /var 使用了 80GB,du -sh /var 只显示 50GB
df -h /var
# Filesystem Size Used Avail Use% Mounted on
# /dev/sda2 100G 80G 20G 80% /var
du -sh /var
# 50G /var
# 差异 30GB 去哪了?
原因:已删除但仍被进程打开的文件 。当一个进程打开了一个文件,即使该文件被 rm 删除了(即目录项被删除,inode 引用计数减 1),只要进程还没关闭文件描述符,实际的磁盘空间不会被释放。du 遍历目录树,看不到这些"已删除但仍占用"的文件;df 从文件系统层面统计,能看到这些空间。
定位和清理:
bash
# 找到持有已删除文件的进程
lsof +L1
# +L1 = link count < 1,即文件已删除但 fd 还开着
# 输出格式:进程名 PID 用户 FD 文件大小 文件名(deleted)
# 常见场景:日志文件被 logrotate 删除,但程序还在写旧文件
# 解决方案 1:重启该进程(让它关闭旧 fd,重新打开新日志文件)
# 解决方案 2:如果不能重启,向进程发 SIGHUP 让它重新打开日志
kill -HUP <PID>
5.2 du 使用技巧:快速找到大文件目录
bash
# 按目录大小排序,只显示第一层(不递归)
du -h --max-depth=1 /var | sort -rh | head -20
# 找出大于 1GB 的文件
find /var -type f -size +1G -exec ls -lh {} \;
# 找出最近 24 小时内修改的大文件(排查突然增大的日志)
find /var/log -type f -mtime -1 -size +100M
# 快速统计某个目录的文件数量(inode 占用分析)
find /path -type f | wc -l
六、文件描述符耗尽:一个被忽略的"磁盘"问题
文件描述符(fd)耗尽不是磁盘空间问题,但症状和磁盘问题很像------应用报"too many open files",无法创建新连接,无法写日志,看起来像磁盘坏了。
6.1 现象与诊断
bash
# 1. 查看当前系统 fd 总使用量
cat /proc/sys/fs/file-nr
# 输出:已分配数 空闲数 最大限制
# 例:32768 0 1048576(已用 32768,最大 1048576)
# 2. 查看单个进程的 fd 使用情况
ls -la /proc/<PID>/fd | wc -l # 该进程打开的 fd 数
lsof -p <PID> | wc -l # 更详细
# 3. 查看进程的 fd 限制
cat /proc/<PID>/limits | grep "open files"
# 4. 找出 fd 使用最多的进程
lsof 2>/dev/null | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
6.2 调整 fd 上限
bash
# ─── 临时调整(重启后失效)───────────────────────────────────
ulimit -n 65535 # 当前 shell 及其子进程的软限制
# 对运行中的进程(不需要重启)
prlimit --pid <PID> --nofile=65535:65535
# ─── 持久化调整 ──────────────────────────────────────────────
# /etc/security/limits.conf(PAM 方式,用户级别)
cat >> /etc/security/limits.conf <<EOF
* soft nofile 65535
* hard nofile 65535
EOF
# /etc/systemd/system/myapp.service(systemd 管理的服务)
[Service]
LimitNOFILE=65535
# ─── 系统级别 fd 上限 ────────────────────────────────────────
# /etc/sysctl.conf(所有进程的系统总上限)
fs.file-max = 1048576
# 生效
sysctl -p
七、联合分析场景:一次完整的磁盘问题排查
场景 :某台服务器上的 MySQL 响应变慢,df 显示 /var/lib/mysql 所在分区使用率 92%,系统 iowait 维持在 35-40%。
bash
# 第一步:确认空间实际占用来源
du -h --max-depth=2 /var/lib/mysql | sort -rh | head -20
# 发现 binlog 文件占用了 20GB,正常情况应该只保留 7 天
# 第二步:确认 binlog 占用是否可清理(查 MySQL 是否在使用)
mysql -e "SHOW BINARY LOGS;"
mysql -e "SHOW MASTER STATUS;"
# 确认 binlog 保留策略
mysql -e "SHOW VARIABLES LIKE 'expire_logs_days';"
# 如果 expire_logs_days=0,表示不自动清理
# 第三步:分析磁盘 IO 是否和 binlog 有关
iostat -xz 2 &
iotop -o -b -d 2 -n 5
# 如果 mysqld 的写 IO 很高,而 binlog purge 正在进行,可能是 purge 操作造成的 IO 峰值
# 第四步:清理旧 binlog 并修复自动清理策略
mysql -e "SET GLOBAL expire_logs_days = 7;"
mysql -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"
# 第五步:确认 LVM 状态,考虑是否扩容
lvs vg_data
vgs vg_data
# 如果 VG 有可用空间,立即扩容 LV 防止满盘
lvextend -L +20G /dev/vg_data/lv_db
xfs_growfs /var/lib/mysql
这个场景串联了本文的多个工具:df/du 定位空间来源、iostat/iotop 分析 IO 行为、LVM 提供在线扩容兜底------三个层次各司其职。
总结
磁盘管理核心
理解 IO 栈
Page Cache 解释 write 语义
await 比 util 更准确
iowait 不等于磁盘瓶颈
存储方案选择
GPT 优于 MBR
分区对齐防性能陷阱
LVM 是生产必备
LVM 三个核心用法
在线扩容无停机
快照写时复制备份
Thin Provisioning 监控为先
联合分析习惯
df+du 找空间差异
iostat+iotop 定位 IO 进程
lsof 找已删除占用文件
fd 管理
limits.conf 持久化
systemd LimitNOFILE
prlimit 不重启调整
磁盘问题的排查本质是"分层"------确认是存储层(空间不足)、文件系统层(inode 耗尽、已删除文件占用)还是 IO 栈层(延迟高、带宽满),再对应层次选择工具。跳过分层直接用单一工具排查,往往会绕很多弯路。
LVM 在生产环境中的价值不是"高级功能",而是兜底能力------在磁盘快满的紧急情况下,有 LVM 的服务器可以在5分钟内完成在线扩容,没有 LVM 的服务器需要停机操作。这种差距在凌晨告警时体现得最为明显。