一、基本原理
这里“写时复制”加了一个引号,因为这是专门针对使用rsync备份时的写时复制效果,而不是事实上的写时复制(copy-on-write),其达到的目的如下:
使用 rsync 备份数据后,立即创建一份快照:
该快照的数据状态不会因为之后的同步行为被改变,而是始终保持快照创建时的状态。
该快照不会占用额外的存储空间。
再次使用 rsync 备份,原文件发生变化,或出现新的文件,则只有发生变化的文件和新出现的文件才会占用新的存储空间。
实现这一功能,主要依赖于一个事实:使用 rsync 进行备份时,针对内容发生变化的文件,新备份的文件与原文件对比,inode 将被改变。
我们知道,Linux 文件系统中以 inode 作为文件索引,inode 编号相同的多个文件称为硬链接,指向的是硬盘中同一块数据,占用同一份存储空间。多个硬链接之间地位等同,没有主从之分,只要有一份硬链接拷贝没有删除,这个文件所占用的存储空间就不会释放出来。我们使用 rsync 进行备份时,如果文件内容没有发生变化,则rsync跳过该文件不作处理,如果文件内容发生变化,rsync 则删除原文件并重新复制文件为同一文件名,此时虽然文件相同,但 inode 将发生变化,表示备份前后的文件是文件名相同的不同文件。
基于这个事实,我们可以在备份完成后,对每一个文件创建硬连接,并以相同的目录结构保存,这里把这一行为称为创建快照,快照创建完成后,之后有不同的文件备份过来,或有原文件被删除,都在备份目录发生,不会影响快照本身。
二、基本实验
基本步骤:
模拟真实业务场景,新建一个数据目录,并向数据目录写入实验数据
创建备份目录,并使用使用 rsync 进行备份
手动创建快照,对比快照与备份数据的 inode ,查看总占用空间
更改业务数据
再次使用 rsync 进行备份,对比备份前后的 快照数据
具体操作如下
# 创建目录并写入数据 mkdir data dd if=/dev/urandom of=data/bigfile bs=1M count=20 echo the first line in text file. > data/file.txt echo the another file. > data/file_another.txt # 备份 mkdir backup rsync -a --delete data/ backup/ # 手动创建快照 mkdir snapshot-1 ls backup | xargs -i{} ln backup/{} snapshot-1/{} # 查看结果 du -sh; tree --inodes -sh
结果如下(具体 inode 编号每次实验可能会有不同):
# 更改业务数据 echo the second line. >> data/file.txt rm data/file_another.txt # 同步 rsync -a --delete data/ backup/ # 查看结果 du -sh; tree --inodes -sh; diff data/file.txt snapshot-1/file.txt
结果如下:
三、通用实现
为使这一备份方式便于使用,将创建备份和删除旧备份写成 shell 函数,需要用的脚本 source 后就能使用:
# 用法: mk_snapshot backup_dir snapshot_dir function mk_snapshot(){ # mk_snapshot src_dir des_dir if [ ! -d "$1" ]; then echo "$1" is not a directory! >&2 return 1 fi WorkDir="$(pwd)" Target=$(realpath $2) cd "$1" find . -type d -exec mkdir -p "${Target}/{}" \; find . -type f -exec ln "{}" "${Target}/{}" \; # 由于文件夹的mtime不可靠,创建一个.snapshot文件用于后期辅助find判断快照创建时间 touch "${Target}/.snapshot" cd "$WorkDir" } # 用法:删除snapshots文件夹下n天前的快照 # clean_snapshot snapshots n function clean_snapshot(){ # clean_snapshot snapshot_dir/snapshot_for days_ago if [ ! -d "$1" ]; then echo "Error: \"$1\" is no a directory!" > &2 return 1 fi MountPoint=$(df "$1" | tail -1 | awk '{print $NF}') TempDir=$(mktemp -dp "$MountPoint") find "$1" -maxdepth 2 -mindepth 2 -mtime +$2 -type f -empty \ -name ".snapshot" -exec dirname "{}" \; | xargs -i{} mv {} $TempDir rm -rf "$TempDir" }
四、优点与不足
这一备份方案的优点:
可以防止损坏的业务数据污染备份数据,导致备份数据不可用
快照创建速度快
对重复的数据不额外占用存储空间
限制与不足:
由于使用的是硬连接,快照与备份数据必须处于同一分区
仅适用于类unix系统(windows现在也可以通过mklink创建硬连接,有兴趣的小伙伴可以研究一下,欢迎交流)