Linux tar --listed-incremental 怎样制作增量归档
来源:17golang原创
时间:2026-10-27 00:38:59 284浏览 收藏
我第一次把目录备份改成增量方式时,最容易混淆的是“增量”到底由谁判断。tar 并不是凭归档文件名猜测变化,而是把上一次扫描得到的目录状态保存到快照文件,再用 --listed-incremental 对比这个快照。最小可用方案是:先生成一次 level 0 基线归档,随后继续使用同一个快照文件创建增量归档,恢复时按基线到增量的顺序全部解开。
官方地址:https://www.gnu.org/software/tar/manual/
下面只讨论 GNU tar 的 --listed-incremental,不把它扩展成通用备份系统。命令中的目录名可以替换成自己的数据目录,但一次归档链应该始终使用相同的目录根和快照文件。
先把三类文件的职责分开
这套方式至少涉及三类文件:
- 数据目录:真正要归档的目录,例如
./project。 - 快照文件:例如
./state/project.snar,由 GNU tar 保存目录状态,用来判断后续哪些内容新增、修改或删除。 - 归档文件:例如
archive-0.tar、archive-1.tar,恢复时需要按时间顺序组合。
第一次使用不存在的快照文件时,tar 会把这次归档作为 level 0。之后快照文件会被更新,因此它不是“只读配置”,而是这条归档链的状态文件。它必须和归档文件一起纳入保管范围;如果只留下某个增量归档,通常无法独立得到完整目录状态。

第一次运行先创建 level 0 基线
先准备一个专门保存快照和归档的目录。不要把快照文件放进正在被归档的目录里,否则快照本身可能成为输入的一部分。
# 创建独立的归档状态目录,避免快照文件混入数据目录 mkdir -p ./backup-state # 第一次运行没有 snapshot 文件,tar 会建立 level 0 基线归档 tar --create \ --file=./backup-state/archive-0.tar \ --listed-incremental=./backup-state/project.snar \ ./project
这条命令的关键不是 archive-0.tar 这个名字,而是 --listed-incremental 后面指向的快照文件。执行后要同时保留 archive-0.tar 和 project.snar。如果想明确强制重新做基线,可以使用 --level=0,但要把它当成一次新的备份基点处理,避免覆盖掉仍在使用的快照状态。
第二次运行复用快照文件
在 ./project 下新增或修改少量内容后,仍然指向同一个 project.snar:
# 复用上一次的快照,tar 只记录相对基线已变化的目录项 tar --create \ --file=./backup-state/archive-1.tar \ --listed-incremental=./backup-state/project.snar \ ./project
第二次运行时,tar 会更新快照文件。新增目录可能连同目录中的文件一起进入增量归档;删除也会通过 GNU tar 的增量元数据表达。不要把“文件数量少”理解成“归档可以脱离基线独立恢复”,它仍然依赖 archive-0.tar。
我在实际排期中会把快照文件当成一个需要版本化的状态:归档命令运行前先确保上一次归档已经完成,归档文件和快照文件的备份动作也要有一致的时间点。若希望从同一个基线制作多个互不影响的分支,可以先复制快照文件,再让不同任务使用各自的副本:
# 复制基线快照,避免两个归档任务共同改写同一份状态 cp ./backup-state/project.snar ./backup-state/project-snar-branch-a.snar # 分支任务使用自己的快照副本,归档链仍从同一个基线出发 tar --create \ --file=./backup-state/archive-branch-a-1.tar \ --listed-incremental=./backup-state/project-snar-branch-a.snar \ ./project
恢复时按 level 0 到增量的顺序
恢复阶段不需要原来的 .snar 文件。GNU tar 会从归档内部保存的增量元数据读取删除和目录状态信息;常见写法是把 --listed-incremental 指向 /dev/null,并严格按创建顺序依次解开:
# 先恢复完整基线,建立恢复目录的初始状态 mkdir -p ./restore tar --extract \ --listed-incremental=/dev/null \ --file=./backup-state/archive-0.tar \ --directory=./restore # 再按时间顺序叠加增量归档,得到最后一次归档时的状态 tar --extract \ --listed-incremental=/dev/null \ --file=./backup-state/archive-1.tar \ --directory=./restore
如果还有 archive-2.tar,继续放在 archive-1.tar 后面。顺序反过来会让增量内容缺少基线,或者让较早的状态覆盖较新的状态。恢复前要确认目标目录是允许被归档元数据调整的目录,因为增量恢复会尝试把目录还原到创建归档时的精确状态,包含清理当时不存在的成员。

用双 verbose 查看增量归档记录
只看普通文件名列表,往往看不出增量归档记录了哪些目录状态。GNU tar 手册建议在列出时同时使用 --incremental 和两个 --verbose:
# 查看归档中的增量目录信息,便于判断成员是存在、缺失还是目录 tar --list \ --incremental \ --verbose \ --verbose \ --file=./backup-state/archive-1.tar
输出中的标记是给人和程序读取的目录状态信息。它比单纯比较归档文件大小更适合用来理解“为什么这次没有把整个目录重新写入”。这一步只是读取归档,不会让原快照文件重新参与恢复。
这几个边界不处理,增量结果会变得难以解释
不要在归档过程中随意改时间戳
增量判断依赖时间戳。归档期间修改文件时间,或把系统时钟向后调整,都可能让结果不可靠。生产任务应尽量避免在扫描同一目录时批量重写 mtime,并保证时间同步策略稳定。
跨 NFS 或 LVM 快照时注意设备号
快照中还会记录设备号。NFS 自动挂载、LVM 快照卷等场景可能让设备号看起来发生变化,造成未修改文件被重复归档。这类场景可以显式使用 --no-check-device:
# 设备号不稳定时,避免把设备号变化直接当成目录变化 tar --create \ --file=./backup-state/archive-1.tar \ --listed-incremental=./backup-state/project.snar \ --no-check-device \ ./project
它是针对设备号比较的选择,不等于关闭所有变化判断。应结合实际挂载方式决定是否启用,并把这个参数固定在同一条归档链的配置里。
增量归档不是所有 tar 都能读
--listed-incremental 使用 GNU tar 的扩展格式,非 GNU 版本的 tar 可能无法正确读取。跨机器恢复时,应提前确认目标环境使用 GNU tar;不要把一个普通 POSIX tar 工具能解开普通归档,推断为它也能解开这条增量归档链。
我的取舍:把快照文件和归档链一起管理
对我来说,最稳妥的目录布局是把数据、快照、归档和恢复演练分开:数据目录只负责业务文件,快照文件与归档文件一起进入备份保管,恢复演练使用临时目录。每次新增一层增量,就记录它对应的基线归档和创建时间;需要迁移时,把 GNU tar 的版本与命令参数也记在任务配置中。
可以用下面的速查表做决定:
| 目标 | 做法 | 重点边界 |
|---|---|---|
| 第一次建立备份链 | 不存在的快照文件 + --listed-incremental | 得到 level 0,快照文件开始产生 |
| 后续只归档变化 | 继续使用同一快照文件 | 快照会更新,不能并发写同一份状态 |
| 恢复到最后状态 | level 0 后按顺序解开所有增量 | 恢复不需要原快照,但不能跳过中间层 |
| 设备号不稳定 | 考虑 --no-check-device | 只改变设备号比较策略,不是万能去重 |
归档前真正要确认的只有三件事:快照文件是否属于这条归档链、归档任务是否会并发改写它、恢复环境是否具备 GNU tar。把这三个问题处理好,--listed-incremental 就能稳定地承担“基线 + 变化层”的目录归档任务。
相关问题
可以只恢复 archive-1.tar 吗?
不建议。它通常只包含相对快照发生变化的内容,完整状态需要先恢复对应的 level 0,再按顺序应用后续增量。
恢复时一定要找到原来的 snar 文件吗?
不需要。创建阶段依赖快照文件,提取阶段可以把参数指向 /dev/null,因为恢复所需的增量元数据已写入归档。
为什么第二次归档仍然很大?
先排查时间戳是否被批量更新、设备号是否发生变化、归档根目录是否改变,以及快照文件是否被删除或换成了另一份。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
215 收藏
-
219 收藏
-
184 收藏
-
261 收藏
-
478 收藏
-
318 收藏
-
422 收藏
-
392 收藏
-
272 收藏
-
285 收藏
-
252 收藏
-
432 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习