语境
我有用户上传的内容需要备份。内容位于 3 个独立的服务器上/var/www/domain/media/
(在每个服务器上都位于同一位置)。备份是安装在 的 NFS RAID /var/www/domain/bak/
。
media/
与 属于不同的用户bak/
,基本上 web 应用程序可以写入media/
但只能读取bak/
(用户只能删除其上传,直到在 00:00 GMT 备份为止)。
这导致了两个问题:用户可以强制使用相同的文件名覆盖备份中的文件,并且文件media/
可能最终出现在两个不同的服务器上(如果用户上传两次并且由一个不同的服务器)。
所有这些都在 4 个 CenOS 7 上运行(Web X 3 + 备份 X 1)。 “Web”服务器的磁盘空间有限,需要将内容移动到备份服务器以防止它们填满磁盘。
有没有竞争条件所以这是我们不需要关心的事情。备份是通过在ssh
其他三台机器上顺序执行命令从单个备份机器完成的。
目前的解决方案
文件“移动”到备份是在清除重复项后完成的:
find /var/www/domain/media/ -type f | > media
find /var/www/domain/bak/ -type f | awk '{a=gensub("bak","media",1); print a}' > bak
cat bak media | sort | uniq -d > dupes
cat dupes | xargs rm
cp -r /var/www/domain/media/* /var/www/domain/bak/
rm -rf /var/www/domain/media/*
使用的问题mv
是/var/www/domain/media/
每个用户都有子目录。例如:
media/user13/myvideo.webm
media/user13/walk-in-the-park.webm
media/user16/cat-video.webm
media/user17/presentation-may-2016.webm
bak/user13/mountai-trip.webm
bak/user13/walk-in-the-park.webm
bak/user14/reax-the-dog.webm
该命令必须为user16
和创建目录user17
,同时必须避免覆盖bak/user13/walk-in-the-park.webm
。
当前解决方案存在问题
我想保留重复项media/
而不是删除它们。将它们复制到另一个地方也会遇到同样的问题,因为新文件会在白天出现,我需要将复制品与其副本同步。
我如何移动所有media/
不存在的文件bak/
,同时保留目录结构而不删除已存在的文件。
换句话说,我正在寻找一个能够执行以下操作的动作:
source | destination | action
----------- | ------------------- | ----------------------------------
file exists | file does not exist | move (`mv`), source -> destination
file exists | file exists | do nothing, both files stay as they are
no file | file exists | do nothing (will not trigger)
no file | file does not exist | do nothing (well, there's nothing to do something with!)
尝试更优雅的解决方案
我相信rsync
一定能够做到这一点。我知道,--remove-source-files
但我找不到办法不是检查时间戳、校验和、文件大小等等。
我将保存和检查校验和作为一个完全独立的过程。
我只关心文件名。我知道这可能会导致文件损坏,但我担心在普通磁盘上而不是在 RAID 服务器上获取损坏的文件要容易得多。
rsync
欢迎非解决方案。我想编写一个shell
脚本来执行移动(从目前的解决方案部分)。然而,一旦我想到这很容易出错,我就放弃了。
我也尝试过:
tar -cf /var/www/domain/media | (cd /var/www/domain/bar; tar -kxf -)
但它对于媒体文件(可能相当大)来说太慢,并且将所有文件保持在media/
(磁盘空间有限)。
答案1
如果文件已存在于目标树中(无论任何元数据),若不执行任何操作,请将该选项传递--ignore-existing
给 rsync。
rsync -a --remove-source-files --ignore-existing /var/www/domain/media/ /var/www/domain/bak/
为了完整起见,这是一个基于源和目标位于同一文件系统上的情况的解决方案(在这种情况下,find
这不是一个好的解决方案,因为它复制然后删除文件,而不是简单地将它们移动到目标目录) 。mv
rsync
cd /var/www/domain/media
find -type f -exec '
for x; do
if ! [ -e "/var/www/domain/bak/$x" ]; then
mkdir -p "/var/www/domain/bak/${x%/*}" &&
mv -- "$x" "/var/www/domain/bak/$x"
fi
done
' sh {} +