增量备份(incremental backup)是备份的一个类型,是指在一次全备份或上一次增量备份后,以后每次的备份只需备份与前一次相比增加或者被修改的文件。

五 增量备份

rsync 的最大特点就是它可以完成增量备份, 除了源目录与目标目录直接比较,rsync还支持使用--link-dest参数用来指定同步时的基准目录,即将源目录与基准目录之间变动的部分,同步到目标目录。

$ rsync -a --delete --link-dest /compare/path /source/path /target/path
上面命令中,--link-dest参数指定基准目录/compare/path
然后源目录/source/path跟基准目录/compare/path进行比较,找出变动的文件,将它们拷贝到目标目录/target/path。
那些没变动的文件则会生成硬链接,硬链接指向上一个/target/path中的文件。这个命令的第一次备份时是全量备份,后面就都是增量备份了。

[root@local <sub>]# mkdir /data
[root@local </sub>]# echo 111 > /data/1.txt
[root@local <sub>]# echo 222 > /data/2.txt
[root@local </sub>]#
[root@local <sub>]# mkdir /bak
[root@local </sub>]#
[root@local <sub>]#
[root@local </sub>]# # 第一次,全量
[root@local <sub>]# rsync -a --delete /data/ /bak/111/
[root@local </sub>]#
[root@local <sub>]# # 比较上一次备份与当前/data,将增量备份到新目录中
[root@local </sub>]# echo 333 > /data/3.txt
[root@local <sub>]# rsync -a --delete --link-dest /bak/111/ /data/ /bak/222 #
[root@local </sub>]# echo 666 >> /data/1.txt
[root@local <sub>]# rsync -a --delete --link-dest /bak/222 /data/ /bak/333
[root@local </sub>]# rm -rf /data/2.txt
[root@local <sub>]# rsync -a --delete --link-dest /bak/333 /data/ /bak/444
[root@local </sub>]#

与传统的增量备份略有不同的是,rsync的每次增量都会保留源目录中的全部文件,但是若文件较之上一次没有变化,则新备份中的文件只是创建了一个指向上一次对应文件的硬链接,这就保证了在不浪费空间的前提下保证了rsync的增量备份在恢复数据时,只需要找到一份备份既可以恢复到指定的状态,这就使得其数据恢复过程的简洁性与完全备份一样了,但它还比全量备份的数据量要小很多。 下面是一个脚本示例,备份/opt目录。

#!/bin/bash
# A script to perform incremental backups using rsync
set -o errexit # 它使得脚本只要发生错误,就终止执行,而不是一声不响地往下执行
set -o nounset # 在shell中,遇到变量不存在,并不会报错,而是输出空,然后继续执行后续代码
# 开启nounset选项后,脚本若碰到变量为定义则报错并终止运行




set -o pipefail # 上面的选项errexit针对管道命令是无效的,比如xxx | echo "egon",并不存在
# xxx命令,但是该管道命令整体是执行成功的,加上pipefail选项后则可以防止这件事
readonly SOURCE_DIR="/opt" # 备份的源目录
readonly TARGET_START_DIR="/egon/backups" # 目标目录的起始目录
readonly TARGET_DIR="${TARGET_START_DIR}/$(date '+%Y-%m-%d_%H:%M:%S')" # 目标目录
readonly LATEST_LINK="${TARGET_START_DIR}/latest" # 基准目录
mkdir -p "${TARGET_START_DIR}" # 先把目标目录的起始目录创建好
rsync -av --delete \
"${SOURCE_DIR}/" \
--link-dest "${LATEST_LINK}" \
--exclude=".cache" \
"${TARGET_DIR}"
# 删除基准目录LATEST_LINK,然将最新一次备份ok的目标目录链接到LATEST_LINK作为下一次的基准目录
rm -rf "${LATEST_LINK}"
ln -s "${TARGET_DIR}" "${LATEST_LINK}"

解释

1、首次执行该脚本
--link-dest指定的基准目录并不存在,但rsync命令仍会执行成功,不会报错
所以首次执行是只参照备份的源目录SOURCE_DIR进行的备份,即全量备份,目录目录下都是全新的文件
首次执行完毕后,会将最新一次备份的成果TARGET_DIR链接成基准目录,这样下一次执行rsync就有了基准目录
2、第二次执行该脚本
此时执行脚本时,用户可能已经改动了源目录里的内容(新增了文件或者改动了某个文件)
所以此刻的源目录跟当初的源目录依然不同了,如果找到这个不同呢?这就用到了上一次备份留给我们的基准目录,即
SOURCE_DIR + 上一次备份留给我们的基准目录LATEST_LINK ===》得出增量
然后将增量备份到一个新的目标目录,目标目录以当前时间命名
注意注意注意,此时的目标目录拥有与当前源目录一模一样的文件,但实际上,这些文件中,只有那些变动过的文件是刚刚新建/备份到该目录中的,其他没有变动的文件都是直接指向基准目录文件的硬链接而不是重新拷贝了一份。
所以,因为采用的是硬链接,基于rsync的增量备份在恢复数据时,只需要去/egon/backups找对应时间的目录即可,那里面每个目录里存放的就是对应时间节点的所有数据
删除上一次的基准目录,然后将当前刚刚备份好的、最新的目标目录链接成新的基准目录,为下一次增量做好准备
3、第三次执行该脚本
同上,都是增量

总结

# 1、首次执行为全量
# 2、后续执行为增量,后续的每次备份产生的以时间命名的目标目录TARGET_DIR都代表着当前的最新状态,但旧的、未曾改动的文件都是指向上一次备份的TARGET_DIR
# 3、数据恢复时,只需要找到对应时间的目标目录即可,那里面每个目录里存放的就是对应时间节点的所有数据,无需按照传统的增量恢复->先恢复全量,再依次恢复每次增量一直到当前想要的时间节点