搞Linux的人,迟早会被磁盘空间打脸。我刚工作那会儿,最怕的就是半夜收到“磁盘空间不足”的告警,大半夜爬起来敲命令,结果发现根分区早就写满了,临时清日志、删缓存,治标不治本,第二天同事照样来问“怎么又满了”。后来被师傅按着头学会了LVM,才明白所谓磁盘管理,根本不只是“分区格式化挂载”三板斧,而是一整套关于空间规划、动态调整和故障兜底的玩法。这篇东西不打算写成man手册,主要结合我这些年实际维护Linux服务器的经验,把磁盘管理的底层逻辑、LVM的核心机制、从零搭建到扩容缩容的完整操作,以及我在生产环境里踩过的坑,一次性讲清楚。想真正搞定linux磁盘管理的人,无论你是运维、后端还是自己折腾服务器的爱好者,这篇都应该能帮上忙。
1. 为什么磁盘管理绕不开LVM
很多刚接触Linux的人容易有个错觉:磁盘管理就是分区、格式化、挂载,够用就行。这话在只有一台测试机、数据随便删的场合确实没错,但一旦你开始管理一台正经服务器,或者硬盘里装着不能丢的业务数据,传统分区方案的短板就会立刻暴露出来。
1.1 传统分区模式的两个死穴
第一个死穴是“分区大小写死在分区表里”。你用fdisk把一个2TB的盘分成一个500GB的/和一个1.4TB的/data,当初觉得够用了,半年后业务一涨,/data满了,而/可能还剩300GB用不到。你看着那300GB空闲空间,只能干瞪眼,因为分区的大小是在创建时定死的,物理相邻的剩余空间可以用工具调整,但操作起来各种约束,跨磁盘更是想都别想。
第二个死穴是“扩容流程极度痛苦”。传统分区扩容,要么依赖growpart类的工具配合在线文件系统扩展,要么就得卸载分区、用resize工具调整、甚至停机操作。对数据库主机、核心业务节点来说,卸载分区基本等于重启服务,这种停机时间在今天的业务环境里,很多团队根本承受不起。
我见过一个真实案例:某台跑着内部报表系统的机器,/data分区用的是普通分区方案,空间满了之后,运维想了各种办法,最后是挑了个凌晨、停掉服务、用live CD启动,花了三个多小时调整分区,中间还差点把数据搞坏。事后我们复盘,如果当初直接上LVM,整个过程不会超过十分钟,压根不需要停机。
1.2 LVM解决问题的基本思路
LVM之所以是Linux磁盘管理里的“标准答案”之一,是因为它在物理磁盘和文件系统之间加了一层“逻辑抽象”。传统分区里,文件系统直接挨着磁盘分区,而LVM让文件系统住在一个叫“逻辑卷(LV)”的房子里,这个房子可以变大变小,可以移动位置,甚至可以让几块物理硬盘的多个分区拼成一个房子。
一句话版本:逻辑卷就是可以随时调整大小的“分区”。它对上层文件系统来说看起来像个块设备,对下层物理磁盘来说,它又是灵活可拆解的资源组合。你想扩容量,往卷组里加一块新硬盘就行,不用动现有分区的结构;你想缩容量,只要文件系统支持,逻辑卷也能压缩。这种“空间池化、按需分配”的思路,恰恰回应了业务增长的不确定性——你永远不知道半年后需要多少空间,但你至少知道自己有随时调整的能力。
2. 磁盘管理的基础认知:设备命名与分区表
要玩转LVM,第一步不是背命令,而是搞清楚Linux眼里磁盘到底长什么样。我见过不少新手一上来就写“fdisk /dev/sdb”,结果在nvme固态盘上根本找不到设备名,其实只是命名规则没摸清。
2.1 Linux设备命名规则
传统的SATA/SAS/SCSI盘,设备名一般是/dev/sd[a-z],比如/dev/sda、/dev/sdb。这里的字母一般是内核识别磁盘的顺序,并不严格对应物理插槽位置,所以你在机房不要看着盘序乱猜槽位。分区就是在这个名字后面加数字:/dev/sda1、/dev/sda2。
NVMe固态盘用的是另一套规则:/dev/nvme0n1这种格式。它的规律是“nvme”加控制器编号,再加“n”加命名空间编号,最后是分区号。比如/dev/nvme0n1p1表示第一块NVMe盘的第一个分区。很多格式化脚本里写死了sd开头,换到新服务器上直接找不到盘,原因就在这里。
列出块设备最直观的命令是lsblk,它会把磁盘、分区、以及LVM逻辑卷的树形关系都展示出来,我几乎每天都会用一次。fdisk -l适合看全盘的几何信息和分区表,但输出又长又杂。日常看容量和挂载点,lsblk加df -h配合基本就够了。
2.2 分区表MBR与GPT
分区表就是磁盘的“目录页”,告诉系统这块盘上有哪些分区、每个分区的起始和结束位置。目前主流是两种:老牌的MBR和现代的GPT。
MBR有几个硬伤:单盘最大只支持2TB,分区数量最多4个主分区(扩展分区那个玩法现在已经没必要学了)。GPT则把单盘上限拉到了远超实用范围,分区数量几乎不受限制,还能存冗余的分区表副本,在数据安全上更踏实。
结论特别简单:在2024年,任何新盘都直接用GPT。如果你在fdisk交互界面里看到“The size of this disk is greater than 2TB”然后被逼着用GPT,或者用parted创建新表时默认选择了GPT,别纠结,这是好事。MBR留给那些老得掉牙的系统和特殊兼容场景就行。
2.3 分区工具基本用法
创建分区时,fdisk和parted是两大主力。交互式的fdisk适合人工操作:fdisk /dev/sdb进入界面,输入g创建GPT分区表,输入n新建分区,后面一路回车默认值,最后输入w写盘退出。
parted更适合脚本化操作,也支持非交互执行:
parted /dev/sdc mklabel gpt parted /dev/sdc mkpart primary 1MiB 100%两条命令就把盘清空并创建了一个占满全盘的分区。注意parted的mkpart需要指定起始和结束位置,用百分比也行。
这里有个特别多新手踩的坑:分区写盘后,内核不一定立刻刷新分区表。老手习惯顺手敲一句:
partprobe /dev/sdb让内核重新读取分区信息,尤其是热插拔的盘和虚拟化环境里的盘,少了这步,有时lsblk看不到新分区。
3. LVM核心机制拆解
LVM的英文全称是Logical Volume Manager,逻辑卷管理器。它本质上是一个内核层的映射机制,把底层的物理存储重新编排成上层可用的逻辑块设备。要理解它,核心就三个对象加一个关键单位:PV、VG、LV和PE。
3.1 三个核心对象
PV(Physical Volume,物理卷)。PV直接建在磁盘分区(比如/dev/sdb1)或整块磁盘上,它相当于对物理存储打了一个“LVM专有用”的标记。用pvcreate把分区初始化为PV之后,这个分区的系统ID会变成LVM的8e类型,分区就不再是普通的数据分区了。
VG(Volume Group,卷组)。卷组就是把一个或多个PV“打包”成一个存储资源池。这个池子的意义在于空间可以汇总:你有三块1TB的盘,每个盘做PV,三个PV一起加入一个VG,那这个VG就有3TB的“毛坯空间”,逻辑卷从里面切。
LV(Logical Volume,逻辑卷)。逻辑卷是从VG里切出来的“精装房间”。lvcreate创建之后,会生成一个块设备文件,通常在/dev/卷组名/逻辑卷名。你可以对这个设备直接格式化、挂载,就像对一个分区操作一样。唯一的不同是,这个设备的大小可以动态调整。
我自己给刚入门的朋友打过一个比方:物理硬盘是毛坯地皮,分区是把地皮划成一块块地号,PV是拿了房产证的地号,VG是你把若干已拿证的地号合并成一个大开发区,LV则是开发区里划出来的一栋商品房,文件系统就是这栋房子里的精装修。开发商(也就是你)随时可以卖掉或买入地皮,也能隔出新的户型。
3.2 PE大小的讲究
PE(Physical Extent,物理扩展块)是LVM分配空间的最小单位,默认一般是4MB。你可以把它理解成VG这个资源池里的“标准货架箱子”,不管是建立LV还是扩展LV,空间都是按PE整数倍分配的。
PE大小为什么值得了解?因为它直接决定空间分配精度和性能。假设一个大VG里PE是4MB,你想建一个刚好100GB的逻辑卷,系统按HE=100GB/4MB=25600个PE来分配,基本刚好。但如果PE设成1GB,一个小逻辑卷可能就得按1GB的整数倍来切,剩下几百MB的空间就白白卡住了。
PE的设置在创建VG时用vgcreate -s指定,比如:
vgcreate -s 16M vg_data /dev/sdb1常规场景用默认4MB完全够了。只有在巨型卷组(几十TB甚至更大)且逻辑卷数量极多时,才有必要加大PE以减少元数据开销。反过来,如果你希望存储系统更“精打细算”,可以用更小的PE,但PE过小元数据变多,性能上有一定开销。我的建议是:没有特殊性能审计要求,默认值就是最佳值。
3.3 数据在LVM中的写入路径
理解LVM性能特性,关键要明白数据从文件系统到物理磁盘到底怎么走。文件系统读写一个文件时,操作的是逻辑卷(比如/dev/vg_data/lv_data),内核的LVM驱动把这个逻辑块地址转换成“VG内偏移”,再映射到某个PV的物理偏移上。
这个映射是动态的,跟普通分区那种“逻辑分区号直接对物理LBA”完全不一样。LVM可以随时把一段物理空间从逻辑卷中抽走或补进来。所以同一块LV的数据,完全可能分散在多块物理盘的不同区域——这既是LVM灵活性的来源,也是性能上需要注意的地方:如果底层是多块机械硬盘组成的VG,而你没有做任何软件RAID,那一个LV的读写会同时压在多块盘上,IOPS不一定叠加,延迟反而可能变高。
LVM本身不做数据冗余,它只管映射,不管数据在物理盘上坏没坏。如果一块物理盘损坏,落在它上面的LV数据就是真的丢了。所以生产环境一般是RAID卡或软件RAID(mdadm)之下再上LVM,等于先用RAID解决“硬盘坏了怎么办”,再用LVM解决“空间不够怎么办”。
3.4 LVM的两种关键能力
LVM最值钱的能力就是两个:在线扩展和快照。
在线扩展的意思是,如果VG里还有空闲的PE,你可以直接对LV执行lvextend,然后扩展文件系统,整个过程业务不用停,服务不用断。这是生产环境敢于“先上LVM再说”的根本原因。
快照则是LVM的另一个杀手锏。它利用了写时复制(Copy-On-Write)机制,创建快照那一刻并不复制所有数据,而是把当时的“数据状态”冻结起来,后续原卷有新的写入时,才把被覆盖的旧数据块复制到快照预留空间里。这个概念听着抽象,后面我会专门讲实操和用途,这里先有个印象:快照不是备份,它更像“时光暂停机”,用于保证某个瞬间的数据一致性,或者让你在一个秒级时间点上回头查看状态。
4. 从零搭建一个LVM的完整实操
理论讲完,得来点真家伙。下面我带大家从三块裸盘开始,完整走一遍搭建LVM、格式化文件系统、挂载上线的流程。你可以照着在虚拟机里练,也可以直接参考流程操作自己的测试机。
4.1 环境规划与分区准备
假设机器里有三块盘要交给LVM管理:
/dev/sdb:200GB/dev/sdc:200GB/dev/sdd:500GB
我的规划是:三块盘各建一个分区,全部作为PV,组成一个900GB的卷组vg_data,然后从里面切一个200GB的逻辑卷lv_data挂到/data。
先处理第一块盘:
fdisk /dev/sdb进入交互界面后依次输入:
g创建新的GPT分区表n新建分区,一路回车用默认值t修改分区类型,输入8e(Linux LVM)w保存退出
t这一步有人会跳过,觉得后面pvcreate反正能识别。但我建议养成写清楚的习惯,分区表里标注了LVM类型,后续维护的人一眼就能看明白这块分区是干嘛的。/dev/sdc和/dev/sdd重复同样操作。全部完成后执行partprobe刷新内核分区表。
4.2 创建PV与VG
分区就绪后,初始化PV:
pvcreate /dev/sdb1 /dev/sdc1 /dev/sdd1如果哪块盘的分区类型没改对或者已经有文件系统,pvcreate会明确报错,这时候先确认分区内容确实可以放弃,再考虑用--force强刷。把三个PV合成一个VG:
vgcreate vg_data /dev/sdb1 /dev/sdc1 /dev/sdd1执行完后用vgs或者vgdisplay看一下卷组的Total PE和Free PE,应该能看到约900GB的空间。这里有个细节:vgcreate后面可以跟多个PV,中间用空格分隔,一次建好。如果你之后又加了一块新盘,再单独vgextend vg_data /dev/sde1就行。
4.3 创建LV并格式化挂载
从卷组里切逻辑卷,我用的是指定大小:
lvcreate -L 200G -n lv_data vg_data-n给逻辑卷起名字,-L指定大小。如果你想把VG剩下的空间全部分配完,也可以用:
lvcreate -l 100%FREE -n lv_data vg_data创建后,逻辑卷设备出现在/dev/vg_data/lv_data,同时也会在/dev/mapper/vg_data-lv_data生成一个对应的软链接。格式化时用哪个路径都行,但我更推荐用/dev/mapper/下的路径,因为它在脚本和配置里往往更稳定。
接下来格式化并挂载:
mkfs.xfs /dev/vg_data/lv_data mkdir -p /data mount /dev/vg_data/lv_data /data如果系统默认文件系统是ext4,用mkfs.ext4也一样。生产环境中,只要不是特殊原因(比如要兼容老内核),XFS在RHEL/CentOS系是默认推荐,性能和扩展性都很均衡。
这时用df -h /data和lsblk看一下,逻辑卷已经挂载好了,大小200GB。
4.4 自动挂载配置
手工挂载重启后会失效,必须写进/etc/fstab。我的习惯是用UUID而不是设备路径:
blkid /dev/vg_data/lv_data拿到UUID后写入:
echo "UUID=xxxx-xxxx /data xfs defaults 0 0" >> /etc/fstab然后执行:
mount -a没有报错才算通过。这里想提醒一个细节:如果文件系统是XFS,fstab里挂载参数用defaults就够了,别去网上抄那些为了性能乱加的noatime,nodiratime等参数,除非你明确知道自己在干什么。真挂了有数据的分区,参数错误导致mount失败,才是最尴尬的。
5. 在线扩容与缩容避坑指南
LVM的核心卖点就是“空间想变就变”,但这不代表你可以瞎操作。扩容和缩容的步骤一旦顺序搞反,轻则文件系统无法识别新容量,重则直接损坏数据。这一章我说清楚顺序和原理。
5.1 给逻辑卷扩容的正确顺序
很多人以为LVM扩容就是一句话,其实完整流程是三步:先保证VG有空间,再扩展LV,最后扩展文件系统。顺序反了或漏了,都会出问题。
第一步,看VG还剩多少空间:
vgs如果VG的Free空间不够,先扩VG:
vgextend vg_data /dev/sde1第二步,扩展LV,两种方式:
lvextend -L +100G /dev/vg_data/lv_data # 增加100G # 或者 lvextend -l +100%FREE /dev/vg_data/lv_data # 把VG剩余空间全给这个LV第三步,扩展文件系统。这一步最常被忽略。很多人看到lvextend成功后df -h一查,发现容量没变,就开始怀疑LVM有问题。其实LVM层已经扩了,只是文件系统还不知道。
XFS和ext4的命令不一样:
# XFS xfs_growfs /data # ext4 resize2fs /dev/vg_data/lv_dataXFS的xfs_growfs参数是挂载点,它在线就能扩展;ext4的resize2fs参数是设备路径,也是在线操作。跑完之后再df -h,容量就刷新了。
为什么会这样设计?因为文件系统在格式化时建立了一套自己的元数据追踪机制,它只认自己在逻辑卷里分配到的“地块”。逻辑卷变大了,文件系统必须通过专门的工具把它能用的新空间接管进来,否则空着也没用。
5.2 缩容:ext4可以缩,xfs千万别想
缩容在LVM里是个敏感操作,远没有扩容那么洒脱。XFS文件系统从诞生起就明确不支持缩容,这是设计层面决定的事。如果你在XFS的逻辑卷上执行缩LV,先不说工具报不报错,文件系统元数据与逻辑卷大小一旦不匹配,数据损坏几乎是必然的。所以XFS的LV只许扩,不许缩。
ext4倒是支持缩容,但顺序是反过来的:必须先缩文件系统,再缩LV。因为文件系统是住在LV里的,你得先让它知道自己要变小、并把没占用的空间让出来,然后LVM才能把那些“归还”的空间回收掉。
正经的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 # 5. 重新挂载 mount /data第4步为什么要求LV目标大小不小于文件系统目标大小?道理很简单,你不能让文件系统比它住的房子还大,那样文件系统指向的空间超出了LV边界,写数据时直接越界,结果就是鸡飞蛋打。
另外强调一下:缩容一律要有备份意识,真正生产环境里的数据库主机,能不缩就不缩,宁可留着富余空间也别赌这一把。
5.3 实战:一个扩容案例全过程
给你一个我最近处理过的完整场景。一台跑日志采集的服务器,/data是XFS格式的LVM逻辑卷,原来200GB,挂在vg_data卷组里。业务量涨了,200GB快撑爆了,要扩到400GB。我当时的操作如下:
# 查看VG空间 vgs VG #PV #LV #SN Attr VSize VFree vg_data 3 1 0 wz--n- 900.00g 700.00gVG里有700GB空闲,不需要加盘,直接扩:
lvextend -L +200G /dev/vg_data/lv_data Size of logical volume vg_data/lv_data changed from 200.00 GiB to 400.00 GiB.然后扩展文件系统:
xfs_growfs /data meta-data=/dev/mapper/vg_data-lv_data isize=512 ... data blocks changed from 52428800 to 104857600整个过程大约一两秒,业务无感知。最后df -h确认容量到位。这里多说一句,如果你的命令环境是XFS且逻辑卷名比较长,xfs_growfs后面也可以直接跟挂载点,比写设备路径更保险。
5.4 常踩的坑
第一个坑,就是刚才说的“只扩LV不扩文件系统”。我的习惯是lvextend后立刻xfs_growfs或resize2fs,中间不离开终端,两条命令连续执行,不给忘性留机会。
第二个坑,扩容时把-L +200G写成了-L 200G。-L 200G表示“把LV设成200G”,如果原来已经是200G,那等于没操作也没报错;如果原来只有100G,就变成缩容了。要用+号表示增量。这个差别很隐性,我亲眼见过有人想把卷从100G扩到300G,结果写成了-L 300G,正好是从100G扩到300G,算是歪打正着。但你要是本来200G想加到400G,却写成-L 200G,这就是把卷“降回”200G,数据还在但文件系统瞬间觉得空间缩水,风险非常大。
第三个坑,扩容时如果底层分区是MBR且大于2TB,LVM会安静地报出“not a valid LVM”之类的错。这类老盘需要转GPT,处理起来牵扯数据迁移,这里建议直接放弃MBR,用GPT永远省心。
6. 磁盘与LVM故障排查记录
我始终觉得,一个工程师的水平,不在部署时有多熟练,而在于出了问题能不能冷静地按链路排查。LVM相关故障有很强的套路性,把套路理清楚,大部分问题都能在自己可控范围内解决。
6.1 卷组状态异常:“PV missing”怎么办
最典型的LVM故障是某块PV掉线或者物理盘损坏,导致VG变成“不完整”状态。此时运行vgs,Attr列会出现字母p,表示卷组里有一个PV处于缺失状态。pvscan也会直接标红提示“PV missing”。
真正的排查顺序,第一件事永远不是急着修复,而是确认物理层有没有救:
dmesg | tail -100 lsblk看内核日志里有没有磁盘报错、掉盘记录,lsblk确认设备还在不在。如果是虚拟化环境,还要检查是不是有人误操作把磁盘热拔了。如果设备回来了,直接pvscan让它重新识别就行。
如果确认盘再也回不来了,但卷组里其他PV还健在,就需要把缺失的PV从VG里摘除:
vgreduce --removemissing vg_data这一步本质上是告诉LVM:“这块PV不要了,你以后别找它了”,同时把原本映射到那块盘上的逻辑卷空间标记为不可用。执行后数据会少一块,但剩下的卷组还能继续用——前提是你对缺了那块盘之后的数据完整性有充分预期。我的建议是:如果要执行--removemissing,先想想缺失PV上的数据丢了能不能接受,能接受才动手,否则先想办法恢复硬件或从备份重建。
6.2 误删逻辑卷的恢复
手滑删了逻辑卷,这种事发生得比想象中频繁。还好LVM自带一条不算太难的恢复路径,核心是vgcfgrestore。
LVM在每次修改VG元数据时,都会往/etc/lvm/backup/卷组名里写一份配置文件,里面记录了当时PV、LV、PE分配等完整信息。如果你误删了LV,并且删除之后没有立刻在这个卷组里创建新LV或执行其他写元数据的操作,那么恢复概率很高。
恢复思路是:先看备份文件列表,找到误删除之前的那份备份。假设误删在某个时间点之后,可以用:
vgcfgrestore --list vg_data vgcfgrestore -f /etc/lvm/backup/vg_data vg_datavgcfgrestore会把VG的元数据还原到备份时的状态,逻辑卷定义自然就回来了。但注意:文件系统数据是否还在取决于你有没有对LV设备做写入。如果删除LV后你马上格式化了或者新建了别的LV覆盖了那些PE,那数据已经物理损坏,vgcfgrestore救不回文件内容。所以“删卷之后立刻停手”是关键,这也是我常在团队里强调的原则:任何LVM变更操作前,先备份VG元数据:
vgcfgbackup vg_data一行命令,保存一条退路。
6.3 文件系统与LVM元数据不一致
有一种隐蔽故障,LV设备看起来正常,lsblk大小也对,但mount时报错“Structure needs cleaning”或者直接“Input/output error”。这种多数是文件系统元数据和LVM设备之间出现了不一致,常见诱因是异常断电、强制重启、或者缩容顺序搞反。
排查思路按文件系统类型分叉:XFS用xfs_repair,ext4用e2fsck。跑之前最好先确认LV状态正常(lvdisplay、vgs无异常),卸载后再修复:
umount /data xfs_repair /dev/vg_data/lv_data # 或者 e2fsck -f -y /dev/vg_data/lv_data说句掏心窝的话:这类修复工具带-y自动修复非常危险,尤其当文件系统损坏严重时,自动选的处理方案不一定对。我更建议e2fsck -f不带-y跑一遍,逐条看修复项,确认没有乱七八糟的“重建目录结构”之类的建议后再手动确认。至于XFS,如果xfs_repair都报“needs more memory”或长时间卡住,说明损坏程度很深,这时候该考虑的是找最近的备份,而不是继续硬修。
6.4 磁盘RAID与LVM混用的经验
现在生产服务器,硬件RAID卡或系统级RAID基本是标配,因此很多时候你看到的/dev/sdb其实是RAID卡暴露出来的虚拟盘,并不是物理单盘。在这种架构下使用LVM没有任何冲突,层次很清晰:物理盘 -> RAID -> LVM -> 文件系统。
我特别想提醒的是,不要在RAID之上再用LVM做跨卷组拼盘时,忽略RAID本身的性能特征。比如RAID6阵列的写性能本来就延迟高,你再把多个RAID组的PV塞进同一个VG,一个LV的数据同时散落在不同RAID组上,IO就会跨阵列交错,延迟更不稳定。对高并发数据库这种场景,与其把所有盘都塞进一个VG追求容量爽快,不如细分几个VG,按业务类型隔离,性能反而更容易把控。
另外,硬件RAID卡在配置时,每块虚拟盘不要残留上一任环境的文件系统签名。我遇到过把旧阵列盘直接加进VG时,LVM报“WARNING: PV /dev/sdd1 in VG xx is using an old PV header”的情况,多半就是PV头信息残留。处理办法是pvcreate前先用wipefs清掉旧签名:
wipefs -a /dev/sdd1这条命令会把该设备上的旧文件系统、LVM元数据签名全部擦除。注意它很暴力,确认数据不要了再用。
7. 快照与备份的真实用法
LVM快照是我特别喜欢跟人安利的功能,但也是最容易被误解的功能。很多人一听“快照”就以为是备份,这是很危险的理解。实际工作中,快照更像一个“瞬间的时光机”,用对了是生产救星,用错了反而给你一种虚假的安全感。
7.1 LVM快照的原理与适用场景
LVM快照基于写时复制(COW)机制。创建快照的一瞬间,系统并不复制逻辑卷的全部数据,而是只记录一个“元数据状态”。之后只要原LV上的数据块被修改,LVM在写入新数据之前,会先把原始数据拷贝到快照预留的空间里。
所以快照刚创建时几乎不占空间,随着原卷写入量的增加,快照占用的空间会越来越大。如果快照空间被写满,快照会进入“失效”(inactive)状态,此时再想挂载它读取数据,得到的已经不是完整时间点了。
快照最适合的三个场景:
- 备份前的“一致性闸点”:先打一个快照,再从快照做备份。这样备份过程中即使业务数据在变,备份看到的数据仍是快照时刻的。
- 升级/变更前的“后悔药”:给重要逻辑卷打个快照再发版本,升级出问题可以秒级回滚到变更前状态。
- 临时只读分析:从快照挂载出另一个只读副本,跑报表、做测试,不动生产数据。
快照不能替代备份,原因很直接:快照和原卷在同一个VG里、通常还在同一批物理盘上。如果整机爆炸、多块盘同时损坏,快照和原数据一起没。真正可靠的备份,必须有一份存在独立的存储介质或异地位置。
7.2 创建、挂载与删除快照
快照的创建命令非常简单:
lvcreate -L 10G -s -n snap_lv /dev/vg_data/lv_data-s表示快照,-n snap_lv是快照名字,-L 10G指定快照的COW空间大小,最后一个参数是原LV设备路径。
执行完后,/dev/vg_data/snap_lv就是一个快照设备。你可以直接挂载:
mkdir /mnt/snap mount -o ro /dev/vg_data/snap_lv /mnt/snap我习惯挂载成只读,防止一不小心写进快照。如果快照数据量不大,读写盘完成后,直接卸载并删除:
umount /mnt/snap lvremove /dev/vg_data/snap_lv删除快照前务必想清楚:快照一旦删除,那扇“时光门”就永久关闭了,没有二次后悔的机会。
7.3 利用快照做一致性备份的流程
给数据库做快照备份,最大的坑是“数据不一致”。数据库内部有缓冲区和事务日志,你直接对LV打快照,文件和磁盘状态可能是“写到一半”的,直接用快照做备份,恢复出来的数据可能是一堆不一致的碎片。
要让数据库和快照动作对齐,标准做法是:要么靠数据库自己的备份命令(比如MySQL的FLUSH TABLES WITH READ LOCK,或者备份工具内部的锁),要么用文件系统级冻结(fsfreeze)把写入动作临时停一下。
一个通用流程大致是:
- 业务或数据库执行“冻结写入”操作
- 立刻创建LVM快照
- 解除写入冻结
- 从快照复制或归档数据到独立存储
- 验证归档文件的完整性
- 删除快照
第4步注意,快照本身就是COW空间随时间膨胀,所以备份动作要快。如果把快照挂在服务器上两三天不管,小空间快照可能早就“爆了”。
操作层面,fsfreeze在XFS上特别方便:
fsfreeze -f /data lvcreate -L 10G -s -n snap_lv /dev/vg_data/lv_data fsfreeze -u /data-f冻结、-u解冻。在这两个命令之间的窗口期,文件系统写入被暂停,快照拿到的是完整一致的数据。当然,如果是生产数据库,建议直接用数据库自己的备份机制,比如MySQL的企业备份工具、PostgreSQL的pg_basebackup等,这些工具对一致性处理得更专业,LVM快照在这里更多是“辅助时间点控制”。
7.4 快照空间的监控和预防
快照失效是个很“安静”的故障,它不会敲锣打鼓告诉你。等到你某天真要靠它恢复数据时,才发现它早就因为空间写满而失效了,那才是最绝望的。
因此我的习惯是把快照当作“临时资源”而非“储物柜”:一是每次打完快照,就在脑子或运维记录里设一个清理时间;二是用监控工具盯一下快照使用率,COW空间超过70%就要警觉;三是快照的尺寸不要太小,一般建议至少等于原LV日写入量的几倍,但这个真的因业务而异,有人一个10GB快照能撑两天,有人一小时就塞满了。
我自己的经验是,快照用完就删,不让它在系统里过夜。如果是迁移或版本升级场景,快照保留到升级验证通过后第一时间删除。别信“留着也没事”这句话——它占着空间不说,还一直在被写入,早晚变成一颗定时炸弹。
写到最后,还是得说点实在的。磁盘管理和LVM,说到底是围绕“空间可控”和“数据安全”两个词打转。我从一个看到磁盘满告警就手心冒汗的新手,到现在基本能做到任何LVM变更前先vgcfgbackup、先打快照、先确认文件系统类型,靠的就是一次次在测试环境里折腾、在生产环境里看别人翻车攒下的经验。最后分享一个保留多年的小习惯:每次对卷组或逻辑卷做变更,我都会在服务器的/root/lvm_changes.log里记一行,比如“2025-03-12 lvextend +100G vg_data/lv_data,原因:日志增长”。长期看下来,这个文本日志比任何监控面板都好使,它能让你在三个月后复盘时,瞬间明白当初为什么这么改、改了之后发生了什么。磁盘空间永远在涨,但只要理解了你手上的每一块盘、每一个卷,它就不再是半夜告警的怪兽,而只是你随时可以调度的资源而已。