服务器入坑指北:增量备份的深度与恢复链长度
备份越攒越多,恢复越算越怕;不知道该留几层;真出事时才发现链断了一环。
在你动手调层数之前,先把三条前提看清:① 层数是取舍,不是越少越好——层数少=每份更大、占用多;层数多=恢复要读更多份、链路更长、断一环的风险更大;② 链是链式依赖:每一层都依赖它的前一层,缺一环,整条链就落不了地——所以"备份跑完了"不等于"链是完整的";③ 多数工具需要先装包或只适用于特定文件系统(下面每条命令都带了它自己的前提)。
这篇给三段可直接粘贴的内容:数层与看链 → 算两式 → 抽验每环与恢复演练(最后一段含"别在生产上试"的有副作用动作)。取值用占位符,命令名与参数名照写。
恢复要读几份,由层数决定
恢复时需读的份数 ≈ 增量层数 + 1——因为要先落全量、再逐层往前推。
判据:先数清链里有几层,再谈恢复要多久——顺序反了会把"恢复很慢"误判成"盘太慢"。
几个名字先认全:数层用 find <备份根> -mindepth 1 -maxdepth 1 -type d(-mindepth 1 用来排除起点目录本身)或 restic snapshots;看实占用 du -sh <层目录>、看去向 du -shc <各层目录> 的合计;量耗时用 ps -o etime= -p <PID> 轮询或两次 date +%s 相减。工具名统一写法:rsync / tar / restic。
数层与看链块
find <备份根> -mindepth 1 -maxdepth 1 -type d # 数层数:排除起点目录本身,一行一层
restic snapshots # 数层:列出快照份数(需先装 restic)
du -sh <层目录> # 每层实占(人类可读)——注意 -b 是表观字节数,二者不是一回事
du -sb <层目录> # 表观字节数:与实占不是一回事,容量那一路不要用它
du -shc <各层目录> # 各层合计:硬链接只算一次,容量的总数看这个
tar -tzf <归档> # 抽验某层内容:能列出内容即该层可读
判据:每一行的数都要认领它在算什么——find <备份根> -mindepth 1 -maxdepth 1 -type d 的行数是层数,du -sh <层目录> 是每层实占、du -shc <各层目录> 是去重后的合计,tar -tzf <归档> 只判该层可读不可读——它判不出"这一层是不是增量层"(增量信息在快照文件里,不在归档清单里)。
算两式
第一式:恢复份数 = 层数 + 1。
算一遍(数值为演示值):用上面那条带 -mindepth 1 的命令数出层数 6(若你用的是不带 -mindepth 1 的老写法 find <备份根> -maxdepth 1 -type d,行数会是 7——那是把起点目录本身也数进去了,要减掉 1,并说清减的就是它)⇒ 恢复份数 = 6 + 1 = 7(1 份全量 + 6 份增量)。读数口径与算例里的"6"必须对齐,否则读者数出 7 却按 6 算。
第二式:恢复耗时 ≈ (层数 + 1) × 单层耗时;存储占用 ≈ 去重后的总实占。
"单层耗时"要按层类型分别量:全量层与增量层的代价不同——层大小相差很大时不能共用一个数;下面那个 20 分钟只在各层大小相近时当平均值用。
接着算(同一组数往下走):单层耗时按 20 分钟(层大小相近时的平均值)⇒ 最坏恢复时长 ≈ (6 + 1) * 20 = 140(分钟,约 2.3 小时)。
存储占用这一路不能靠加法:用 du -shc <各层目录> 的合计——它会把硬链接只算一次。为什么不能逐层 du -sb 相加:① -b 给的是表观字节数,与实占不是一回事(稀疏文件上能差到"一个很大、一个为 0");② 用 --link-dest=DIR 造的层里,未变化的文件是从 DIR 硬链接过去的,逐层相加会把同一份数据重复计——实测能偏高到几倍。
"单层耗时"必须是量出来的,不能假设(它=读一份并应用的代价,不是"拷一份的时间"):
date +%s # 通用测法:开始前记一次
ps -o etime= -p <PID> # 轮询该进程已运行时长取差值(ps 由 procps 提供)
date +%s # 结束后再记一次;两次相减=秒数
判据:两个量一起摆出来才叫取舍——链条越长越省容量、越脆;把"省下的空间"与"多花的恢复时间"并列看。
层是怎么造出来的(工具不同,前提不同)
tar -g <快照文件> -czf <归档> <目录> # 造一层增量:快照文件记住"上次到哪"
rsync -a --link-dest=<上一版目录> <源> <新版本目录> # 硬链接式"层":每层看着完整、实际只存变化(rsync 未装则先装)
xfsdump -l <级别> -f <归档文件> <挂载点> # 级别语义:0 是全量、更高级别只存自上次更低级别以来的变化(需先装 xfsdump,且仅适用于 XFS)
btrfs subvolume snapshot <源子卷> <目标子卷> # 快照式"层"(同一子卷上的层之间共享块;需先装 btrfs-progs)
两种"层"的关键语义:
--link-dest=DIR:与 DIR 中未变化的文件建立硬链接,只传变化——所以每层看着完整、实际只存变化;--listed-incremental=FILE:增量级别的记录处——删掉这个文件,链就断了;-l level(xfsdump):0 是全量、N 是自上次更低级别以来的变化——级别用错会让下一次静默退化成全量:它只按"自上次更低级别以来的变化"计算,级别用得比之前更低,就会把大量内容重新包含进来,你以为在存增量、其实在存全量——这是最容易让人白备份的一条。
链是链式依赖,要抽验每一环
每一层都依赖它的前一层——所以:
tar -tzf <归档> # 归档能列出内容 ⇒ 该层可读
restic snapshots # 快照清单一行一个 ⇒ 缺哪一份一眼可见
zfs send -i <起始快照> <目标快照> # 增量流:接收端必须按链补齐才能落地(需先装 OpenZFS;选项以 zfs send --help/手册现读为准)
zfs receive <数据集> # 接收落地:链上缺一环就收不了(选项以手册现读为准)
判据:抽验每一环可不可读(能列出内容/能列出快照/能收下流),而不是只看最后一份在不在——"备份跑完了但链不全"就是只看最后一份的后果;zfs receive <数据集> 收不下时的报错位置直接指出链上缺的是哪一环。
取舍:留几层
- 按天数留:简单;代价是数据量大时层数暴涨;
- 按层数留:层数固定;代价是覆盖的时间跨度不定;
- 定期合成全量:把链缩短;代价是合成本身要跑一次、要额外空间。
三条代价都要写出来。判据:先问"最坏情况下我要恢复到多久以前"——那决定层数下限;再拿第二式的恢复耗时与你的恢复目标比:恢复目标小于算出来的时长,就得插入新的全量把链缩短。
抽验每环与恢复演练块
恢复演练是有副作用的动作——在非生产上做,并记回滚点:
restic restore <快照ID> --target <目录> # 恢复动作:实测"要读几份"的入口(需先装 restic)
判据:--target <目录> 指向非生产目录;演练前记下该目录的原状态(回滚依据);演练完把实际耗时与第二式的估算对照——对不上就先看单层耗时是不是量少了。
现象到动作的判定表
| 现象 | 结论 | 动作 |
|---|---|---|
| 恢复慢 | 先数层数,别先怪盘 | 用第一式数份数,再用第二式算时长 |
| 空间涨得快 | 层数太少 | 每份更大、占用多;评估是否加层 |
| 链断一环 | 链式依赖 | 抽验每一环,用 zfs receive <数据集> 的报错定位缺哪一环 |
| 不知道留几层 | 没有下限依据 | 先定"最坏要回到多久前",再比恢复耗时 |
| 备份跑完了但链不全 | 只看最后一份 | 每层都抽验(能列出内容/能列出快照/能收下流) |
改完怎么验证:每项一条判据
- 层数数得出(清单行数);
- 两式都算得出(份数与时长);
- 单层耗时是量过的(不是假设的);
- 每环都抽验过;
- 恢复演练有回滚说明,且不在生产上;
- 装包与环境前提都带了。
这篇不解决什么
- 讲备份窗口与业务窗口错峰的那篇管什么时候备、窗口够不够;本篇管"链有多少层、恢复要读几份"。判定入口:“窗口不够”→ 那篇;“恢复要多久”→ 本篇。
- 讲稀疏文件与预分配的那篇管单份文件的实占;本篇管多份之间的依赖关系与恢复链长度。
- 讲不可复现问题的观测代价取舍的那篇管观测代价;与本篇无重叠。
踩坑清单
- 只看最后一份在不在:链断了也不知道。
- 把"层数少"当成好:每份更大、占用更多。
- 恢复慢先怪盘:其实是层数。
- 算恢复耗时却假设单层代价:结论只是猜。
- 在生产上做恢复演练:验证动作自己成了事故。
- 引用
zfs选项不加限定:那些选项字母以现读为准。 - 忘了工具要先装或限特定文件系统:
xfsdump仅 XFS、btrfs-progs/OpenZFS/restic都要先装。 - 定期合成全量却没算它要的空间:把空间问题推给了下一次。
- 点赞
- 收藏
- 关注作者
评论(0)