VPS数据备份的“增量”陷阱——一次因文件系统元数据损坏导致的备份黑洞排查笔记
1. 故障现象:备份任务耗时暴增,且数据量异常
某日,我登录一个运行着生产环境数据库的轻云互联VPS(2H4G,SSD RAID10),例行检查crontab中配置的每日备份脚本。该脚本使用rsync将/var/www目录增量同步至远程存储。平时耗时仅3分钟,增量传输在几百MB以内。但这天,备份跑了快30分钟且仍在跑。
top查看,rsync进程CPU占用100%,iotop显示磁盘持续读取但没有明显写入。直觉告诉我没跑假死,但这数据量绝对不对。
2. 初步排查:软链接的“伪装”
我首先怀疑是某个应用生成的大量临时文件被误同步。通过lsof -p [PID]查看rsync打开的文件句柄,发现它正在疯狂读取一个名为data.tar.gz的文件,大小显示为1.2TB。但我知道数据盘总共才300GB。
这是经典的文件系统元数据损坏或软链接循环。先查看这个文件真实属性:
stat /var/www/backups/data.tar.gz
# 输出:Size: 1288490188800 Blocks: 0 IO Blocks: 4096 regular file
Size显示1.2TB, 但Blocks是0。这是一个典型的稀疏文件(sparse file)被错误识别或由于文件系统事故导致元数据中逻辑块索引损坏。rsync在传输时不会跳过稀疏空洞,会尝试读取整个扩展逻辑空间。
原因找到了:文件系统元数据出问题,导致一个原本只有几KB的小文件被赋予了巨大的逻辑尺寸。
3. 深度根因:ext4文件系统故障与备份策略的致命盲区
我检查dmesg日志,发现了大量EXT4-fs error和block bitmap inconsistency的记录。时间点正好是昨天凌晨,备份脚本执行时间。
回溯发现:昨天的备份脚本里,调用mysqldump导出数据库后,为了节省空间,手动调用truncate命令裁剪了一个日志文件。但在同一个VPS上,数据库长时间高负载时,ext4的journal commit恰好因为I/O抖动与truncate、fallocate操作产生了时序竞争。最终导致了inode元数据被写入了错误的状态,把一个小文件映射了巨大逻辑块范围。
最关键点:我的备份策略是“增量备份”,却完全没有对文件系统层做任何校验。rsync默认开启--sparse,对于这种已经写死的元数据错误,它只会傻传。监控只检查了rsync退出码(0表示成功),但1.2TB的“增量”明显是数据灾难。
4. 核心修复与策略重构
4.1 紧急擦屁股:手动校验并修复inode
该文件并不重要(是已过期的备份压缩包)。直接删除并回收block:
# 强制回收inode,即便size巨大
debugfs -w -R "kill_file /var/www/backups/data.tar.gz" /dev/vda1
# 强制进行后台文件系统检查,修复元数据
e2fsck -f -y /dev/vda1
如果文件很重要,需通过dd从裸设备对特定block范围做镜像后,再调用fsck。
4.2 备份脚本技术改造(避坑)
所有rsync备份命令,增加以下关键参数(我称之为“三防”):
--no-inc-recursive:禁止增量递归算法,在遇到破损元数据时能更保守地处理。--inplace --size-only:完全放弃时间戳和checksum检查,只通过size判断是否传输。但注意,这会完全跳过稀疏文件的空洞校验,所以配合下一项。--min-size=1:强迫rsync检查所有文件的元数据是否是合法size(跳过size=0或blocks=0的异常情况)。
最终命令示例:
rsync -avP --no-inc-recursive --inplace --size-only --min-size=1 --delete /var/www/ backupuser@remote:/backup/staging/
4.3 引入文件系统快照式备份(杀手锏)
在VPS上,我用LVM做底。备份前创建快照,再从快照挂载做数据复制。这样即使元数据逻辑有问题,快照会记录当下真实块状态。
脚本核心片段:
lvcreate -L 10G -s -n www_snap /dev/vg0/www
mount /dev/vg0/www_snap /mnt/snap_backup
# 只从快照读,真实盘不受影响
rsync -avP --delete /mnt/snap_backup/ remote:/backup/
umount /mnt/snap_backup
lvremove -f /dev/vg0/www_snap
这才是真正的“增量备份”——检查的是真实磁盘的分配状态,而不是伪造的逻辑size。 从此,就算文件系统坏了,备份也是基于快照时刻的原子性数据。
5. 结论
后来的复盘里,我还加了一个小脚本:每天备份前,用find /var/www -type f -size +10G -exec ls -lh {} \;先扫描一遍size异常的文件,一旦发现size > 物理盘可用空间,立刻报警。现在这台VPS,即使元数据挂掉,备份也绝对不会被它带沟里去。
别让相信rsync的--checksum麻痹了你,文件系统层的小坑,可以毁掉你整个备份体系。