我为本地网络制作了一些常用存储库的夜间和每周镜像。有几次,有人试图在 rsync 进行时进行更新,但失败了,因为预期的文件尚未全部到位。
是否可以执行 rsync 以便全部更改的文件在完成时才会以正确的名称出现?我知道 rsync 在每次传输过程中都会使用临时的 .hidden 文件,但我可以推迟重命名直到传输完成吗?
或者,我似乎可以使用 --backup 选项将所有更改移动到一个目录,然后自动移动它们,但我希望该功能以与现在相反的方式工作。
我使用 Linux 是因为它的价值。
答案1
您可以使用该--link-dest=
选项。基本上,您将创建一个新文件夹,所有文件都硬链接到新文件夹。完成后,您只需交换文件夹名称并删除旧文件夹即可。
在 Linux 中不可能 100% 原子地完成此操作,因为没有内核/VFS 支持。但是,交换名称实际上只需要 2 个系统调用,因此完成它应该需要不到 1 秒的时间。只有在 Darwin (MAC/OSX) 上,使用 HFS 文件系统上的 exchangedata 系统调用才有可能。
答案2
我对rsync
备份[到磁盘]做了类似的事情,并且遇到了同样的问题,因为守护进程在备份运行时更新了文件。
与许多程序不同,rsync 具有许多不同的错误代码 [参见手册页底部]。以下是两个值得关注的代码:
23 — 由于错误导致部分传输
24 — 由于源文件消失导致部分传输
当 rsync 正在进行传输并遇到以下情况时,它不会立即停止。它会跳过并继续传输它要传输的文件能传输。最后,它会显示返回代码。
因此,如果您收到错误 23/24,只需重新运行 rsync。后续运行将运行得更快,通常只需传输上一次运行中丢失的文件。最终,您将获得 [或应该获得] 干净的运行。
至于原子性,我在传输过程中使用“tmp”目录。然后,当 rsync 运行干净时,我将其重命名为 [原子性]<date>
我也使用该--link-dest
选项,但我用它来保留增量备份(例如--link-dest=yesterday
每日)
虽然我自己没有用过,但它--partial-dir=DIR
可以防止隐藏文件弄乱备份目录。确保 DIR 与备份目录位于同一文件系统上,这样重命名将是原子的
虽然我是用 perl 来做这件事的,但我编写了一个脚本,它总结了我所说的内容,并针对您的具体情况进行了更详细/更精确的总结。它采用了类似 tcsh 的语法,[未经测试,有点粗糙],但您可以将其视为伪代码,以根据自己的选择编写自己的 、bash
、perl
脚本python
。请注意,它对重试次数没有限制,但您可以根据自己的意愿轻松添加。
#!/bin/tcsh -f
# repo_backup -- backup repos even if they change
#
# use_tmp -- use temporary destination directory
# use_partial -- use partial directory
# use_delta -- make delta backup
# set remote server name ...
set remote_server="..."
# directory on server for backups
set backup_top="/path_to_backup_top"
set backup_backups="$backup_top/backups"
# set your rsync options ...
set rsync_opts=(...)
# keep partial files from cluttering backup
set server_partial=${remote_server}:$backup_top/partial
if ($use_partial) then
set rsync_opts=($rsync_opts --partial-dir=$server_partial)
endif
# do delta backups
if ($use_delta) then
set latest=(`ssh ${remote_server} ls $backup_backups | tail -1`)
# get latest
set delta_dir="$backup_backups/$latest"
if ($#latest > 0) then
set rsync_opts=($rsync_opts --link-dest=${remote_server}:$delta_dir)
endif
endif
while (1)
# get list of everything to backup
# set this to whatever you need
cd /local_top_directory
set transfer_list=(.)
# use whatever format you'd like
set date=`date +%Y%m%d_%H%M%S`
set server_tmp=${remote_server}:$backup_top/tmp
set server_final=${remote_server}:$backup_backups/$date
if ($use_tmp) then
set server_transfer=$server_tmp
else
set server_transfer=$server_final
endif
# do the transfer
rsync $rsync_opts $transfer_list $server_transfer
set code=$status
# run was clean
if ($code == 0) then
# atomically install backup
if ($use_tmp) then
ssh ${remote_server} mv $backup_top/tmp $backup_backups/$date
endif
break
endif
# partial -- some error
if ($code == 23) then
continue
endif
# partial -- some files disappeared
if ($code == 24) then
continue
endif
echo "fatal error ..."
exit(1)
end
答案3
不确定这是否会对您有帮助,但是...
如果你不介意每次复制整个数据集和如果你可以使用符号链接来引用目标目录,那么你应该能够将所有内容 rsync 到临时目录中,然后交换(改名())原子地删除旧的和新的符号链接,如下所示:
% mkdir old_data new_data
% ln -s old_data current
% ln -s new_data new
% strace mv -T new current
运行
rename("new", "current")
= 0
并给出
current -> new_data
即使这样,任何尝试从该设置读取的客户端也应该cd
在尝试任何读取之前进入符号链接引用的目录,否则他们可能会从旧副本中加载部分代码/数据,从新副本中加载部分代码/数据。
答案4
镜像同步是否自动(计划任务任务或类似任务)?如果是这样,您可能为此使用了专用的 OS 用户,对吗?因此,解决方案可能是,而不是简单地复制:
- 设置目标目录权限,因此仅同步将能够访问它。
- 继续同步。
- 更改目标的权限(无条件),以便其他人可以再次访问它。
缺点是同步过程中(不确定需要多长时间)目标目录将无法访问。您需要自行决定是否可以。