rsync 备份数据的底层原理
- rsync+inotify=》制作异地镜像站点(实时同步)
目的 : 为异地备份做好准备工作
rsync传递数据可以简单总结为三步
- 先验证用户身份
- 检查源路径到底需要传哪些文件,默认quick check算法
- 传输
rsync命令有上百个选项,主要分为两大类 :
- 一类是检测,检测是在源目录里面进行的。如果改变检测机制,一般情况下,会增加数据校验的可靠性,降低检查的效率。
- 还有一类是传输(比如要不要改变传输文件的属性),传输只传输源目录数据改动的部分。在源目录中,需要通过算法得出数据变化的部分,在目标目录中,也需要算法计算拼接出新的完整数据,覆盖到原文件。
相比scp进行数据备份,rsync更加耗费cpu资源。
关于检测
- quick check算法原理 : 只比较Modify(stat命令中修改文件内容的时间)和传输文件的大小,从远程拿到目标路径下所有文件的Modify和传输文件的大小,跟本地模块下的文件进行校验。
- -c选项是改变了默认的quick check算法,采用hash算法来校验文件内容,从远程拿到目标路径下所有的文件,在源服务器中把拿到的文件的内容全部读一遍,进行数据的校验。
- 大多数检查选项的调整,都会提升数据传输的可靠性,降低传输数据的效率。
关于传输
- 传输只传输源目录数据改动的部分,目标目录收到源目录传输过来的数据后,会把本机的文件内容读出来,再把源目录传输过来的新的数据内容读出来,通过算法重新拼接成一份新的数据。
- rsync总结
- 耗费cpu资源
- 源路径如果是频繁改动的,rsync不适合
- 不适合同步大文件
rsync选项补充
- 本地命令
mkdir /test
mkdir -p /a/b/c/d/e
mkdir /dst
echo 123 > /a/b/c/d/e/1.txt
-R 选项:只取相对路径
rsync -a -R /a/b/c/d/e/ /dst -R 拷贝/a以及目录下的所有子目录和文件
ls /dst
# a
rm -rf /dst/*
rsync -a -R /a/./b/c/d/e/ /dst -R 拷贝当前目录下所有的子目录和文件
ls /dst
# b
rm -rf /dst/*
cd /a/b/c
rsync -a -R d/e /dst -R 拷贝当前目录下所有的子目录和文件
ls /dst
# d
--backup 选项:保留更改前的文件内容(保留备份)
mkdir /a
mkdir /b
echo 111 > /a/1.txt
echo 222 > /a/2.txt
echo 333 > /a/3.txt
echo 666 > /b/1.txt
echo 777 > /b/2.txt
rsync -a --backup /a/ /b/ --backup 保留原文件的内容,默认在文件名后面加上~
ls /b
# 1.txt 1.txt~ 2.txt 2.txt~ 3.txt
ll /b
# total 20
# -rw-r--r-- 1 root root 4 Apr 18 17:01 1.txt
# -rw-r--r-- 1 root root 4 Apr 18 17:02 1.txt~
# -rw-r--r-- 1 root root 4 Apr 18 17:01 2.txt
# -rw-r--r-- 1 root root 4 Apr 18 17:02 2.txt~
# -rw-r--r-- 1 root root 4 Apr 18 17:01 3.txt
cat /b/1.txt~
# 666
cat /b/2.txt~
# 777
- 场景还原
rm -rf /b/*
echo 666 > /b/1.txt
echo 777 > /b/2.txt
--suffix 选项:修改保留文件名的后缀
rsync -a --backup --suffix=".bak" /a/ /b/ --suffix跟--backup连用,将默认的后缀~改成.bak
ls /b
# 1.txt 1.txt.bak 2.txt 2.txt.bak 3.txt
cat /b/1.txt.bak
# 666
cat /b/2.txt.bak
# 777
- 场景还原
rm -rf /b/*
echo 666 > /b/1.txt
echo 777 > /b/2.txt
--back-dir 选项:指定备份文件存放的目录位置(指定好备份文件存放的目录位置后,保留的文件将不会再加后缀)
rsync -a --backup --backup-dir=/b/bak /a/ /b/
ll /b
# total 12
# -rw-r--r-- 1 root root 4 Apr 18 17:01 1.txt
# -rw-r--r-- 1 root root 4 Apr 18 17:01 2.txt
# -rw-r--r-- 1 root root 4 Apr 18 17:01 3.txt
# drwxr-xr-x 2 root root 32 Apr 18 17:22 bak
ll /b/bak
# total 8
# -rw-r--r-- 1 root root 4 Apr 18 17:21 1.txt
# -rw-r--r-- 1 root root 4 Apr 18 17:21 2.txt
cat /b/bak/1.txt
# 666
cat /b/bak/2.txt
# 777
软连接与硬链接原理图
- 硬链接
echo 111 > a.txt
ln a.txt b.txt 创建一个硬连接b.txt
echo 222 >> a.txt
cat b.txt
# 111
# 222
ls -i a.txt 查看a.txt的inode号
# 33575024 a.txt
ls -i b.txt 查看b.txt的inode号
# 33575024 b.txt
rm -rf a.txt
ls
# b.txt
- 软链接
echo 666 > 1.txt
ls -i 1.txt
# 33575032 1.txt
ln -s 1.txt 2.txt 创建一个软链接2.txt
ll 2.txt 软连接2.txt指向文件1.txt
# lrwxrwxrwx 1 root root 5 Apr 18 20:51 2.txt -> 1.txt
echo 777 >> 1.txt
cat 2.txt
# 666
# 777
ls -i 2.txt 1.txt与2.txt的inode号不一样
# 33754330 2.txt
rm -rf 1.txt 删除1.txt文件
cat 2.txt 无法查看2.txt的内容
# cat: 2.txt: No such file or directory
增量备份的底层原理
- rsync的最大特点就是它可以完成增量备份,除了源目录与目标目录直接比较,rsync还支持使用
--link-dest
选项用来指定同步时的基准目录,即将源目录与基准目录之间变动的部分,同步到目标目录。
mkdir /data
echo 111 > /data/1.txt
echo 222 > /data/2.txt
ls -i /data/* 我们创建的data目录下的两个文件的inode号不一样,每一个文件的inode号都是不一样的
# 50365089 /data/1.txt 50365091 /data/2.txt
mkdir /bak/
rsync -a --delete /data/ /bak/111/ 备份data目录下的数据,将data目录下的数据备份到/bak/111/目录下
ls -i /bak/* 1.我们第一次同步数据,发现备份的数据跟data目录下的数据的inode号不一样(全量备份)
# 16816421 1.txt 16816423 2.txt
echo 333 > /data/3.txt 我们在data目录下添加新数据3.txt
ls /data 查看目录data下的文件内容
# 1.txt 2.txt 3.txt
rsync -a --delete --link-dest /bak/111/ /data/ /bak/222/ 2.做增量备份,--link-dest指定了基准目录(/bak/111/),将源目录(/data/)与基准目录(/bak/111)之间变动的部分,拷贝到目标目录(/bak/222/)
ls -i /bak/111/*
# 16816421 /bak/111/1.txt 16816423 /bak/111/2.txt
ls -i /bak/222/* 3.对比这两次数据的inode号,我们发现,数据相同的部分inode号是一样的
# 16816421 /bak/222/1.txt 16816423 /bak/222/2.txt 33575001 /bak/222/3.txt
- 我们下一次做增量备份的时候,目标目录/bak/222/就变成了基准目录,源目录还是/data/
rsync -a --delete --link-dest /bak/222/ /data/ /bak/333/
.......
- 结论 : rsync在做增量备份的时候,将上一次的数据和当前的数据进行对比。
- 在指定的目录下,发生变化的数据部分会进行拷贝,没有发生变化的数据部分制作成硬链接。
- 通过这种这种方法进行增量备份,弥补了数据恢复的效率问题,我们在做数据恢复的时候,直接找到相对应的时间点进行数据恢复就可以了。
增量备份脚本文件
vim /rsync.sh
# !/bin/bash
set -o errexit # 脚本允许发生错误,终止运行
set -o nounset # 脚本碰到变量未定义终止运行(默认情况下,碰到变量不存在,会输出空继续运行)
set -o pipefail # 控制管道的输入输出(比如xxx|echo 123,出现这种情况会报错)
readonly SOURCE_DIR="/data" # 备份的源目录
readonly TARGET_START_DIR="/bak" # 目标目录的起始目录
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=".mp4" \ 排除某些文件
"${TARGET_DIR}"
# rsync -a --delete /data/ --link-dest /data/latest /bak/%Y-%m-%d_%H:%M:%S
# 源目录 基准目录 目标目录
# 删除第一次的基准目录,然后将最新一次备份ok的目标目录链接到LATEST_LINK作为下一次的基准目录
rm -rf "${LATEST_LINK}"
# rm -rf /bak/latest
ln -s "${TARGET_DIR}" "${LATEST_LINK}"
# ln -s /bak/%Y-%m-%d_%H:%M:%S /bak/latest