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,以及工具的四舍五入方式,可能造成少量差异。严谨地确认最终结果,应查看 pvsvgslvs 的 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 时,可用 partedfdisk 创建分区,并将类型设置为适合 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 至少包含:

  1. 数据区域:保存实际数据块;
  2. 元数据区域:保存虚拟块到实际块的映射、快照关系和引用计数等。

数据流可以简化为:

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_percentmetadata_percent 分别表示 Thin Pool 数据区和元数据区的使用比例。具体字段和属性显示会随 LVM2 版本变化,应以本机 lvs --helplvmthin(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 快照的一致性边界

块级快照只保证某种块映射意义上的时间点视图,不自动保证应用一致性。

例如数据库正在执行:

  1. 更新数据页;
  2. 写日志;
  3. 修改索引;
  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

具体归档文件必须根据时间和内容选择。恢复前应:

  1. 停止可能访问相关设备的服务;
  2. 确认 PV UUID 与归档文件中的 PV 匹配;
  3. 保存当前现场和当前元数据;
  4. 确认归档版本确实描述目标 LV;
  5. 尽量先在设备副本或恢复环境中演练。

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 和文件系统属于不同层。解决方法是根据文件系统类型执行 resize2fsxfs_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

实际生产操作中,不要因为示例中的卸载命令就无条件卸载业务文件系统;应先使用 findmntlsblk 确认挂载关系。

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 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。