Linux 磁盘管理实战:LVM、RAID 与数据恢复
从一次报警说起
凌晨两点,手机弹出 Zabbix 告警:/data 分区使用率 92%。SSH 上去一看,df -h` 确认只剩 80GB。但这是一台日志服务器,删旧日志只是缓兵之计——日志增长率每月 50GB,加盘才是正解。
问题来了:这台机器上线时图省事,/data 直接用了一个独立分区,不是 LVM。扩容意味着要么停机用分区工具调整大小(风险极大),要么新建分区迁移数据(需要停服)。
如果当初用了 LVM,在线加一块盘、扩容逻辑卷,整个过程不超过 5 分钟,零停机。
这篇文章不讲磁盘原理(CHS/LBA 那些已经过时了),只讲生产环境磁盘管理的实战操作:LVM 管理、RAID 配置、文件系统修复、数据恢复。
一、磁盘基础知识
1.1 磁盘识别
# 查看所有块设备
lsblk
# NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# sda 8:0 0 500G 0 disk
# ├─sda1 8:1 0 1G 0 part /boot
# └─sda2 8:2 0 499G 0 part
# └─vg0-root 253:0 0 499G 0 lvm /
# sdb 8:16 0 2T 0 disk
# sdc 8:32 0 2T 0 disk
# nvme0n1 259:0 0 500G 0 disk
# 查看磁盘详细信息
fdisk -l /dev/sdb
# 查看磁盘健康状态(SMART)
smartctl -a /dev/sda
# 重点关注:
# Reallocated_Sector_Ct > 0 → 有坏道
# Current_Pending_Sector > 0 → 待处理坏扇区
# UDMA_CRC_Error_Count > 0 → 数据线问题
# 查看磁盘 IO 性能
iostat -xdm 1
# 重点关注:
# %util → 磁盘使用率(持续 > 80% 说明瓶颈)
# await → IO 等待时间(> 20ms 偏高,> 100ms 严重)
# r/s w/s → 每秒读写次数
# rkB/s wkB/s → 每秒读写吞吐量
1.2 分区管理
# 新磁盘分区(推荐 parted,支持 GPT)
parted /dev/sdb mklabel gpt
parted /dev/sdb mkpart primary 0% 100%
# 或用 fdisk(传统,只支持 MBR,最大 2TB)
fdisk /dev/sdb
# n → 新建分区
# p → 主分区
# 1 → 分区号
# 回车 → 默认起始
# 回车 → 默认结束(用满)
# w → 保存
# 格式化
mkfs.xfs /dev/sdb1 # CentOS/RHEL 推荐
mkfs.ext4 /dev/sdb1 # Ubuntu/Debian 常用
# 挂载
mkdir /data
mount /dev/sdb1 /data
# 持久化挂载
echo '/dev/sdb1 /data xfs defaults 0 0' >> /etc/fstab
# 或用 UUID(更可靠)
blkid /dev/sdb1
echo 'UUID=xxx-xxx /data xfs defaults 0 0' >> /etc/fstab
为什么推荐 LVM 而不是直接分区? 因为 LVM 可以在线扩容、缩容、快照、跨磁盘。直接分区一旦空间分配完成,调整就非常麻烦。
二、LVM 逻辑卷管理
2.1 LVM 三层架构
物理磁盘 (PV) → 卷组 (VG) → 逻辑卷 (LV) → 文件系统
/dev/sdb vg_data lv_data /data
/dev/sdc lv_logs /var/log
- PV (Physical Volume):物理卷,一块磁盘或一个分区
- VG (Volume Group):卷组,把多个 PV 组合成一个存储池
- LV (Logical Volume):逻辑卷,从 VG 中分配空间,相当于"虚拟分区"
2.2 创建 LVM
# 1. 创建 PV
pvcreate /dev/sdb /dev/sdc
# Physical volume "/dev/sdb" successfully created.
# Physical volume "/dev/sdc" successfully created.
# 查看
pvdisplay
# PV Name /dev/sdb
# VG Name vg_data
# PV Size 2.00 TiB
# Allocatable yes
pvs # 简洁版
# PV VG Fmt Attr PSize PFree
# /dev/sdb vg_data lvm2 a-- 2.00t 1.00t
# /dev/sdc vg_data lvm2 a-- 2.00t 2.00t
# 2. 创建 VG
vgcreate vg_data /dev/sdb /dev/sdc
# Volume group "vg_data" successfully created
# 查看
vgs
# VG #PV #LV #SN Attr VSize VFree
# vg_data 2 0 0 wz--n- 4.00t 4.00t
# 3. 创建 LV
# 方式一:指定大小
lvcreate -L 500G -n lv_data vg_data
# 方式二:使用全部剩余空间
lvcreate -l 100%FREE -n lv_logs vg_data
# 方式三:指定百分比
lvcreate -l 50%VG -n lv_data vg_data
# 查看
lvs
# LV VG Attr LSize
# lv_data vg_data -wi-ao---- 500.00g
# lv_logs vg_data -wi-ao---- 3.50t
# 4. 格式化并挂载
mkfs.xfs /dev/vg_data/lv_data
mkdir /data
mount /dev/vg_data/lv_data /data
# 持久化
echo '/dev/vg_data/lv_data /data xfs defaults 0 0' >> /etc/fstab
2.3 在线扩容(重点!)
这是 LVM 最大的价值——不停机扩容。
# 场景:/data 空间不足,加了一块新磁盘 /dev/sdd
# 1. 将新磁盘加入 VG
pvcreate /dev/sdd
vgextend vg_data /dev/sdd
# 2. 扩展 LV(使用 VG 中全部剩余空间)
lvextend -l 100%FREE /dev/vg_data/lv_data
# 或指定大小
lvextend -L +500G /dev/vg_data/lv_data
# 或指定最终大小
lvextend -L 2T /dev/vg_data/lv_data
# 3. 扩展文件系统(关键!很多人忘这步)
# XFS:
xfs_growfs /data
# EXT4:
resize2fs /dev/vg_data/lv_data
# 4. 验证
df -h /data
# /dev/mapper/vg_data-lv_data 4.0T 1.2T 2.8T 30% /data
整个过程不需要卸载文件系统,不需要停服务。 这就是 LVM 的魅力。
2.4 在线缩容
# 注意:XFS 不支持缩容!只有 EXT4 可以缩容。
# EXT4 缩容:
# 1. 先卸载(必须!)
umount /data
# 2. 检查文件系统
e2fsck -f /dev/vg_data/lv_data
# 3. 缩小文件系统
resize2fs /dev/vg_data/lv_data 300G
# 4. 缩小 LV
lvreduce -L 300G /dev/vg_data/lv_data
# 警告: shrinking a logical volume with data on it may cause data loss!
# 输入 y 确认
# 5. 重新挂载
mount /dev/vg_data/lv_data /data
安全缩容建议:先缩小文件系统到比目标大一点(如目标 300G,先缩到 310G),再缩小 LV 到 300G,最后再 resize2fs 到 300G。多一步但更安全。
2.5 LVM 快照
快照是 LVM 的另一个杀手级功能——瞬间创建一个逻辑卷的时间点副本。
# 创建快照(大小为源 LV 的 10-20%,存储变化的数据)
lvcreate -L 50G -s -n lv_data_snapshot /dev/vg_data/lv_data
# 挂载快照
mkdir /mnt/snapshot
mount /dev/vg_data/lv_data_snapshot /mnt/snapshot
# 现在可以在快照上做备份,不影响源卷的正常读写
# 备份完成后删除快照
umount /mnt/snapshot
lvremove /dev/vg_data/lv_data_snapshot
快照的实际用途:
- 数据库热备份:先做 LVM 快照,再从快照中备份数据文件,保证一致性
- 变更前回滚点:升级前做快照,出问题可以从快照恢复
- 测试环境:从生产快照创建测试卷
快照恢复:
# 将源卷回滚到快照时间点(源卷数据丢失!)
lvconvert --merge /dev/vg_data/lv_data_snapshot
# 需要重新激活 LV 才生效
lvchange -an /dev/vg_data/lv_data
lvchange -ay /dev/vg_data/lv_data
2.6 LVM 常用诊断
# 查看所有 PV/VG/LV
pvs && vgs && lvs
# 详细信息
pvdisplay
vgdisplay
lvdisplay
# 查看 LV 的映射关系
lsblk
# 查看 VG 的物理分布
pvdisplay -m
# 查看哪些 PV 在哪些 VG
pvdisplay -C
# LVM 日志
journalctl -u lvm2-monitor
三、RAID 阵列配置
3.1 RAID 级别选择
| 级别 | 最少磁盘 | 冗余 | 可用容量 | 适用场景 |
|---|---|---|---|---|
| RAID 0 | 2 | 无 | 100% | 临时数据、性能优先 |
| RAID 1 | 2 | 1块 | 50% | 系统盘、小容量高可靠 |
| RAID 5 | 3 | 1块 | (n-1)/n | 通用、读多写少 |
| RAID 6 | 4 | 2块 | (n-2)/n | 大容量、高可靠 |
| RAID 10 | 4 | 每对1块 | 50% | 高性能+高可靠 |
生产建议:
- 系统盘:RAID 1(两块 SSD)
- 数据盘:RAID 10(性能要求高)或 RAID 6(容量要求高)
- 备份盘:RAID 6(大容量 + 双盘容错)
3.2 软件 RAID 配置(mdadm)
# 安装
apt install mdadm # Ubuntu
yum install mdadm # CentOS
# 创建 RAID 10(4块磁盘)
mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/sd{b,c,d,e}
# mdadm: Defaulting to version 1.2 metadata
# mdadm: array /dev/md0 started.
# 查看状态
mdadm --detail /dev/md0
# State : clean
# Active Devices : 4
# Working Devices : 4
# Layout : near=2
cat /proc/mdstat
# md0 : active raid10 sde[3] sdd[2] sdc[1] sdb[0]
# 4194304 blocks super 1.2 512K chunks 2 nearcopies [4/4] [UUUU]
# 格式化并使用
mkfs.xfs /dev/md0
mount /dev/md0 /data
# 保存配置(重启后自动组装)
mdadm --detail --scan >> /etc/mdadm.conf
# Ubuntu 还需要更新 initramfs
update-initramfs -u
3.3 RAID 维护
# 查看健康状态
mdadm --detail /dev/md0 | grep -E "State|Active|Working|Failed|Spare"
# 监控重建进度
watch -n 5 'cat /proc/mdstat'
# 替换故障磁盘
# 1. 标记故障
mdadm /dev/md0 --fail /dev/sdb
# 2. 移除
mdadm /dev/md0 --remove /dev/sdb
# 3. 插入新磁盘后添加
mdadm /dev/md0 --add /dev/sdb
# 4. 观察重建
cat /proc/mdstat
# [UU_U] → [UUUU] 重建中
# 添加热备盘
mdadm /dev/md0 --add-spare /dev/sdf
# 停止并删除 RAID(谨慎!)
umount /dev/md0
mdadm --stop /dev/md0
mdadm --zero-superblock /dev/sd{b,c,d,e}
3.4 RAID + LVM 组合
生产环境推荐 RAID 做底层冗余,LVM 做上层管理:
# 1. 先建 RAID
mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/sd{b,c,d,e}
# 2. 在 RAID 设备上建 LVM
pvcreate /dev/md0
vgcreate vg_data /dev/md0
lvcreate -L 1T -n lv_data vg_data
# 3. 格式化使用
mkfs.xfs /dev/vg_data/lv_data
mount /dev/vg_data/lv_data /data
四、文件系统管理
4.1 XFS vs EXT4
| 特性 | XFS | EXT4 |
|---|---|---|
| 最大文件 | 8EB | 16TB |
| 在线扩容 | 支持 | 支持 |
| 在线缩容 | 不支持 | 支持 |
| 元数据日志 | 有 | 有 |
| 快照支持 | LVM快照 | LVM快照 |
| 默认 | RHEL/CentOS | Ubuntu/Debian |
| 大文件性能 | 优秀 | 良好 |
| 小文件性能 | 良好 | 优秀 |
选择建议:CentOS/RHEL 用 XFS,Ubuntu 用 EXT4,不确定就选 XFS(扩容方便)。
4.2 文件系统检查与修复
# XFS 检查(只读,不修复)
xfs_db -c frag /dev/vg_data/lv_data
# 查看碎片率
# XFS 修复(必须卸载!)
umount /data
xfs_repair /dev/vg_data/lv_data
# 如果修复失败,加 -L 强制日志清零(可能丢数据)
xfs_repair -L /dev/vg_data/lv_data
# EXT4 检查
e2fsck -f /dev/vg_data/lv_data
# EXT4 修复
e2fsck -p /dev/vg_data/lv_data # 自动修复
e2fsck -y /dev/vg_data/lv_data # 全部回答 yes
# 开机自动检查
tune2fs -c 30 /dev/sda1 # 每30次挂载检查一次
tune2fs -i 30d /dev/sda1 # 每30天检查一次
4.3 磁盘碎片整理
# XFS 查看碎片
xfs_db -c frag /dev/vg_data/lv_data
# 实际块数 / 理想块数 = 碎片率
# 碎片率 > 30% 才需要整理
# XFS 整理(需要挂载状态)
xfs_fsr /dev/vg_data/lv_data
# EXT4 查看碎片
e4defrag -c /data
# Fragmentation score: 5
# 0-30 不需要整理
# EXT4 整理
e4defrag /data
注意:现代文件系统碎片化问题不严重,XFS 和 EXT4 都有预分配机制。SSD 更不需要整理碎片。
4.4 磁盘配额
# 启用配额(XFS)
# /etc/fstab 中添加 prjquota 或 usrquota
echo '/dev/vg_data/lv_data /data xfs defaults,prjquota 0 0' >> /etc/fstab
mount -o remount /data
# 设置目录配额
xfs_quota -x -c 'limit bhard=50g bsoft=40g -d /data/logs'
# 查看配额
xfs_quota -x -c 'report -h'
五、数据恢复实战
5.1 误删文件恢复(extundelete)
# 场景:rm -rf 误删了 /data/important/
# 第一步:立即将分区挂载为只读,防止数据被覆盖!
mount -o remount,ro /data
# 安装工具
apt install extundelete # Ubuntu
# 恢复指定目录
extundelete /dev/vg_data/lv_data --restore-directory important
# 恢复指定文件
extundelete /dev/vg_data/lv_data --restore-file important/config.yaml
# 恢复全部
extundelete /dev/vg_data/lv_data --restore-all
# 恢复的文件在当前目录的 RECOVERED_FILES/ 下
注意:extundelete 只支持 EXT3/EXT4。XFS 没有好的文件级恢复工具。一旦误删,恢复概率取决于数据块是否被覆盖。
5.2 误删分区恢复(testdisk)
# 场景:误删了分区表
apt install testdisk
# 运行
testdisk
# 1. 选择磁盘
# 2. 选择 Intel (MBR) 或 GPT
# 3. 选择 Analyse → Quick Search
# 4. testdisk 会扫描并显示找到的分区
# 5. 确认后选择 Write 恢复分区表
5.3 LVM PV 丢失恢复
# 场景:重装系统后 VG 找不到了,但 PV 数据还在
# 扫描 PV
pvscan
# PV /dev/sdb VG vg_data lvm2 [2.00 TiB / 0 free]
# 如果 pvscan 没找到,手动扫描
pvcreate --restorefile /etc/lvm/archive/vg_data_*.vg /dev/sdb
# 恢复 VG
vgcfgrestore vg_data
# 激活 VG
vgchange -ay vg_data
# 检查 LV
lvs
mount /dev/vg_data/lv_data /data
5.4 RAID 超级块损坏恢复
# 场景:md0 突然消失,mdadm --assemble 失败
# 查看磁盘上的 RAID 超级块
mdadm --examine /dev/sdb
# 如果超级块还在,重新组装
mdadm --assemble /dev/md0 /dev/sd{b,c,d,e}
# 如果超级块损坏,强制组装
mdadm --assemble --force /dev/md0 /dev/sd{b,c,d,e}
# 如果某块盘的超级块损坏,用其他盘恢复
mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/sdb /dev/sdc /dev/sdd missing
# 注意:用 missing 替代坏盘,然后正常 add 新盘
5.5 数据恢复的黄金法则
1. 发现数据丢失 → 立即停止写入!挂载只读!
2. 不要在原磁盘上操作 → 先 dd 整盘镜像到另一块盘
3. 在镜像上做恢复操作
4. 恢复是概率事件,不保证100%成功
5. 最佳恢复方案 = 定期备份(lsnapshot/rsync/bareos)
# 创建磁盘镜像(在恢复前先做镜像)
dd if=/dev/sdb of=/dev/sdc bs=4M status=progress
# 或创建到文件
dd if=/dev/sdb of=/backup/sdb.img bs=4M status=progress
# 在镜像上操作
losetup /dev/loop0 /backup/sdb.img
# 然后对 /dev/loop0 做恢复操作
六、磁盘性能测试
6.1 基准测试(fio)
# 安装
apt install fio
# 顺序读写测试(适合评估大文件性能)
fio --name=seqwrite --ioengine=libaio --iodepth=1 \
--rw=write --bs=1M --size=1G --numjobs=1 \
--runtime=60 --time_based --directory=/data
fio --name=seqread --ioengine=libaio --iodepth=1 \
--rw=read --bs=1M --size=1G --numjobs=1 \
--runtime=60 --time_based --directory=/data
# 随机读写测试(适合评估数据库性能)
fio --name=randwrite --ioengine=libaio --iodepth=32 \
--rw=randwrite --bs=4k --size=1G --numjobs=4 \
--runtime=60 --time_based --directory=/data
fio --name=randread --ioengine=libaio --iodepth=32 \
--rw=randread --bs=4k --size=1G --numjobs=4 \
--runtime=60 --time_based --directory=/data
# 混合读写测试(模拟生产负载)
fio --name=mixed --ioengine=libaio --iodepth=32 \
--rw=randrw --rwmixread=70 --bs=4k --size=1G --numjobs=4 \
--runtime=60 --time_based --directory=/data
性能参考值:
| 磁盘类型 | 顺序写 | 顺序读 | 随机写(IOPS) | 随机读(IOPS) |
|---|---|---|---|---|
| HDD 7200rpm | 150 MB/s | 180 MB/s | 100 | 150 |
| SATA SSD | 500 MB/s | 550 MB/s | 80K | 90K |
| NVMe SSD | 2000 MB/s | 3000 MB/s | 300K | 500K |
| RAID 10 (HDD×4) | 400 MB/s | 500 MB/s | 350 | 500 |
6.2 简单测试(dd)
# 写测试(注意:不准确的基准,但快速粗略)
dd if=/dev/zero of=/data/testfile bs=1M count=1024 oflag=direct
# 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 2.1 s, 500 MB/s
# 读测试
dd if=/data/testfile of=/dev/null bs=1M iflag=direct
rm /data/testfile
6.3 IO 监控
# 实时 IO 监控
iostat -xdm 1
# 进程级 IO 监控
iotop -oP
# 只显示有 IO 活动的进程
# 查看哪个进程在读写磁盘
lsof /data
七、生产环境最佳实践
7.1 磁盘分区规划
| 挂载点 | 大小 | 类型 | 说明 |
|---|---|---|---|
| /boot | 1GB | 独立分区 | 存放内核,避免 LVM |
| / | 50-100GB | LVM | 系统根分区 |
| /data | 按需 | LVM on RAID | 数据分区 |
| swap | 内存×1~2 | 独立分区或文件 | 物理内存的1-2倍 |
| /var/log | 20-50GB | LVM | 日志分区,防止日志撑爆根分区 |
# /etc/fstab 生产配置示例
/dev/vg0/root / xfs defaults 0 0
UUID=xxx /boot xfs defaults 0 0
/dev/vg0/swap swap swap defaults 0 0
/dev/vg_data/lv_data /data xfs defaults,noatime,nodiratime 0 0
/dev/vg0/logs /var/log xfs defaults 0 0
noatime:不更新文件访问时间,减少 IO。对数据盘和日志盘推荐。
7.2 定期检查清单
#!/bin/bash
# disk-health-check.sh
echo "=== 磁盘使用率 ==="
df -h | grep -vE 'tmpfs|loop'
echo -e "\n=== LVM 状态 ==="
lvs 2>/dev/null || echo "LVM not configured"
vgs 2>/dev/null
echo -e "\n=== RAID 状态 ==="
for md in /dev/md*; do
[ -b "$md" ] && echo "$md:" && mdadm --detail "$md" | grep -E "State|Active|Failed"
done
echo -e "\n=== SMART 健康 ==="
for disk in $(lsblk -d -n -o NAME | grep -E 'sd|nvme'); do
echo "/dev/$disk:"
smartctl -H /dev/$disk 2>/dev/null | grep -E "result|PASSED|FAILED"
done
echo -e "\n=== IO 等待 ==="
iostat -x 1 3 | tail -20
echo -e "\n=== 大文件 TOP10 ==="
find /data -type f -size +1G -exec ls -lh {} \; 2>/dev/null | sort -k5 -rh | head -10
加入 cron:
# 每天早上8点检查
echo '0 8 * * * /opt/scripts/disk-health-check.sh | mail -s "Disk Health Report" ops@example.com' | crontab -
7.3 自动告警
# 磁盘使用率告警(配合 Zabbix/Prometheus)
# 或简单脚本:
#!/bin/bash
THRESHOLD=85
ALERT_EMAIL="ops@example.com"
df -h | awk 'NR>1 && $5+0 > '$THRESHOLD' {print $5, $6}' | while read usage mountpoint; do
echo "警告:$mountpoint 使用率 $usage(超过 ${THRESHOLD}%)" | \
mail -s "磁盘空间告警" "$ALERT_EMAIL"
done
总结
磁盘管理是运维的基本功。记住几个核心原则:
- 永远用 LVM:不管是数据盘还是系统盘,LVM 的在线扩容和快照功能在关键时刻能救命
- 重要数据用 RAID:单盘故障不丢数据是底线,RAID 10 或 RAID 6
- 定期检查 SMART:坏道是渐进的,不要等到磁盘完全失效才发现
- 备份比恢复靠谱:数据恢复是概率事件,定期备份是确定性保障
- 误删后立即停止写入:越早停止写入,恢复概率越高
最后回到开头那个故事。后来那台日志服务器花了一个周末迁移到 LVM,从此扩容就是 pvcreate → vgextend → lvextend → xfs_growfs 四条命令的事。运维的价值不在于处理了多少故障,而在于让多少故障不再发生。

