zip 太好了(Mac OS X)

zip 太好了(Mac OS X)

我使用 zip 将本地目录定期备份到远程机器上。他们不相信 rsync 之类的东西,所以这是我能做的最好的事情 (?)。这是我使用的脚本

echo $(date)>>~/backuplog.txt;
if [[ -e /Volumes/backup/ ]];
then 
    cd /Volumes/Non-RAID_Storage/;
    for file in projects/*; 
        do nice -n 10 zip -vru9 /Volumes/backup/nonRaidStorage.backup.zip "$file" 2>&1 | grep -v "zip info: local extra (21 bytes)">>~/backuplog.txt;
    done;
else 
    echo "backup volume not mounted">>~/backuplog.txt;
fi

一切都运行良好,只是 zip 从来不会占用太多 CPU,所以它似乎比应有的时间花费了更多时间。它似乎从未超过 5%。我尝试将其设置为 -20,但没有任何变化。是网络或磁盘速度阻碍了该过程,还是我做错了什么?

答案1

您可能会发现它zip大部分时间都在等待 I/O(读取文件和写入压缩版本),这就是它没有像您预期的那样使用那么多 CPU 的原因。通过为进程赋予额外优先级nice对此没有影响,因为如果没有以所需的速率向任务提供数据,任务就无法使用更多 CPU 时间。

在 Linux 下,您可以看到这种情况,在类似实用程序的输出中,“IO 等待”时间的百分比很高top,对于 OSX 来说可能也是如此。

IO 等待时间的原因可能包括:

  1. 处理许多小文件(大量的头部运动读取文件和相关目录结构)
  2. 文件碎片
  3. 在备份过程中与相关驱动器上的其他活动(用户复制/移动/访问文件、计划的 AV 扫描等)竞争
  4. 通过网络读取文件(现代 CPU 压缩数据的速度远快于 100Mbit/s 链路传输数据的速度,网络延迟会加剧许多小文件的影响)或通过网络推送压缩数据(除非您的数据具有极强的可压缩性,否则同样的“现代 CPU 上的压缩速度比网络速度快”的条件也适用,因为它将从您的本地驱动器读取数据并处理数据的速度比通过网络发送结果的速度快)
  5. 网络争用(如果您正在连接的服务器有 100Mbit 链接,并且其他人正在使用它,这可能是一个问题,当然,如果它有更快的链接,问题就小得多)
  6. 驱动器速度慢或接口速度慢(如果相关驱动器连接 USB2,则其传输速度往往不会超过 25Mbyte/sec,有时会更慢,具体取决于所使用的 USB 适配器以及 USB 总线与其他快速设备的争用情况,而现代内置驱动器在批量传输时会推动两倍甚至更多的速度)

如果您想利用“空闲”的 CPU 周期,但无法通过减少 IO 延迟来实现,您可以尝试使用 7zip - 这会占用每个数据块更多的 CPU 时间,但在许多情况下,压缩效果也比 zip 好很多,从而减少了备份的大小。这会更快(因为 7zip 会导致通过网络发送的数据更少)还是更慢(因为额外的计算复杂性意味着您的 CPU 可能成为瓶颈,而不是磁盘/文件系统/网络)取决于您的机器的具体规格。

编辑:

还有一件事,有些工具会报告每个核心的进程使用情况,有些会报告每个 CPU 的进程使用情况(有些则根据设置而定),而 zip 通常是一个线程进程。因此,如果您有一个四核 CPU,那么 5% 很可能是“CPU 的 5%”,或者一个核心的大约 20%(尽管它可能在核心之间来回切换,但如果它是单线程的,则不会在任何给定时刻在多个核心上运行)。

答案2

“nice -n 10” 通过使用较低的优先级使相关程序变得更加“好用”。也许您的意思是“nice -n -10”或“nice --10”,这会使程序变得不那么“好用”,从而占用更多 CPU。

答案3

如果有人感兴趣的话,这是我的脚本。显然,这里有一些硬编码的路径,所以你不能直接运行它。它会记录它的操作和使用情况咆哮通知您任何错误。如果您没有 growl 或不想安装它,只需注释掉/删除任何带有 growlnotify 的行即可。

我所做的另一项更改是在添加文件之前将远程 zip 存档复制到本地驱动器。当时的情况是,zip 将存档复制到本地临时文件中,进行更改,然后将临时文件移回远程驱动器,针对顶级目录中的每个文件/文件夹。所以现在它只进行一次繁重的网络提升。我觉得我应该看看 tar 的这种做法。

localBaseDirectory=/Volumes/Non-RAID_Storage/;
backedUpDirectory=projects;
backupVolume=/Volumes/video-prod/Backup;
backupFile=$backupVolume/Non-RAID_Storage_backup.zip;
zipTempfile=/Volumes/B_media
localBackupTempFile=/Volumes/A_Media/backuptemp.zip;
log=~/backuplog.txt;
temp=/var/tmp/backupError.txt;
##############################################
/usr/local/bin/growlnotify -m "backing up $backedUpDirectory on $localBaseDirectory" 2>&1 >/Dev/Null  #need this redirect because growl chucks errors when run by cron;

echo $(date) > $log
if [[ -e $backupVolume ]];
then 
   if [[ -e $backupFile ]];
        then cp $backupFile $localBackupTempFile;
        mv $backupFile "$backupFile-old";
        echo "copied old backup to $backupFile-old">>$log;
    fi;
    cd $localBaseDirectory 2>$temp;
    if [[ -s $temp ]];
        #notify if there was an error cding to this directory. -s is true if the file exists and is not zero sized
        then cat $temp | /usr/local/bin/growlnotify -s;
        cat $temp >> $log;
    fi;
    for file in $backedUpDirectory/*;
    do
        /usr/local/bin/growlnotify -m "backing up $file" 2>&1 >/Dev/Null;
        #zip using verbose, recursive, update (ie don't overwrite files unless they're older), highest level compression
        #zip creates a lot of garbage errors when being verbose eo send errors to a temp file, and stdout to the log file
         nice -n 10 zip -vru9 -b $zipTempfile $localBackupTempFile "$file" 2>$temp |grep "adding:">>$log;
         #add just the important errors to the log
         cat $temp 2>/dev/null|grep "error:">>$log;
    done;
    echo "done adding to local zip file - moving to $backupFile">>$log;
    #move the local version to the remote backup file
    mv $localBackupTempFile $backupFile 2>>$log;
    echo "$(date) - backed up Non-RAID_storage">>$log
    /usr/local/bin/growlnotify -m "backed up $backedUpDirectory on $localBaseDirectory" 2>&1 >/Dev/Null;
else 
    /usr/local/bin/growlnotify -s -m "Backup volume not mounted" 2>&1 >/Dev/Null;
    echo "backup volume not mounted" >> $log;
fi;

相关内容