本文思维链:理解分区职能 → 拆开传统分区的两个角色 → 看清 LVM 的三层抽象 → 落到扩容流程与常见坑
基本概念
Linux 的存储从上到下是一条链:物理磁盘 → 分区表 → 分区 / PV → (VG)→ 文件系统 → 挂载点。 日常最容易混淆的是”分区”和”文件系统”——把它们分开看,后面所有扩容操作才讲得通。
分区概念
分区不是一块独立的设备,它只是分区表里的一条记录:「从第 X 个扇区开始,共 Y 个扇区」。内核读到这条记录后,生成一个块设备 /dev/sdb1,上层就能像读写整盘一样读写这段地址范围。
分区表写在磁盘最前面,常见两种:
| 类型 | 位置 | 分区数上限 | 容量上限 | 特点 |
|---|---|---|---|---|
| MBR | 第 0 扇区(512 B) | 4 个主分区(或 3 主 + 1 扩展) | 2 TiB | 老、兼容性好,改完需重读 |
| GPT | 头部 + 尾部各一份,带 CRC 校验 | 默认 128 个 | 远超 2 TiB | 自带备份表,主表损坏可恢复 |
分区与文件系统的分工:mkfs.xfs /dev/sdb1 是把文件系统的超级块、空闲块位图、inode 表写进分区覆盖的地址范围。也就是说:
- 分区决定「文件系统能用多大」——地址范围写在分区表里,建好即固定;
- 文件系统决定「这些空间怎么组织数据」——元数据写在分区内部。
两边互不知情,所以扩容时要拆成两次扩:先改分区表/LV 映射让块设备变大,再通知文件系统吃满新空间。这是全文最关键的一句话。
flowchart TD
A[物理磁盘 /dev/sdb] --> B[分区表 MBR / GPT]
B --> C[分区 /dev/sdb1]
C --> D[文件系统 XFS / EXT4]
D --> E[挂载点 /data]查看命令
lsblk -f # 树形展示:磁盘→分区→文件系统→挂载点lsblk -o NAME,SIZE,TYPE,MOUNTPOINTblkid # 查看 UUID / 文件系统类型fdisk -l /dev/sdb # 查看分区表LVM概念
LVM(Logical Volume Manager) 是架在 device-mapper(dm)之上的卷管理层:它把「物理空间」和「逻辑空间」解耦,用一张可动态修改的映射表取代固定地址。
层次结构:物理磁盘/分区 → PV → VG → LV → 文件系统
| 层 | 全称 | 作用 | 关键命令 | 类比 |
|---|---|---|---|---|
| PV | Physical Volume | 被 LVM 纳管的块设备;空间切成固定大小的 PE(默认 4 MiB),头部写入 LVM 元数据 | pvcreate / pvs / pvdisplay | 砖头 |
| VG | Volume Group | 把一个或多个 PV 汇总成存储池,池内空间以 PE 为单位统一编号 | vgcreate / vgextend / vgs | 砖堆 |
| LV | Logical Volume | 从 VG 里切出一段空间,由若干 LE 组成,与 PE 一一映射 | lvcreate / lvextend / lvs | 砌好的墙 |
| PE / LE | Physical / Logical Extent | 分配的最小粒度:PE 在 PV 上,LE 在 LV 上,默认 4 MiB | pvcreate -s / lvcreate -l | 砖的单位 |
映射是怎么实现的:LV 并不是”把某块盘的一部分划出来”,而是 dm 里的一张 linear 映射表——「LV 的第 0 ~ 10239 个 LE → /dev/sdb 上第 0 ~ 10239 个 PE」。由此得到三个直接结论:
- 一个 LV 可以横跨多块 PV;
- 扩容只是往映射表尾部追加条目,已有条目的地址不动,所以不需要搬数据;
- 设备节点其实是
/dev/mapper/vg_data-lv_data,/dev/vg_data/lv_data只是 udev 生成的软链接,两者是同一个设备。
元数据存在哪:LVM 自己的元数据(PV/VG/LV 结构、PE 分配表)有两份——磁盘上每个 PV 头部的 label + metadata area,以及 /etc/lvm/backup/、/etc/lvm/archive/ 里的文本备份,必要时可用 vgcfgrestore 回滚。这也是 LVM 比裸分区”更好救”的原因之一。
pvs # PV 清单vgs # VG 清单(重点看 VFree:还剩多少可分配)lvs -o +devices # LV 清单,+devices 可看到它落在哪些 PV 上pvdisplay -m # 展开 PE → LE 的映射关系两者关系
传统分区 /dev/sdb1 同时干了两件事:
- 划分物理边界:把一块物理磁盘切成一段一段的存储区域
- 作为文件系统的载体:
mkfs /dev/sdb1,文件系统直接建在分区上
而 LVM 把这两个角色拆开了:
| 角色 | 传统 | LVM |
|---|---|---|
| 划分物理边界 | 分区(fdisk) | PV(pvcreate,切成 PE) |
| 池化/合并空间 | 不存在 | VG(vgextend 加入新 PV) |
| 作为文件系统载体 | 分区(sdb1) | LV(lvcreate) |
| 容量调整粒度 | 整块磁盘的空闲区域 | VG 中任意可用 PE |
注:VG ≈ 传统里没有的中间池化层(把多个物理范围合并成一个可分配空间)。
两者区别
分区大小
LV 的物理边界不是固定的,而传统分区大小固定:
- 传统
/dev/sdb1建好后,物理位置和大小就钉死了。 - LVM 的
/dev/vg_data/lv_data逻辑上 50G,但底层可能横跨/dev/sda3和/dev/sdb两块盘,扩容后可以变成 150G。
也就是说,LV 只保留”文件系统载体”这个角色,丢掉了”固定物理边界”这个角色——物理边界被 PV/VG 接管了。这正是 LVM 灵活性的来源。
在线扩容
传统分区必须挂载新目录或调整分区,LVM 可通过 LV 实现逻辑上的扩展:
- PV 层面:
pvcreate /dev/sdb只是初始化磁盘元数据,不涉及已挂载的磁盘。 - VG 层面:
vgextend vg_data /dev/sdb只是在卷组里新增一个物理存储池成员,不影响现有 LV 的数据分布。 - LV 层面:
lvextend -l +100%FREE /dev/vg_data/lv_data是逻辑上的扩展,内核支持动态调整映射表。 - 文件系统层面:
xfs_growfs /data或resize2fs专门设计为在线扩展,底层文件系统会动态分配新空间到已有目录中。
整个过程业务进程无需重启,正在读写的文件不受影响(因为扩展的是空闲区域,不移动已有数据)。
扩容能力的边界
| 场景 | 传统分区 | LVM |
|---|---|---|
| 同一块盘有紧邻空闲空间时扩容 | 可以(改分区表 + 重读 + resize2fs) | 可以 |
| 跨磁盘扩大同一个目录 | 不可能,分区不能跨盘 | 可以(vgextend + lvextend) |
| 在线扩容(不 umount、不停业务) | 受盘内空间与内核重读分区表限制 | 可以 |
| 缩容 | ext4 需 umount + e2fsck;XFS 完全不支持 | 同样受限,lvreduce 必须离线且风险极高 |
| 秒级快照 / 克隆 | 不支持 | lvcreate -s 支持 |
结论:LVM 换来灵活性,代价是多一层抽象(多一份要维护的元数据、多一处可能敲错的地方)。缩容不是它的强项,扩容才是它的主场。
扩容流程
扩容要先分清诉求,两种诉求的代价完全不同:
- A. 新增挂载点:磁盘满了,加一块盘,把新盘挂到新目录(如
/data2)——不需要停机。 - B. 扩大已有目录:磁盘满了,但业务路径不能改,必须让
/data本身变大——这才是考验底层方案的地方。
新增磁盘(传统 & LVM)
场景1:为新磁盘新建分区(或 LV)并挂载到新目录,即上面的诉求 A。
传统流程:OS 层执行 rescan → 确认 lsblk → fdisk 分区 → mkfs 格式化 → mount 挂载 → df 验证→ fstab 永久挂载 → mount -a 验证
# OS层发现新设备echo "- - -" > /sys/class/scsi_host/host0/scanlsblk # 确认新容量已识别fdisk /dev/sdb # 分区mkfs.xfs /dev/sdb1 # 格式化mount /dev/sdb1 /mnt/data # 挂载df -h # 验证# 永久挂载:获取UUID → 写入 /etc/fstabblkid /dev/sdb1 # 获取UUIDblkid /dev/sdb1 | grep -o ' UUID="[^"]*' | cut -d'"' -f2 # 获取纯UUID字段vim /etc/fstab # vim 手动加一行或用 echo 追加echo 'UUID=<UUID> /mnt/data xfs defaults 0 0' >> /etc/fstabmount -a -v # 用 mount -a 测试 fstab 是否正确LVM 流程:OS 层执行 rescan → 确认 lsblk → PV/VG/LV 操作 → mkfs → mount → df 验证 → fstab 永久挂载 → mount -a 验证
echo "- - -" > /sys/class/scsi_host/host0/scanlsblkpvcreate /dev/sdb # 相当于分区中的初始化物理边界vgcreate vg_data /dev/sdb # 建存储池(传统没有这层)lvcreate -L 50G -n lv_data vg_data # 相当于"切出分区"mkfs.xfs /dev/vg_data/lv_data # 格式化mount /dev/vg_data/lv_data /mnt/data # 挂载df -hblkid /dev/sdb1vim /etc/fstabmount -a -v场景2:为新磁盘新建分区(或 LV)并挂载到已有目录,即上面的诉求 B。
流程:停业务 → 搬运现有业务数据 → 识别新容量 → umount → 其余流程
为什么该流程要停止业务/不支持在线操作?
“新盘 + 新挂载点”这套流程本身不需要停机。mkfs 和 mount 只作用于新的块设备与新的空目录,完全不碰正在服务的文件系统。真正要停机的,是「想把新盘并进已有目录」(诉求 B)——传统分区方案做不到,只能停机搬数据。
拆开看,原因有三层:
- 分区是固定地址,且不能跨盘:
/dev/sda1的边界写死在分区表里,/dev/sdb1是另一段独立地址。“把 sdb 的空间追加给 sda1 上的文件系统”在传统分区里根本不存在对应操作。 - 同一块盘内扩容也有限制:只有当盘上存在紧邻分区尾部的空闲空间时才能扩(
growpart/parted resizepart→resize2fs);改完分区表还要让内核重读(partprobe/partx -u),而分区正被挂载占用时,旧内核会直接拒绝重读,只能 umount。 - 做不到就只能搬数据:换新盘意味着
rsync全量搬运 → umount → 重新分区、mkfs→ 改/etc/fstab→ 重新 mount。停机时间和数据量成正比,且搬运期间的新写入无法保证一致,业务必须停。
另外,mkfs 这个动作永远不可能在线做:它是”在块设备上从零构建文件系统元数据”的破坏性操作,等于把原有超级块、inode 表、位图全部重建。这和”扩容文件系统(在原有结构上变大、保留数据)“是两件完全不同的事。
LVM 在线扩容
生产环境中的最佳实践。不动现有磁盘/分区,而是把新磁盘加入卷组,再扩展 LV,让文件系统直接使用新空间。
流程:OS 层执行 rescan → 确认 lsblk → PV/VG/LV 操作 → xfs_growfs/resize2fs → df 验证
echo "- - -" > /sys/class/scsi_host/host0/scanlsblkpvcreate /dev/sdb # 新盘纳入 LVMvgextend vg_data /dev/sdb # 加入卷组,VG 变大lvextend -l +100%FREE /dev/vg_data/lv_data # LV 变大(也可以 -L +50G 指定增量)# 扩容文件系统,通知文件系统用满新空间xfs_growfs /data # XFS:接挂载点resize2fs /dev/vg_data/lv_data # EXT4:接设备# 检查df -h为什么扩容流程中没有格式化/挂载操作?
- 格式化(mkfs)是对整个块设备”从无到有”创建文件系统的操作,只能做一次;而扩容是扩展文件系统(在原有基础上变大、保留数据),走的是另一条代码路径。
- 挂载(mount)只在全新磁盘 / 全新 LV 第一次投入使用时做一次,作用是把文件系统接入目录树;扩容是对已挂载的对象操作,属于两个不同阶段。
- 扩容其实是”拆成两次扩”:
lvextend让块设备变大(改 dm 映射表),xfs_growfs/resize2fs让文件系统用满新空间。少做后一步,lsblk已经变大而df -h毫无变化。
常见坑
- 只扩了 LV,忘了扩文件系统:补一条
xfs_growfs /data(XFS)或resize2fs /dev/vg_data/lv_data(EXT4)即可,对已挂载的文件系统是安全的(XFS 只支持扩、不支持缩)。 - 参数搞反:
xfs_growfs接挂载点,resize2fs接设备;lvextend接 LV 路径,vgextend接 VG 名 + 新 PV。 - 对已有数据的盘执行
pvcreate:会覆盖磁盘头部(含分区表),旧数据事实不可见。新盘整盘使用没问题;复用旧盘前务必确认盘上没有要保留的数据。 - 整盘做 PV 还是分区做 PV:
pvcreate /dev/sdb最省事;若这块盘还要分给别的用途、或需要保留分区表结构,就先建一个类型为8e(Linux LVM)的分区,再pvcreate /dev/sdb1。 - 虚拟机扩完盘系统没识别:SCSI 用
echo "- - -" > /sys/class/scsi_host/host0/scan;NVMe 用echo 1 > /sys/class/nvme/nvme0/rescan_controller;只改了分区表则用partprobe /dev/sdb或partx -u /dev/sdb。 - 缩容是另一回事:ext4 缩容要先
umount+e2fsck -f+resize2fs,顺序错一步就丢数据;XFS 根本不支持缩容。生产上想”腾出空间”,通常是新建 LV、迁移数据、再删旧 LV。 - 变更前留后路:动 LV 之前可以先
lvcreate -s打个快照,或确认有可用备份,扩容失败时可回滚。
小结
| 问题 | 一句话答案 |
|---|---|
| 分区和文件系统有什么区别? | 分区是「地址范围」(写在分区表里),文件系统是「数据的组织方式」(写在分区内部),扩容要分别扩两次。 |
| LVM 比传统分区多了什么? | 多了 VG 池化层和一张可动态修改的映射表,所以空间能跨盘拼、容量能在线追加。 |
为什么还要执行 xfs_growfs / resize2fs? | lvextend 只改块设备大小,文件系统必须被显式通知才能用满新空间。 |
| 为什么扩容不需要 mkfs / mount? | 格式化是”从无到有创建”,扩容是”在原有基础上变大”;挂载只在对象第一次投入使用时做一次。 |
| 传统方案什么时候必须停机? | 当诉求是”扩大已有目录”,且可用空间不在同一块盘的紧邻位置时——只能停机搬数据,这正是 LVM 存在的理由。 |
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 5 天
部分内容可能已过时