我正在寻找一种具有增量备份功能的备份实用程序,但方式更复杂。
我尝试了 rsync,但它似乎无法做到我想要的,或者更有可能的是,我不知道如何让它做到这一点。
这是我想用它实现的目标的一个例子。我有以下文件:
testdir
├── picture1
├── randomfile1
├── randomfile2
└── textfile1
我想运行备份实用程序并基本上在不同的目录中创建所有这些文件的存档(或 tarball):
$ mystery-command testdir/ testbak
testbak
└── 2020-02-16--05-10-45--testdir.tar
现在,假设第二天,我添加一个文件,这样我的结构如下所示:
testdir
├── picture1
├── randomfile1
├── randomfile2
├── randomfile3
└── textfile1
现在,当我运行神秘命令时,我将获得当天的另一个 tarball:
$ mystery-command testdir/ testbak
testbak
├── 2020-02-16--05-10-45--testdir.tar
└── 2020-02-17--03-24-16--testdir.tar
picture1
关键在于:我希望备份实用程序能够检测到、randomfile1
、自上次备份以来未更改的事实,randomfile2
并且仅备份新的/更改的文件,在本例中为,这样:textfile1
randomfile3
tester@raspberrypi:~ $ tar -tf testbak/2020-02-16--05-10-45--testdir.tar
testdir/
testdir/randomfile1
testdir/textfile1
testdir/randomfile2
testdir/picture1
tester@raspberrypi:~ $ tar -tf testbak/2020-02-17--03-24-16--testdir.tar
testdir/randomfile3
作为最后一个例子,假设第二天我更改了textfile1
, 并添加了picture2
和picture3
:
$ mystery-command testdir/ testbak
testbak/
├── 2020-02-16--05-10-45--testdir.tar
├── 2020-02-17--03-24-16--testdir.tar
└── 2020-02-18--01-54-41--testdir.tar
tester@raspberrypi:~ $ tar -tf testbak/2020-02-16--05-10-45--testdir.tar
testdir/
testdir/randomfile1
testdir/textfile1
testdir/randomfile2
testdir/picture1
tester@raspberrypi:~ $ tar -tf testbak/2020-02-17--03-24-16--testdir.tar
testdir/randomfile3
tester@raspberrypi:~ $ tar -tf testbak/2020-02-18--01-54-41--testdir.tar
testdir/textfile1
testdir/picture2
testdir/picture3
使用这个系统,我将通过仅备份每个备份之间的增量更改来节省空间(显然是包含所有初始文件的主备份),并且我将备份增量更改,例如,如果我进行了更改在第 2 天,并在第 3 天再次更改相同的内容,我仍然可以获得包含第 2 天更改的文件,但在第 3 天更改之前。
我认为这有点像 GitHub 的工作方式:)
我知道我可能可以创建一个运行差异的脚本,然后根据结果选择要备份的文件(或者更有效,只需获取校验和并进行比较),但我想知道是否有任何实用程序可以执行此操作容易一点:)
答案1
我尝试了 rsync,但它似乎无法做到我想要的,或者更有可能的是,我不知道如何让它做到这一点。
我知道我可能可以创建一个运行差异的脚本,然后根据结果选择要备份的文件(或者更有效,只需获取校验和并进行比较),但我想知道是否有任何实用程序可以执行此操作容易一点:)
rsync
正是那个基于差异进行复制的程序。默认情况下,它仅在上次修改时间或大小存在差异时才进行复制,但它甚至可以通过校验和与-c
.
这里的问题是你正在tar
备份。如果你不这样做,这会变得更容易。我什至不知道你为什么这么做。如果您压缩它们可能是有意义的,但您甚至没有这样做。
这增量备份的维基百科文章有一个示例rsync
命令,大致如下:
rsync -va \
--link-dest="$dst/2020-02-16--05-10-45--testdir/" \
"$src/testdir/" \
"$dst/2020-02-17--03-24-16--testdir/"
它的作用是在文件与源未更改时硬链接来自先前备份的文件。如果--copy-dest
您希望它进行复制(当$dst
在远程或在更快的驱动器上时它仍然更快)。
如果您使用带有 btrfs 等子卷的文件系统,您也可以在 rsync 之前仅从先前的备份中创建快照。快照是即时的,不占用额外的空间[1]。
btrfs subvolume snapshot \
"$dst/2020-02-16--05-10-45--testdir" \
"$dst/2020-02-17--03-24-16--testdir"
或者,如果您使用支持引用链接的文件系统,那么您也可以这样做。引用链接是通过创建一个新的 inode 但引用与源文件相同的块来完成的,从而实现 COW 支持。它仍然比常规复制更快,因为它不读取和写入数据,而且也不需要额外的空间[1]。
cp --reflink -av \
"$dst/2020-02-16--05-10-45--testdir" \
"$dst/2020-02-17--03-24-16--testdir"
不管怎样,一旦完成了类似的事情,你就可以做一个常规的rsync
复制差异:
rsync -va \
"$src/testdir/" \
"$dst/2020-02-17--03-24-16--testdir/"
不过,您可能想要添加--delete
,这将导致 rsync 从目标中删除源中不再存在的文件。
另一个有用的选项是-i
或--itemize-changes
。它生成简洁的、机器可读的输出,描述 rsync 正在执行的更改。我通常添加该选项和管道,例如:
rsync -Pai --delete \
"$src/testdir/" \
"$dst/2020-02-17--03-24-16--testdir/" \
|& tee -a "$dst/2020-02-17--03-24-16--testdir.log"
通过简单的文件来记录更改grep
。是|&
通过管道传输 stdout 和 stderr。
The是and 的-P
缩写。保留部分传输的文件,但更重要的是报告每个文件的进度。--partial
--progress
--partial
--progress
这与使用 tar 归档更改相比如何
上述解决方案导致目录似乎包含所有内容。即使是这种情况,对于任何数量/频率的备份来说,它们所占用的空间量与仅进行更改的普通 tar 存档的空间量大致相同。这是因为硬链接、引用链接和快照的工作原理。创建备份时带宽的使用也是相同的。
优点是:
- 使用 rsync 可以轻松恢复备份,而且速度更快,因为 rsync 只会传输备份中的差异。
- 如果需要的话,它们更容易浏览和修改。
- 文件删除可以自然地编码为新备份中文件的缺失。当使用 tar 档案时,人们必须诉诸黑客手段,比如删除文件
foo
、标记它foo.DELETED
或做一些复杂的事情。例如,我从未使用过口是心非,但查看其文档,似乎它通过在新 tar 中添加同名的空文件并将文件的原始签名保存在单独的 .sigtar 文件中来对删除进行编码。我想它会将原始签名与空文件的签名进行比较,以区分文件删除和对实际空文件的更改。
如果仍然希望将每个备份设置为仅保存不同(添加或修改)的文件,那么可以使用--link-dest
上述解决方案,然后使用如下所示的方法删除硬链接:
find $new_backup -type f ! -links 1 -delete
[1] 严格来说,它们确实以重复元数据的形式使用额外的空间,例如文件名等。然而,我想任何人都会认为这微不足道。
答案2
尽管tar
确实有增量模式,但还有一些更全面的工具可以完成这项工作:
它们不仅支持增量备份,还可以轻松配置需要进行完整备份的计划。例如duplicity
:duplicity --full-if-older-than 1M
将确保完整备份已运行。它们还支持及时返回到特定文件,使用普通 tar,您必须遍历所有增量文件,直到找到包含正确文件的文件。
此外,它们确实支持加密和上传到各种后端(如 sftp、blob 存储等)。显然,如果您加密,请不要忘记将密钥备份到辅助备份中!
另一个重要的方面是您可以验证备份的完整性,确保您可以恢复,例如使用duplicity verify
.
我会对基于 git 的备份策略提出负面建议。大型恢复需要大量时间。
答案3
而你为什么不考虑git
自己呢?
在一次完整备份和两次增量备份之后,您描述的策略在您继续时会变得复杂。很容易犯错误,而且能根据变化,效率会变得非常低。必须有一种轮换,即时不时地进行新的完整备份 - 然后您是否要保留旧的备份?
给定一个在职的目录“testdir”包含一些项目(文件和子目录),git
默认情况下.git
为数据创建一个隐藏的子目录。这将是针对当地的、额外的版本控制特征。对于备份,您可以将其存档/复制到介质或通过网络克隆。
这修订控制你得到的(无需请求)是 git 差异存储的副作用。
您可以省略所有分叉/分支等。这意味着您有一个名为“master”的分支。
在提交(实际上写入 git 存档/存储库)之前,您必须为配置文件配置最小用户。那么你应该首先在子目录(也许是 tmpfs)中学习和测试。有时,Git 和 tar 一样棘手。
无论如何,正如评论所说:备份很容易,困难的是恢复。
git 的缺点就是开销很小/杀伤力太大。
优点是:git曲目内容和文件名。它仅根据差异保存必要的内容(至少对于文本文件)。
例子
我的目录中有 3 个文件。之后git init
,git add .
我git commit
就有了一个 260K 的.git
目录。
然后我cp -r .git /tmp/abpic.git
(保存备份的好地方:)。我是rm
154K jpg,还有改变一个文本文件。我也rm -r .git
。
]# ls
atext btext
]# git --git-dir=/tmp/abpic.git/ ls-files
atext
btext
pic154k.jpg
在恢复文件之前我可以得到精确的差异:
]# git --git-dir=/tmp/abpic.git/ status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: atext
deleted: pic154k.jpg
no changes added to commit (use "git add" and/or "git commit -a")
这里我想按照git restore
提示进行操作。
后git --git-dir=/tmp/abpic.git/ restore \*
:
]# ls -st
total 164
4 atext 156 pic154k.jpg 4 btext
jpeg 回来了,文本文件btext
有不是已更新(保留时间戳)。中的修改atext
将被覆盖。
要重新组合存储库和(工作)目录,您只需将其复制回来即可。
]# cp -r /tmp/abpic.git/ .git
]# git status
On branch master
nothing to commit, working tree clean
当前目录中的文件与.git
存档相同(在 后面restore
)。新的更改将被显示,并且可以添加和提交,无需任何计划。您只需将其存储到另一个介质上即可用于备份。
修改文件后,您可以使用status
或diff
:
]# echo more >>btext
]# git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: btext
no changes added to commit (use "git add" and/or "git commit -a")
]# git diff
diff --git a/btext b/btext
index 96b5d76..a4a6c5b 100644
--- a/btext
+++ b/btext
@@ -1,2 +1,3 @@
This is file b
second line
+more
#]
就像git
知道文件“btext”中的“+more”一样,它也只会增量存储该行。
在git add .
(或git add btext
)之后status
,命令从红色切换为绿色,并为commit
您提供信息。
]# git add .
]# git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: btext
]# git commit -m 'btext: more'
[master fad0453] btext: more
1 file changed, 1 insertion(+)
你可以通过某种方式真正了解内容:
]# git ls-tree @
100644 blob 321e55a5dc61e25fe34e7c79f388101bd1ae4bbf atext
100644 blob a4a6c5bd3359d84705e5fd01884caa8abd1736d0 btext
100644 blob 2d550ffe96aa4347e465109831ac52b7897b9f0d pic154k.jpg
然后是前 4 个十六进制哈希数字
]# git cat-file blob a4a6
This is file b
second line
more
要通过一次提交回到过去,它是:
]# git ls-tree @^
100644 blob 321e55a5dc61e25fe34e7c79f388101bd1ae4bbf atext
100644 blob 96b5d76c5ee3ccb7e02be421e21c4fb8b96ca2f0 btext
100644 blob 2d550ffe96aa4347e465109831ac52b7897b9f0d pic154k.jpg
]# git cat-file blob 96b5
This is file b
second line
btext 的 blob 在上次提交之前具有不同的哈希值,其他的 blob 具有相同的哈希值。
概述如下:
]# git log
commit fad04538f7f8ddae1f630b648d1fe85c1fafa1b4 (HEAD -> master)
Author: Your Name <[email protected]>
Date: Sun Feb 16 10:51:51 2020 +0000
btext: more
commit 0bfc1837e20988f1b80f8b7070c5cdd2de346dc7
Author: Your Name <[email protected]>
Date: Sun Feb 16 08:45:16 2020 +0000
added 3 files with 'add .'
您无需手动添加时间戳的 tar 文件,而是使用消息和日期(以及作者)进行提交。逻辑上附加到这些提交的是文件列表和内容。
Simplegit
比 Simple 复杂 20% tar
,但您可以从中获得决定性的多 50% 的功能。
我想做OP的第三个更改:更改一个文件加上两个新的“图片”文件。我做到了,但现在我有:
]# git log
commit deca7be7de8571a222d9fb9c0d1287e1d4d3160c (HEAD -> master)
Author: Your Name <[email protected]>
Date: Sun Feb 16 17:56:18 2020 +0000
didn't add the pics before :(
commit b0355a07476c8d8103ce937ddc372575f0fb8ebf
Author: Your Name <[email protected]>
Date: Sun Feb 16 17:54:03 2020 +0000
Two new picture files
Had to change btext...
commit fad04538f7f8ddae1f630b648d1fe85c1fafa1b4
Author: Your Name <[email protected]>
Date: Sun Feb 16 10:51:51 2020 +0000
btext: more
commit 0bfc1837e20988f1b80f8b7070c5cdd2de346dc7
Author: Your Name <[email protected]>
Date: Sun Feb 16 08:45:16 2020 +0000
added 3 files with 'add .'
]#
那么,那个“你的名字”家伙在下午 6 点前不久的两次提交中到底做了什么?
最后一次提交的详细信息是:
]# git show
commit deca7be7de8571a222d9fb9c0d1287e1d4d3160c (HEAD -> master)
Author: Your Name <[email protected]>
Date: Sun Feb 16 17:56:18 2020 +0000
didn't add the pics before :(
diff --git a/picture2 b/picture2
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/picture2
@@ -0,0 +1 @@
+1
diff --git a/picture3 b/picture3
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/picture3
@@ -0,0 +1 @@
+2
]#
并检查倒数第二个提交,其消息宣布了两张图片:
]# git show @^
commit b0355a07476c8d8103ce937ddc372575f0fb8ebf
Author: Your Name <[email protected]>
Date: Sun Feb 16 17:54:03 2020 +0000
Two new picture files
Had to change btext...
diff --git a/btext b/btext
index a4a6c5b..de7291e 100644
--- a/btext
+++ b/btext
@@ -1,3 +1 @@
-This is file b
-second line
-more
+Completely changed file b
]#
发生这种情况是因为我尝试git commit -a
快捷方式git add .
,并且这两个文件是新的(未跟踪)。它以红色显示git status
,但正如我所说,git 并不比 tar 或 unix 更棘手。
“你的初出茅庐的人只知道你需要什么,但我知道你想要什么”(或者反过来。重点是它并不总是一样的)
答案4
更新:
请参阅此处的一些注意事项: 是否可以使用 tar 进行完整系统备份?
根据该答案,使用 tar 恢复增量备份很容易出错,应该避免。除非您完全确定可以在需要时恢复数据,否则请勿使用以下方法。
根据文档,您可以使用 -g/--listed-incremental 选项来创建增量 tar 文件,例如。
tar -cg data.inc -f DATE-data.tar /path/to/data
然后下次做类似的事情
tar -cg data.inc -f NEWDATE-data.tar /path/to/data
其中 data.inc 是您的增量元数据,DATE-data.tar 是您的增量存档。