Linux 基础体系 · 第 57/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。
LVM 深入:PV、VG、LV、Thin Pool、Snapshot、扩缩容和恢复
LVM(Logical Volume Manager)位于块设备和文件系统之间。它不负责理解文件名、目录或文件内容,而是把一个或多个块设备组织成可动态分配的存储空间,再把其中的一部分呈现为块设备,供文件系统、数据库或虚拟机使用。
典型数据路径如下:
flowchart LR
D1["磁盘 / 分区 / RAID设备"] --> PV["PV\nPhysical Volume"]
D2["磁盘 / 分区 / RAID设备"] --> PV2["PV\nPhysical Volume"]
PV --> VG["VG\nVolume Group"]
PV2 --> VG
VG --> LV["普通 LV\n线性或条带"]
VG --> TP["Thin Pool\n数据区 + 元数据区"]
TP --> TLV["Thin LV"]
LV --> FS1["文件系统"]
TLV --> FS2["文件系统"]
LV -. "经典 Snapshot" .-> CS["COW Snapshot LV"]
TLV -. "Thin Snapshot" .-> TS["Thin Snapshot"]
核心关系可以形式化为:
块设备 → PV → VG → LV → 文件系统 → 挂载点
└→ 数据库、虚拟机磁盘、容器存储等
这里的箭头不是简单的文件复制关系,而是块地址映射关系。LVM 通过设备映射器(device mapper)把上层看到的逻辑块,转换为底层 PV 上的物理块。
1. 使用 LVM 前必须区分的几层对象
1.1 块设备、分区和文件系统不是一回事
例如:
/dev/sdb
└── /dev/sdb1
└── PV
└── VG
└── LV
└── ext4 或 XFS
└── /data
每一层都可能独立存在:
/dev/sdb是整块磁盘对应的块设备。/dev/sdb1是磁盘上的一个分区。- PV 是 LVM 在块设备上写入的物理卷元数据。
- VG 是多个 PV 的容量池。
- LV 是从 VG 中分配出来的逻辑块设备。
- 文件系统是写在 LV 上的数据结构,例如 ext4、XFS。
- 挂载点是 Linux 将文件系统接入目录树的位置。
因此,下面几种操作影响的层次不同:
lvextend # 扩大 LV 这个块设备
resize2fs # 扩大 ext2/ext3/ext4 文件系统
xfs_growfs # 扩大 XFS 文件系统
mount # 将文件系统接入目录树
只执行 lvextend,不会自动让所有文件系统都变大。相反,lvreduce 直接缩小块设备而不先缩小文件系统,通常会破坏文件系统。
1.2 LVM 的容量单位是 PE
VG 将空间划分为固定大小的 Physical Extent,简称 PE。LV 的空间由若干 LE(Logical Extent)组成。通常一个 LE 与一个 PE 大小相同;对条带等复杂布局而言,一个逻辑扩展区可能映射到多个物理设备上的区域。
设:
E为 extent 大小;N为可用 extent 数;S为 VG 可分配容量。
则:
S = N × E
例如,extent 大小为 4 MiB,VG 中有 25600 个可用 PE:
25600 × 4 MiB = 102400 MiB ≈ 100 GiB
LVM 命令中的 -L 100G 是人类可读的容量请求,实际分配会按照 extent 对齐。容量显示中的十进制 G、二进制 GiB,以及工具的四舍五入方式,可能造成少量差异。严谨地确认最终结果,应查看 pvs、vgs 和 lvs 的 extent 或字节信息,而不是只看命令参数。
2. PV:把块设备纳入 LVM
2.1 PV 的定义和作用
PV(Physical Volume,物理卷)是一个已经初始化为 LVM 物理卷的块设备。它可以是:
- 整块磁盘,例如
/dev/sdb; - 分区,例如
/dev/sdb1; - RAID 设备,例如
/dev/md0; - 其他能提供块设备接口的设备。
初始化 PV 会写入 LVM 元数据,并保留一部分空间用于描述 VG、PV UUID、extent 布局等信息。它不会创建文件系统,也不会自动挂载设备。
查看当前发现的块设备:
lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINTS
查看已有 PV:
pvs
pvdisplay
初始化前应确认设备没有重要数据:
sudo wipefs -n /dev/sdb
wipefs -n 只读取并显示可能存在的签名,不执行擦除。确认 /dev/sdb 确实是目标设备后,才执行:
sudo pvcreate /dev/sdb
预期结果类似:
Physical volume "/dev/sdb" successfully created.
pvcreate 是破坏性操作的边界之一。它通常会覆盖或重建设备上的 LVM 识别信息;如果设备上已有文件系统、RAID、旧 VG 或其他签名,不应直接执行。
2.2 PV 可以位于分区或整盘上
两种常见布局如下:
整盘 PV:
/dev/sdb → PV → VG
分区 PV:
/dev/sdb1 → PV → VG
现代系统通常使用 GPT 分区表。使用分区作为 PV 时,可用 parted 或 fdisk 创建分区,并将类型设置为适合 LVM 的类型。对于 GPT,常见类型 GUID 是 Linux LVM;但 LVM 实际能否使用设备,主要仍取决于设备上的 LVM 元数据和系统发现规则。
使用整盘作为 PV 配置简单,但以后若需要在同一磁盘上增加其他分区、引导区域或调整磁盘布局,灵活性较低。使用分区可以明确边界,但分区表本身也成为需要备份和恢复的元数据。
2.3 PV 的 UUID 和可用空间
查看更详细的 PV 信息:
sudo pvs -o pv_name,vg_name,pv_size,pv_free,pv_uuid
sudo pvdisplay
典型输出可能是:
PV VG PSize PFree PV UUID
/dev/sdb vgdata 100.00g 20.00g xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
PFree 是尚未分配给 LV 的 extent 空间。一个 VG 可能有很多空闲空间,但这些空间不一定在每个 PV 上均匀分布。若某块磁盘将要损坏,不能只看 VG 总剩余空间,还要确认数据是否已经迁移到其他 PV。
3. VG:将多个 PV 组合成容量池
3.1 VG 的定义
VG(Volume Group,卷组)是多个 PV 组成的分配池。LV 从 VG 中分配空间,因此 LV 可以:
- 位于一个 PV 上;
- 跨越多个 PV;
- 在增加 PV 后继续扩展;
- 通过
pvmove在 PV 之间迁移 extent。
创建 VG:
sudo vgcreate vgdata /dev/sdb
将多个 PV 一次加入:
sudo vgcreate vgdata /dev/sdb /dev/sdc
查看 VG:
sudo vgs
sudo vgdisplay
常见关键字段包括:
VSize:VG 总容量;VFree:尚未分配给 LV 的空间;#PV:PV 数量;#LV:LV 数量;vg_attr:VG 状态和属性。
增加新的 PV:
sudo pvcreate /dev/sdd
sudo vgextend vgdata /dev/sdd
此时,/dev/sdd 的空间才成为 vgdata 可以分配的空间。仅执行 pvcreate,并不会自动扩展任何既有 VG。
3.2 VG 不等于 RAID
把 /dev/sdb 和 /dev/sdc 放入同一个 VG,不会自动产生镜像或校验保护。VG 主要解决容量聚合和分配问题:
VG = 容量池
RAID = 数据冗余或条带布局机制
如果 LV 跨越两个没有冗余的磁盘,任意一个磁盘损坏,都可能使 LV 的部分数据不可读。LVM 可以创建某些 RAID 类型的 LV,但这属于 LVM RAID,而不是普通 VG 聚合,必须单独理解其布局、重建和监控机制。
3.3 extent 分配和碎片
假设 vgdata 有两个 PV:
/dev/sdb: 100 GiB
/dev/sdc: 100 GiB
创建一个 150 GiB 的普通线性 LV 时,LVM 可能先从 /dev/sdb 分配 100 GiB,再从 /dev/sdc 分配 50 GiB。这个 LV 在逻辑上是连续的,但物理上跨越两个设备。
这意味着:
- LV 可以跨 PV;
- 单个 LV 的可用容量不代表单个 PV 的容量;
- 迁移某个 PV 前,必须确保其 extent 能迁移到其他 PV;
- 空间碎片可能导致“VG 还有容量,但无法满足某种布局请求”。
查看 LV 到 PV 的实际映射:
sudo lvs -a -o lv_name,vg_name,lv_size,segtype,devices
devices 字段可帮助判断数据段位于哪些底层设备。生产环境中不应仅凭设备名推断数据布局,因为设备名可能因启动顺序改变。
4. LV:从 VG 中切出的逻辑块设备
4.1 普通 LV
LV(Logical Volume,逻辑卷)是 LVM 对外提供的逻辑块设备。创建一个 50 GiB 的 LV:
sudo lvcreate -L 50G -n lvdata vgdata
设备通常可通过以下路径访问:
/dev/vgdata/lvdata
/dev/mapper/vgdata-lvdata
查看:
sudo lvs -a -o lv_name,vg_name,lv_size,lv_attr,segtype,devices
lsblk
普通 LV 常见的 segment 类型是 linear,表示逻辑地址区间按顺序映射到一个或多个 PV 上。LV 本身没有目录结构,不能直接用 ls 查看内容;必须在其上创建文件系统,或者将其作为数据库、虚拟机磁盘等原始块设备使用。
创建文件系统并挂载:
sudo mkfs.ext4 /dev/vgdata/lvdata
sudo mkdir -p /data
sudo mount /dev/vgdata/lvdata /data
mkfs.ext4 会初始化文件系统结构,若设备已有数据,通常会使原数据不可按原文件系统方式访问。文件系统类型必须与后续扩缩容工具对应。
4.2 LV 的三种“大小”
使用 LV 时容易混淆三个大小:
PV/VG 中分配给 LV 的块设备大小
文件系统识别到的大小
文件系统中可用的空闲空间
例如:
LV: 100 GiB
文件系统: 100 GiB
已用数据: 70 GiB
文件系统可用: 30 GiB
将 LV 扩大到 150 GiB 后,如果不执行文件系统扩展:
LV: 150 GiB
文件系统: 100 GiB
文件系统仍只能使用前 100 GiB
因此在线扩容通常需要两个步骤:
扩大 LV → 扩大文件系统
lvextend -r 可以在支持的情况下尝试同时执行文件系统调整,但它依赖发行版安装的 fsadm、文件系统类型和当前状态,不能把它理解为对所有文件系统都无条件可靠。
5. Thin Pool:按需分配的稀疏块存储
5.1 Thin Provisioning 的含义
普通 LV 在创建时就从 VG 分配实际物理 extent:
lvcreate -L 500G ...
这通常意味着 VG 立即减少约 500 GiB 可用空间。
Thin LV 则把两个大小分开:
- 虚拟容量:上层块设备看到的容量;
- 实际占用:Thin Pool 当前为已写入数据分配的物理空间。
例如:
Thin Pool 实际容量: 1 TiB
Thin LV 虚拟容量: 5 TiB
当前实际写入: 200 GiB
这允许多个 Thin LV 的虚拟容量总和大于 Thin Pool 的实际容量:
虚拟容量总和 > Thin Pool 实际容量
这称为超额分配(overprovisioning)。它不是凭空产生容量,而是把“未来写入不会超过物理空间”的假设交给管理员。假设失效时,写操作可能失败。
5.2 Thin Pool 的内部结构
Thin Pool 至少包含:
- 数据区域:保存实际数据块;
- 元数据区域:保存虚拟块到实际块的映射、快照关系和引用计数等。
数据流可以简化为:
Thin LV 逻辑块号
│
▼
Thin Pool 元数据映射
│
▼
数据区域中的实际块
│
▼
底层 PV
第一次写入某个逻辑块时,Thin Pool 分配实际数据块并建立映射。后续写入使用该映射。Thin Snapshot 还会利用引用关系判断某个数据块是否仍被其他卷共享。
因此,Thin Pool 监控不能只看“普通 LV 是否还有空间”,还必须看:
数据空间使用率
元数据空间使用率
是否触发 read-only、暂停或错误状态
查看 Thin Pool:
sudo lvs -a -o lv_name,lv_attr,lv_size,data_percent,metadata_percent,segtype,origin
典型输出中,data_percent 和 metadata_percent 分别表示 Thin Pool 数据区和元数据区的使用比例。具体字段和属性显示会随 LVM2 版本变化,应以本机 lvs --help 和 lvmthin(7) 为准。
5.3 创建 Thin Pool 和 Thin LV
创建一个 500 GiB 的 Thin Pool:
sudo lvcreate --type thin-pool -L 500G -n thinpool vgdata
从 Thin Pool 创建一个虚拟容量为 1 TiB 的 Thin LV:
sudo lvcreate --type thin -V 1T -n vm-disk vgdata/thinpool
查看结果:
sudo lvs -a -o lv_name,lv_attr,lv_size,segtype,pool_lv,data_percent,metadata_percent
这里 vm-disk 的虚拟容量可以大于 thinpool 的物理容量,但只有实际写入的数据才消耗 Pool 的数据区域。
如果要在 Thin LV 上创建文件系统:
sudo mkfs.ext4 /dev/vgdata/vm-disk
对于虚拟机,通常把 Thin LV 作为虚拟磁盘直接交给虚拟机,并在虚拟机内部创建文件系统。此时宿主机不应再对该 LV 执行 mkfs 或挂载,否则会与虚拟机内部的文件系统产生冲突。
5.4 Thin Pool 满时会发生什么
Thin Provisioning 的风险是“创建成功”不代表“未来写入一定成功”。
如果 Thin Pool 数据空间耗尽:
- 新写入可能失败;
- 上层文件系统可能报告 I/O 错误;
- 数据库或虚拟机可能进入异常状态;
- 某些配置会让池暂时暂停 I/O,等待管理员处理。
如果元数据空间耗尽,风险通常更严重,因为系统无法可靠记录新的映射关系。数据区和元数据区都必须监控。
扩展 Thin Pool:
sudo lvextend -L +100G vgdata/thinpool
若需要单独扩展元数据区,应使用与本机 LVM2 版本匹配的 Thin Pool 管理方式。Thin Pool 的元数据操作比普通 LV 更敏感,不应在不了解当前池状态、备份和修复流程时直接使用低层参数。
生产环境常配置 LVM 监控和自动扩展,但自动扩展只解决“还有可用物理空间且策略允许扩展”的情况,不能替代容量规划。若 VG 本身没有空闲空间,自动扩展仍然无法完成。
6. Snapshot:写时复制,而不是备份
6.1 Snapshot 的核心机制
Snapshot(快照)保存的是某个时间点的块设备视图。它通常使用写时复制(Copy-on-Write,COW):
创建快照时:
origin 和 snapshot 共享原有数据块
origin 后续覆盖某个块时:
先把旧块复制到 snapshot 的 COW 区
再允许 origin 写入新数据
snapshot 读取该块时:
读取 COW 区中的旧块
快照保护的是“原有块的旧版本”,而不是持续复制所有新增数据。可以把它表示为:
snapshot_view(block) =
COW_old_block(block), 如果 origin 曾覆盖该块
origin_current_block(block), 否则
快照创建瞬间通常很快,因为它主要创建映射和元数据,而不是立即复制整个 LV。
6.2 经典 Snapshot
对普通 LV 创建经典快照:
sudo lvcreate -s -L 20G -n lvdata-snap vgdata/lvdata
含义是:
vgdata/lvdata:origin;vgdata/lvdata-snap:snapshot;-L 20G:快照的 COW 空间;- 快照并不是一个“20 GiB 的 origin 副本”。
如果 origin 在快照生命周期内覆盖的数据量超过 20 GiB,COW 空间可能耗尽。快照可能进入无效或不可用状态,依赖该快照的备份读取也会失败。
查看快照状态:
sudo lvs -a -o lv_name,lv_attr,lv_size,origin,data_percent,segtype
对经典快照,data_percent 通常表示 COW 区的使用程度。它不是 origin 文件系统的使用率。
挂载快照前,必须考虑文件系统一致性以及文件系统 UUID 冲突。例如 ext4 快照与 origin 具有相同 UUID,直接同时挂载可能造成识别和写入风险。常见做法是只读挂载,并在需要时使用文件系统支持的临时 UUID 选项;不同文件系统工具的行为不同。
示例:
sudo mkdir -p /mnt/snap
sudo mount -o ro,noload /dev/vgdata/lvdata-snap /mnt/snap
noload 对 ext4 等日志文件系统可避免回放日志,但不是所有文件系统都支持该选项。挂载前必须确认文件系统类型和工具文档。
6.3 Thin Snapshot
如果 origin 是 Thin LV,可以创建 Thin Snapshot:
sudo lvcreate -s -n vm-disk-snap vgdata/vm-disk
Thin Snapshot 与经典 Snapshot 的主要区别是:
- Thin Snapshot 由 Thin Pool 的映射和引用计数管理;
- 可以共享未修改的数据块;
- 不需要像经典快照那样单独指定一个固定大小的 COW LV;
- snapshot 和 origin 的新写入仍然消耗 Thin Pool 的数据区;
- snapshot 数量、写入量和元数据复杂度会增加 Thin Pool 压力。
这并不意味着 Thin Snapshot 不会耗尽空间。只要 Thin Pool 数据区或元数据区满,快照及其 origin 的正常写入都可能受影响。
6.4 快照的一致性边界
块级快照只保证某种块映射意义上的时间点视图,不自动保证应用一致性。
例如数据库正在执行:
- 更新数据页;
- 写日志;
- 修改索引;
- 尚未提交事务。
此时创建块设备快照,得到的内容可能需要数据库恢复流程才能使用。更可靠的流程通常是:
应用进入备份模式或短暂冻结
刷新文件系统和应用状态
创建快照
解除冻结
从快照读取数据
对于数据库,应优先使用数据库自身的备份机制或厂商支持的快照协作机制。fsfreeze 可以冻结文件系统的写入,但它不会自动让数据库知道自己处于备份状态。
6.5 删除、合并和回滚
删除一个不再需要的快照:
sudo lvremove vgdata/lvdata-snap
快照删除本身也可能产生 I/O 和元数据工作,不能在高负载生产系统中默认认为“瞬间完成”。
经典快照可以用于回滚 origin,但回滚会丢弃 origin 在快照创建之后发生的变化。典型形式是:
sudo lvconvert --merge vgdata/lvdata-snap
如果 origin 正在使用,合并可能被延迟到下次激活或重启;具体行为取决于设备状态和 LVM 版本。执行前必须确认:
- origin 是否已备份;
- origin 当前数据是否确实应该被回滚;
- 文件系统是否需要离线检查;
- 快照和 origin 是否正在被挂载或使用。
“删除快照”和“将快照合并回 origin”是完全不同的操作,前者释放快照,后者改变 origin 的数据视图。
7. 扩容:LV、文件系统和底层设备的顺序
扩容的基本因果链是:
底层块设备有新空间
↓
PV 扩大
↓
VG 增加可用 extent
↓
LV 扩大
↓
文件系统扩大
任意一层没有完成,后续层都不能凭空看到空间。
7.1 扩大磁盘后的 PV 扩容
假设虚拟机中的 /dev/sdb 从 100 GiB 扩大到 200 GiB,并且 PV 直接位于整块 /dev/sdb 上:
sudo pvresize /dev/sdb
sudo pvs
sudo vgs
pvresize 让 LVM 重新识别设备尾部新增的空间。它不会扩大 LV,也不会扩大文件系统。
如果 PV 位于分区 /dev/sdb1 上,通常需要先扩大分区,再执行:
sudo pvresize /dev/sdb1
扩大分区前必须确认分区起始位置、分区类型和设备布局。对根盘或正在使用的分区,分区调整存在更高风险;应先保存分区表并准备恢复介质。
7.2 扩大普通 LV 和 ext4
假设 /dev/vgdata/lvdata 上是 ext4,增加 20 GiB:
sudo lvextend -L +20G /dev/vgdata/lvdata
sudo resize2fs /dev/vgdata/lvdata
第一条命令扩大块设备,第二条命令扩大 ext4 文件系统。ext4 通常支持在线扩大,但仍应检查挂载状态、内核和工具版本。
也可以使用:
sudo lvextend -r -L +20G /dev/vgdata/lvdata
-r 要求系统能正确识别并调用文件系统调整工具。为了让操作边界更清楚,关键生产变更常分开执行并分别验证:
lsblk
sudo lvs
df -hT /data
lvs 验证 LV 大小,df 验证文件系统大小;只看其中一个不足以证明扩容完成。
7.3 扩大 XFS
XFS 支持在线扩大,但不支持缩小。扩大流程为:
sudo lvextend -L +20G /dev/vgdata/lvdata
sudo xfs_growfs /data
XFS 的 xfs_growfs 通常针对挂载点执行,而不是直接针对块设备。验证:
df -hT /data
sudo xfs_info /data
对于 XFS,若误执行:
lvreduce ...
再试图用某个“缩小 XFS”的命令补救,是不可行的。通常只能备份数据、重建较小文件系统并恢复。
7.4 扩大根文件系统
根文件系统扩容常见步骤如下:
sudo lvextend -r -L +10G /dev/mapper/vgroot-root
df -hT /
但根 LV 可能位于加密设备、RAID、分区 PV 或云盘之上。完整路径可能是:
云盘
→ 分区
→ RAID
→ LUKS
→ PV
→ VG
→ LV
→ 文件系统
此时必须按照底层到上层的顺序扩展。例如 LUKS、RAID、分区和 PV 的处理方式各不相同,不能把“lvextend -r 能成功”推广为整条链都自动扩展。
8. 缩容:必须反向执行,而且不是所有文件系统都支持
缩容与扩容的关键差异是:扩容通常先扩大容器再扩大内容;缩容必须先缩小内容,再缩小容器。
安全的逻辑顺序是:
停止写入
↓
卸载文件系统
↓
检查文件系统
↓
缩小文件系统
↓
缩小 LV
↓
重新挂载并验证
8.1 ext4 缩容示例
假设 ext4 文件系统当前为 100 GiB,目标缩小到 80 GiB:
sudo umount /data
sudo e2fsck -f /dev/vgdata/lvdata
sudo resize2fs /dev/vgdata/lvdata 80G
sudo lvreduce -L 80G /dev/vgdata/lvdata
sudo mount /dev/vgdata/lvdata /data
sudo e2fsck -f /dev/vgdata/lvdata
df -hT /data
为什么顺序不能反过来?
如果先执行:
sudo lvreduce -L 80G /dev/vgdata/lvdata
文件系统仍认为自己有 100 GiB。文件系统元数据或文件数据可能位于被截掉的后 20 GiB 中,后续检查和挂载都可能失败,甚至造成不可逆的数据损坏。
目标文件系统大小不能小于实际数据和元数据所需空间。resize2fs 会在缩容时检查条件,但执行前仍必须有独立备份。
8.2 XFS 不能原地缩小
XFS 不支持缩小。若目标是把 100 GiB 的 XFS 变为 80 GiB,常见路径是:
创建新的 80 GiB LV
→ 创建 XFS
→ 挂载新文件系统
→ 使用 rsync、xfsdump/xfsrestore 等迁移数据
→ 停止应用并做最终同步
→ 切换挂载点
→ 删除旧 LV
这不是 LVM 层面的缺陷,而是 XFS 文件系统设计和实现能力的边界。LVM 不能替文件系统实现数据重排。
8.3 lvreduce --resizefs 的风险
某些 LVM 版本支持:
sudo lvreduce --resizefs -L 80G /dev/vgdata/lvdata
它尝试先调整文件系统再调整 LV,但这并不消除备份、卸载和验证要求。自动化组合命令发生错误时,排查边界比手动分步执行更复杂。对重要数据,推荐明确执行并记录每一步的输出。
9. 在多个 PV 之间迁移数据:pvmove
如果要移除一个 PV,不能只执行 vgreduce。必须先把该 PV 上的 extents 移到其他 PV。
例如,把 /dev/sdb 上的 LVM 数据迁移到同一 VG 的其他 PV:
sudo pvmove /dev/sdb
如果目标空间不足,命令会失败。可以指定目标 PV:
sudo pvmove /dev/sdb /dev/sdc
迁移完成后检查:
sudo pvs -o pv_name,pv_size,pv_free
sudo lvs -a -o lv_name,segtype,devices
确认 /dev/sdb 不再承载任何 LV extent 后,才能:
sudo vgreduce vgdata /dev/sdb
sudo pvremove /dev/sdb
pvmove 会复制 extent 内容,并更新 LVM 映射。它不是文件级复制,因此不需要理解文件系统结构;但它仍会产生大量 I/O,底层设备若已经不稳定,迁移可能失败或进一步加剧故障。
迁移过程中断电时,LVM 通常可在重新激活后继续处理或恢复映射,但这不是不做备份的理由。高风险设备应优先使用硬件级或块级恢复工具制作副本,再在副本上尝试恢复。
10. 监控和自动扩展
普通 LV 的空间问题和 Thin Pool 的空间问题不同:
普通 LV:
VG 空间不足 → 无法继续扩展 LV
Thin LV:
Thin Pool 数据区不足 → 虚拟设备的实际写入可能失败
Thin Pool 元数据区不足 → 映射更新可能失败
需要同时观察:
sudo vgs
sudo lvs -a -o lv_name,lv_attr,lv_size,lv_active,origin,pool_lv,data_percent,metadata_percent
df -hT
三者含义不同:
vgs:VG 还剩多少未分配空间;lvs:LV 和 Thin Pool 的映射状态、数据区、元数据区;df:文件系统内部还剩多少空间。
例如:
VG 空间很多,但文件系统满了
这表示可以扩大 LV 和文件系统。
文件系统还有空间,但 Thin Pool data_percent 接近 100%
这表示未来写入仍可能受 Thin Pool 的物理分配能力限制,不能只根据 df 判断安全。
Thin Pool 有空间,但 VG 没有空闲 extent
这表示当前池还能使用其已分配的空间,但无法进一步扩展。若启用了超额分配,最终仍可能耗尽。
11. LVM 元数据和数据恢复的区别
恢复 LVM 时首先要区分两种内容:
LVM 元数据:
PV UUID、VG 名称、LV 名称、extent 映射、属性等
用户数据:
文件系统、文件、数据库页、虚拟机磁盘内容等
恢复 VG 元数据,不等于恢复被覆盖的文件内容。反过来,有一份文件备份,也不一定能自动重建原有 LV 布局。
11.1 查看和备份元数据
LVM 通常会保存 VG 配置备份和历史归档,位置常见为:
/etc/lvm/backup/
/etc/lvm/archive/
手动执行:
sudo vgcfgbackup vgdata
查看备份文件:
sudo less /etc/lvm/backup/vgdata
这些文件主要描述 LVM 配置和 extent 映射,不包含 LV 内部的文件数据。它们应纳入系统配置备份,但不能替代文件级、数据库级或块级备份。
同时还应保存:
sudo pvs -a -o +pv_uuid
sudo vgs
sudo lvs -a -o +devices
sudo lsblk -f
sudo blkid
这些输出有助于故障时确认设备身份和原始布局。
11.2 正常激活和扫描
查看设备和 LVM 状态:
sudo pvscan
sudo vgscan
sudo lvscan
sudo lvs -a
激活卷组:
sudo vgchange -ay vgdata
停用卷组:
sudo vgchange -an vgdata
如果某个 PV 暂时不可见,可以看到 VG 不完整。某些场景可用部分激活选项读取仍可访问的 LV:
sudo vgchange -ay --partial vgdata
--partial 不是修复,它只是允许在缺少设备时尝试激活部分映射。读取文件时可能遇到 I/O 错误;对数据库或关键文件系统,部分读取可能得到看似完整但实际已损坏的数据。
11.3 恢复丢失的 VG 元数据
如果 PV 仍然存在,数据区域也未被覆盖,但 VG 元数据被破坏,可以考虑:
sudo vgcfgrestore -f /etc/lvm/archive/vgdata_XXXX.vg vgdata
具体归档文件必须根据时间和内容选择。恢复前应:
- 停止可能访问相关设备的服务;
- 确认 PV UUID 与归档文件中的 PV 匹配;
- 保存当前现场和当前元数据;
- 确认归档版本确实描述目标 LV;
- 尽量先在设备副本或恢复环境中演练。
vgcfgrestore 修改的是 VG 元数据,不会重新生成被覆盖的文件内容。错误的归档版本可能让 LVM 按错误 extent 解释原始数据,造成进一步损害。
11.4 PV UUID 恢复属于高风险操作
如果 PV 头部损坏,但知道原 PV UUID 和原始 VG 配置,可能需要使用 pvcreate 的 UUID 恢复选项配合归档文件。此类操作通常形如:
sudo pvcreate --uuid <原PV_UUID> --restorefile /etc/lvm/archive/vgdata_XXXX.vg /dev/sdb
这不是常规初始化命令,而是灾难恢复操作。以下任一条件不满足时都不应执行:
- 目标设备已确认;
- 原 PV UUID 已确认;
- 设备上的数据区域未被覆盖;
- 归档文件对应正确的 VG 和 PV;
- 有设备镜像或可回滚副本。
错误设备、错误 UUID 或错误归档文件都可能使后续恢复更加困难。执行前应阅读本机 pvcreate(8)、vgcfgrestore(8) 和发行版 LVM 文档。
11.5 底层设备损坏时的恢复路径
如果磁盘出现读错误,不应反复在原盘上执行扫描、修复和迁移。更稳妥的路径是:
故障磁盘
↓
使用支持重试和跳过坏块的工具制作镜像
↓
在镜像或克隆设备上识别 PV/VG/LV
↓
恢复文件系统或执行文件级导出
dd 不适合处理大量坏块的磁盘复制,因为遇到错误时可能中断或效率很低。实际环境常使用具备日志、重试和断点续传能力的块设备恢复工具,例如 GNU ddrescue;具体参数应根据设备状态制定。
12. 文件系统检查和恢复
LVM 映射恢复后,还要检查上层文件系统。常见工具包括:
# ext4,通常要求卸载
sudo e2fsck -f /dev/vgdata/lvdata
# XFS,通常对已挂载文件系统执行检查
sudo xfs_repair /dev/vgdata/lvdata
不要把不同文件系统的工具混用:
ext4 → e2fsck
XFS → xfs_repair
fsck 不是一个通用的“修复所有文件系统”命令;它通常是根据文件系统类型调用具体检查程序的入口。恢复时应先识别类型:
lsblk -f
blkid /dev/vgdata/lvdata
如果文件系统来自快照,先确认快照是否完整、COW 区是否耗尽、Thin Pool 是否正常。对已经发生 I/O 错误的设备,直接运行修复工具可能修改现场;优先在副本上执行。
13. 常见误解与失败表现
13.1 “LV 扩大了,文件系统会自动扩大”
失败表现:
lvs # 显示 LV 已经变大
df -hT # 显示文件系统仍是原大小
原因是 LV 和文件系统属于不同层。解决方法是根据文件系统类型执行 resize2fs、xfs_growfs 或对应工具。
13.2 “Snapshot 就是完整备份”
快照通常与 origin 共享同一个 VG、同一组 PV,甚至同一台主机。如果底层磁盘、VG 或主机损坏,origin 和 snapshot 可能同时不可用。
此外,快照依赖 COW 空间或 Thin Pool 资源;快照存续期间写入量过大,快照可能失效。快照适合:
短期回滚点
升级前保护
备份读取的一致性来源
测试环境快速复制
它不能替代:
异机备份
离线或对象存储备份
数据库逻辑备份
恢复演练
13.3 “删除快照就能释放所有空间”
经典快照删除会释放其 COW LV,但 Thin Snapshot 删除涉及共享块引用和元数据更新。Thin Pool 的空间回收、discard 和下层存储是否支持,也会影响实际效果。删除后应查看 lvs 的数据和元数据占用,而不是仅凭命令成功判断已释放完毕。
13.4 “VG 中有 100 GiB 空闲,所以任何 LV 都能扩 100 GiB”
如果空间分布、条带要求、镜像要求或目标 PV 限制不满足,某个扩容请求仍可能失败。查看:
sudo vgs
sudo pvs
sudo lvs -a -o +devices
还应确认目标 LV 的 segment 类型。普通线性 LV、条带 LV、镜像或 RAID LV 的扩容条件不同。
13.5 “lvreduce --force 可以撤销”
--force 只表示跳过部分检查或确认,不会保护文件系统,也不会保存被截掉的块。缩容前必须先缩小文件系统;如果文件系统不支持缩小,就必须迁移到新 LV。
13.6 “Thin LV 显示 5 TiB,就一定有 5 TiB 可写”
Thin LV 的 5 TiB 是虚拟容量。真正能否继续写入取决于 Thin Pool 数据区、元数据区以及 VG 是否能继续扩展 Pool。超额分配必须伴随监控和写入失败处置方案。
14. 一个完整的安全演示流程
下面使用 /dev/sdb 作为示例设备。它会破坏该设备上的已有结构,只适合实验环境。生产环境必须将设备名替换为经过确认的目标,并先完成备份。
14.1 创建普通 LVM
sudo wipefs -n /dev/sdb
sudo pvcreate /dev/sdb
sudo vgcreate vgdata /dev/sdb
sudo lvcreate -L 10G -n lvapp vgdata
sudo mkfs.ext4 /dev/vgdata/lvapp
sudo mkdir -p /srv/app
sudo mount /dev/vgdata/lvapp /srv/app
逐层验证:
sudo pvs
sudo vgs
sudo lvs -a -o lv_name,vg_name,lv_size,segtype,devices
lsblk -f
df -hT /srv/app
如果 pvs 中看不到 PV,问题位于设备识别或 PV 元数据层;如果 lvs 看不到 LV,问题位于 VG/LV 激活层;如果 df 看不到文件系统,问题位于挂载或文件系统层。分层检查比直接反复执行命令更容易定位原因。
14.2 创建经典 Snapshot
sudo lvcreate -s -L 2G -n lvapp-snap vgdata/lvapp
sudo lvs -a -o lv_name,lv_size,origin,data_percent,lv_attr
创建快照后,对 /srv/app 持续写入大量数据,观察 data_percent:
sudo lvs -a -o lv_name,origin,lv_size,data_percent,lv_attr
这可以验证 COW 区随 origin 覆盖旧块而增长。删除演示快照:
sudo umount /srv/app # 如果挂载的是 origin 或快照,应先确认具体对象
sudo lvremove vgdata/lvapp-snap
实际生产操作中,不要因为示例中的卸载命令就无条件卸载业务文件系统;应先使用 findmnt 和 lsblk 确认挂载关系。
14.3 创建 Thin Pool 和 Thin LV
如果 VG 有足够未分配空间:
sudo lvcreate --type thin-pool -L 20G -n thinpool vgdata
sudo lvcreate --type thin -V 50G -n thinvol vgdata/thinpool
sudo lvs -a -o lv_name,lv_size,segtype,pool_lv,data_percent,metadata_percent
如果要使用 Thin LV 作为文件系统:
sudo mkfs.ext4 /dev/vgdata/thinvol
sudo mkdir -p /srv/thin
sudo mount /dev/vgdata/thinvol /srv/thin
创建 Thin Snapshot:
sudo lvcreate -s -n thinvol-snap vgdata/thinvol
sudo lvs -a -o lv_name,origin,lv_size,segtype,pool_lv,data_percent,metadata_percent
删除 Thin Snapshot:
sudo lvremove vgdata/thinvol-snap
这些命令的前提是 vgdata 中确实有足够空间,且设备没有被业务使用。Thin Pool 的具体默认元数据比例和自动扩展行为可能随 LVM2 版本、lvm.conf 配置和发行版打包方式变化,应检查:
lvm version
lvmconfig --type full activation
15. 生产变更前后的验证模型
一次 LVM 变更不应只验证命令返回码。至少要分别验证四个层次:
设备层: 设备存在、容量正确、没有意外替换
LVM层: PV/VG/LV 状态正常,extent 和映射符合预期
文件系统层: 文件系统大小和一致性正确
应用层: 应用能读写、数据库能启动、虚拟机磁盘可用
例如扩容 /data 后:
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
sudo pvs
sudo vgs
sudo lvs -a -o lv_name,lv_size,lv_attr,segtype,devices
findmnt /data
df -hT /data
touch /data/.resize-test
rm /data/.resize-test
对于数据库或虚拟机,还要进行应用级验证。文件系统能够创建测试文件,不代表数据库日志、事务提交或虚拟机磁盘链路一定正常。
16. LVM 的正确定位
LVM 的价值在于把“物理设备容量”和“上层逻辑块设备”解耦:
- 磁盘可以加入或移出 VG;
- LV 可以独立扩展;
- 快照可以创建临时一致性视图;
- Thin Pool 可以按需分配和共享数据块;
pvmove可以在设备之间迁移 extent;- 元数据归档可以帮助重建卷组布局。
但 LVM 不自动提供:
- 跨主机备份;
- 应用一致性;
- 自动数据冗余;
- 无限容量;
- 文件系统缩容能力;
- 损坏磁盘上的数据恢复保证。
实际设计中,应把 LVM 与文件系统、RAID、加密层、数据库备份和恢复演练分别建模。只有明确每一层保存了什么、依赖什么、失败时如何验证,才能在扩容、快照、磁盘更换或元数据损坏后做出可控的恢复决策。
系列导航与关联阅读
- 系列入口:Linux 完整学习路线:从内核与文件系统到网络、性能和生产运维
- 上一篇:XFS 深入:Allocation Group、Journal、在线扩容、修复和大文件
- 下一篇:Linux 软件 RAID:mdadm、级别、重建、降级、监控和恢复
- 延伸:Linux 块设备与存储:分区、LVM、RAID、挂载和在线扩容
- 延伸:Linux 备份与恢复:文件、块设备、快照、加密和恢复演练
官方资料
本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。

评论
0 条讨论