大家好!我是大聪明-PLUS!
在第一部分中,我介绍了基础知识 rsync:语法、关键选项和 SSH 操作。这些方面可以帮助你在基础层面上有效地使用该实用程序。在本文中,我们将深入了解它,学习如何微调整个同步过程并诊断性能问题......
第 1 章:异常和过滤器 - 完全控制同步
1.1 基本排除:--exclude 和 --exclude-from
有时您不需要复制所有内容,而是复制除某些文件或文件夹之外的所有内容。rsync为此提供了强大而灵活的排除机制。
--exclude=PATTERN:按模式排除
此选项允许您指定在同步过程中跳过的文件或目录的单一模式。模式类似于简化的正则表达式。让我们仔细看看它们:
1. 符号 *******:**替换任意字符的任意序列(包括无字符)。
-
*.txt``.txt- 匹配所有以(例如 、file.txt、document.txt) 结尾的文件1.txt。 -
project*``project- 匹配以(例如 ,project,project1) 开头的所有文件project_backup。 -
data - 匹配名称中包含该单词的所有文件 (
data例如data,,,, ) 。mydata``database_backup``old_data_file.log
2. 符号 **?****:**替换一个任意符号。
file?.txt- 匹配文件file1.txt、fileA.txt、file_.txt,但不匹配file10.txt(因为有两个符号而不是一个)并且不匹配file.txt(因为file后面必须至少有一个符号)。
3. 符号 **[ ]****:**替换括号内列出的一个字符。可指定范围。
-
file[123].txt- 仅匹配文件file1.txt,file2.txt,file3.txt。 -
report_[0-9].log- 对应于文件report_0.log,report_1.log, ...report_9.log。 -
photo_[a-z].jpg- 对应于文件photo_a.jpg,photo_b.jpg, ...,photo_z.jpg。
我们来看一个具体的例子。我的文件夹中有以下 文件 : notes.txt,,,,,, 。 data1.csv``data2.csv``image.png script.sh backup.tar.gz
- 仅复制 CSV 文件:
`rsync `-av` *.csv /backup/
`
- 排除所有以"b"开头的文件:
`rsync `-av` `--exclude='b*'` /source/ /destination/
`
- 排除名称中只有一个数字的文件:
`rsync `-av` `--exclude='data?.csv'` /source/ /destination/
`
--exclude-from=FILE:从文件中批量排除
此选项允许您从文本文件中读取所有异常,而不是在命令行中列出它们。每个异常模式必须另起一行。它有什么用途?
-
当异常很多(几十项)时,命令行会变得繁琐且难以编辑。
-
单个排除文件(例如
.rsync-ignore)可用于许多不同的命令rsync,提供统一的规则。 -
可以向 Git 添加排除文件以确保整个团队使用相同的设置。
如何创建排除文件?我们需要创建一个常规文本文件。例如backup-conf.txt:
`
*.tmp
*.swp
.nfs*
.Trash-*/
.DS_Store
cache/
logs/*.log
node_modules/
__pycache__/`
以下是使用此文件的示例:
`rsync `-av` `--exclude-from='backup-exclude.txt'` ~/important_data/ user@backup-server:/backups/`
值得注意的是,如果您的排除文件有很多模式(100-1000+),这可能会减慢速度,rsync因为它需要检查每个文件是否符合每个模式。
结论:此组合可对同步过程--exclude进行--exclude-from精确控制。您可以轻松忽略临时文件、缓存、日志和其他不需要的数据,从而使备份更干净、更快速。
1.2 结合 --include 和 --exclude:"排除除...之外的所有内容"逻辑
有时简单的排除不足以解决问题。需要更复杂的逻辑:"排除除......之外的所有内容"或"只包含这个,不包含那个"。为此,需要结合使用--include和--exclude。
逻辑如何运作:"先匹配决定"规则
了解rsync过滤规则(--include/ --exclude)按照命令中指定的顺序进行处理非常重要。
每个文件的算法如下:
-
rsync依次根据每条规则检查文件。 -
一旦找到第一条匹配的规则,该文件的命运就注定了。该文件的后续规则将被忽略。
-
如果没有匹配的规则,则包含(复制)该文件。
这表明选项的顺序至关重要。
实用模板:
示例 1:"排除除目录和其中src带有扩展名的文件之外的所有内容".py
一个典型的"统治一切,除了......"的案例,逻辑如下:
-
首先,我们包括我们想要保留的内容。
-
然后我们排除其他一切。
`rsync `-av` \
`--include='src/'` \
`--include='src/***.py'` \
`--exclude='*'` \
/source/project/ /backup/project/`
详细分析:
-
--include='src/'- 包括文件夹本身src/。 -
--include='src/***.py'``.py--- 包括中任何位置扩展名为 的所有文件src/。**--- 的特殊模式rsync,表示"任意深度的任意数量的子目录"。如果没有它,该规则将仅适用于 中的文件src/,而不适用于其子文件夹中的文件。 -
--exclude='*'--- 排除所有其他内容。此规则适用于任何与前两个规则不匹配的文件--include。
为什么顺序很重要?如果我们把它放在第一位--exclude='*',它会匹配所有文件,规则--include就永远不会被检查。所有文件都会被排除。
示例 2:"复制文件夹中除图像之外的所有图像tmp"
这里的逻辑是相反的:我们想复制很多东西,但要做出特定的例外。
`rsync `-av` \
`--include='*/'` \
`--include='*.jpg'` \
`--include='*.png'` \
`--include='*.gif'` \
`--exclude='tmp/***'` \
`--exclude='*'` \
/source/photos/ /backup/photos/`
详细分析:
-
--include='*/'------这是一条至关重要的规则。它涵盖所有目录。如果没有这条规则,rsync它根本无法进入子文件夹并根据后续规则进行检查。 -
--include='*.jpg' --include='*.png' --include='*.gif'--- 包含具有所需扩展名的文件。 -
--exclude='tmp/***'--- 排除文件夹内的所有内容tmp(无论深度如何)。文件夹本身tmp将会被创建(这要归功于第一条规则),但其内容不会被创建。 -
--exclude='*'- 排除所有非图像(例如,.txt等.log)且非文件夹的文件。
对于这样的文件它是如何工作的/source/photos/tmp/trash.jpg:
-
这是一个扩展名为 的文件
.jpg,它符合规则--include='*.jpg'。它应该被包含进去...... -
...但
rsync继续检查。接下来,它检查文件路径,发现它与模式匹配--exclude='tmp/***'。 -
该规则
--exclude将覆盖前一个规则--include,并且不会复制该文件。
总结一下很重要:
-
首先,指定最具体的规则(
--include针对需要保存的内容或--exclude特定例外),然后指定一般规则(--exclude='*')。 -
要
rsync访问子文件夹,必须使用规则明确启用它们--include='*/'。 -
要匹配目录内的路径,请使用模式
**(两个星号),这意味着"任何嵌套深度"。
1.3. 专业级别:过滤文件(--filter)
当包含和排除规则对于命令行来说太复杂时,该选项--filter(或其等效项-f)会为我们提供帮助,使我们能够使用紧凑而强大的语法在单独的文件中描述所有过滤逻辑。
过滤文件语法:+ 和 -
过滤文件中的每一行都是一条规则。规则严格按顺序处理,第一个匹配的规则将应用于每个文件。
-
+(加号)- 表示包含与模式匹配的元素。 -
-(减号)- 表示排除与模式匹配的元素。 -
!(感叹号)- 重置(清除)当前过滤列表。很少使用,通常用于非常复杂的场景。 -
R- 该规则将递归应用于所有嵌套目录。例如,R - .git/它将排除.git所有子目录。
实际示例:用于项目备份的复杂过滤器
为了更好地理解,我们来考虑一个任务:备份Web项目代码,但是:
-
排除系统和临时文件(
.git、node_modules、.DS_Store)。 -
排除日志和缓存(
*.log、*.tmp、文件夹cache/)。 -
消除严重的二进制依赖关系(
vendor/,.venv/)。 -
但包括文件本身
requirements.txt或composer.json,它描述了这些依赖关系。 -
public/assets/但即使该文件夹位于排除文件夹内(假设),也要包括该文件夹。
让我们创建一个文件project-backup.filter(名称可以是任意的):
`
`-` .git/
`-` .DS_Store
`-` *.swp
`-` *.tmp
`-` node_modules/
`-` vendor/
`-` .venv/
`-` *.log
`-` cache/
`+` /vendor/composer.json
`+` /vendor/autoload.php
`+` /requirements.txt
`+` /package-lock.json
`+` /public/assets/***
`+` /public/assets/
`-` *`
要使用的命令:
`rsync `-av` `--filter='merge project-backup.filter'` /path/to/project/ user@backup-server:/backups/project/`
该键merge指定rsync从指定文件读取规则。
工作原理(/project/vendor/composer.json 文件的步骤):
-
rsync检查文件vendor/composer.json。 -
它从上到下贯穿规则。
-
规则
- vendor/匹配。该文件应被排除。 -
但
rsync继续进一步检查规则。 -
他遵守了规则
+ /vendor/composer.json。 -
此规则覆盖上一个例外。该文件包含在备份中。
让我们考虑一下这种方法的主要优点:
-
带有过滤器的单个文件可用于多个命令
rsync和不同的机器(例如,通过将其添加到项目存储库)。 -
过滤逻辑收集在一个有据可查的文件中,而不是分散在长长的命令行中。
-
允许您描述极其复杂的包含和排除场景,这些场景几乎不可能通过命令行上的
--include/方便地表达。--exclude -
过滤文件可以提交到Git,以便整个团队按照统一的规则同步和备份数据。
结论:--filter='merge file'这是一种处理复杂同步和备份任务的专业方法。它将rsync一个简单的复制工具转变为一个功能强大且配置详细的数据传输管理系统。
第 2 章:使用基于硬链接的重复数据删除实现经济高效的备份
2.1 工作原理:硬链接的原理
要了解高级备份方案的工作原理,了解基本的文件系统概念非常重要:硬链接。
简要说明("磁盘上的一个文件,目录中的多个条目")
文件通常被认为是一个具有名称和存储在文件夹中的对象。但实际上,这并不完全正确。
-
首先,文件是磁盘上的数据集合(inode)。
-
目录中的文件名只是该数据集的链接。
硬链接是磁盘上已存在数据的附加名称(引用)。
我将尝试使用类比来简化和描述它:
> 一个文件就是一栋建筑。
> 硬链接是可以找到同一栋建筑的附加地址。
> 无论一栋建筑有多少个地址,建筑本身始终是相同的。
从技术上讲这意味着什么:
-
我们创建一个文件
A.txt。文件系统为其分配磁盘空间(一个 inode),并将名称写入A.txt指向此位置的目录。 -
B.txt让我们创建到该文件的硬链接:ln A.txt B.txt。 -
现在
A.txt和都B.txt指向磁盘上的同一组数据。 -
如果通过 更改内容
A.txt,则更改将立即显示在 中B.txt,因为它们是同一个文件。 -
如果您删除
A.txt,数据不会从磁盘中删除,因为它仍然被 引用B.txt。只有当指向它的最后一个硬链接被删除时,数据才会被删除。
硬链接的重要属性:
-
平等。不存在所谓的"原始"或"链接"。所有指向单个文件的硬链接都是绝对平等的。
-
无法通过文件系统来判断哪个名称是第一个创建的以及哪个是硬链接。
-
硬链接只能在单个文件系统内创建。您无法在驱动器之间创建硬
C:链接D:。 -
此外,您不能创建指向目录的硬链接。
这与 rsync 和备份有什么关系?
rsync可以使用-H( --hard-links) 选项来使用硬链接。这允许使用复杂的备份方案,例如"增量复制镜像"方案。
我们来看一个例子:每天都进行一次完整备份,99%的文件都不会改变,但每次复制它们都会浪费空间和时间。
硬链接解决方案:
-
周一,我们对该文件夹进行了完整复制
backup.mon。 -
星期二,你
rsync --link-dest在文件夹中执行此操作backup.tue。-
rsync查看哪些文件自昨天以来没有改变。 -
它不会再次复制它们,而是创建
backup.tue指向 中的相同数据的硬链接backup.mon。 -
仅复制新的和修改过的文件。
-
结果:
-
backup.tue截至周二,该文件夹看起来像是完整的副本。 -
但从物理上讲,它只占用周二更改的文件 + 所有文件的元数据的磁盘空间。
-
我们每天都会收到完整的备份,这占用了每天一次完整备份 + 所有更改的空间。
因此,了解硬链接为使用构建高效且经济的历史存储系统开辟了道路rsync。
2.2. 实践:使用 --link-dest 创建增量快照
此功能是"文件级重复数据删除增量备份"策略的核心。它将rsync一个简单的备份工具转变为一个强大的文件版本控制系统。
句法
`rsync `-aH` `--link-dest=`/path/to/previous/backup /source/ /new/backup/`
-
-a:存档模式(需要保留被比较文件的属性)。 -
-H:保存硬链接(对于整个备份链的正确运行很重要)。 -
--link-dest=/path/to/previous/backup:指定先前现有备份的绝对路径。 -
/source/:然后我们就会备份。 -
/new/backup/:我们将在哪里制作新的副本?
重要提示:路径必须--link-dest是绝对路径。使用相对路径可能会导致意外结果。
这是如何运作的
当rsync使用选项运行时--link-dest,它对源中的每个文件执行以下算法(/source/):
-
检查新目标 ( ) 中是否存在文件
/new/backup/。如果文件已存在,则跳过(取决于其他选项,例如--update)。 -
如果文件不在新目标中,
rsync它会在指定的目录中查找它--link-dest(在我们的示例中/path/to/previous/backup)。 -
如果在目录中找到一个文件
--link-dest,并且它与源中的文件相同(通过大小和时间戳检查),则:-
rsync不再复制数据。 -
相反,它会
/new/backup/在新目标目录()中创建一个硬链接,指向位于中的相同数据集(inode)--link-dest。
-
-
如果未找到文件
--link-dest或文件不同,它rsync会以通常的方式从源复制它。
结果:新的备份文件夹看起来像源的完整副本,但物理上仅占用新文件和修改文件的磁盘空间。
可视化示例
假设我们每天备份该文件夹~/work/。
第 1 天(星期一):制作第一份完整副本。
`rsync `-a` ~/work/ /backups/work.2023-10-01/`
-
尺寸: 假设
10 ГБ。 -
内容:文件
A.txt、、B.txt。C.jpg
第二天(星期二):
-
我们
~/work/只是改变了A.txt。 -
添加了新文件
D.pdf。 -
文件
B.txt保持C.jpg不变。
我们从中进行备份--link-dest,指向昨天的副本:
`rsync `-aH` `--link-dest=`/backups/work.2023-10-01/ ~/work/ /backups/work.2023-10-02/`
文件夹内会发生什么/backups/work.2023-10-02/:
-
A.txt(已修改):将从中完全复制~/work/(因为它与中的版本不同work.2023-10-01)。 -
D.pdf(新):将从 完全复制~/work/。 -
B.txt(未改变):rsync将创建到文件数据work.2023-10-02的硬链接。B.txt``work.2023-10-01 -
C.jpg(未改变):同样,来自 的数据的硬链接work.2023-10-01。
总大小:新的备份文件夹work.2023-10-02占用的磁盘空间大约等于修改文件的大小A.txt+ 新文件的大小D.pdf+ 少量文件元数据。最终可能只有 200 MB,而不是 10 GB。
结论:--link-dest这是一个非常有用的选项,它允许您rsync创建完整、独立的副本,同时通过硬链接重用未更改的数据,从而节省大量磁盘空间。这是构建简单而有效且历史悠久的备份系统的基础。
第 3 章:诊断和故障排除
3.1. 访问权限问题(chown)
rsync尝试保留文件元数据(包括所有者和组),但其功能受到运行用户的权限限制。
有什么问题?
非 root 用户只能将文件所有者更改为自己。如果您rsync以用户身份运行backupuser,则目标上的所有文件都将归 所有backupuser,即使它们在源上由www-data或所有root。这也适用于组
例如:我们要复制 Web 服务器文件(所有者www-data:www-data)进行备份。
`backupuser@localhost:`$ rsync` `-a` server:/var/www/html/ ./backup/`
文件夹中的所有文件./backup/都会有一个 所有者backupuser:backupuser。如果我们尝试使用它进行恢复,这将破坏备份,因为 Web 服务器www-data可能没有足够的权限访问其他用户拥有的文件。
解决方案
解决这个问题有几种策略,我们将从最简单到最灵活的顺序来考虑它们。
1. 以 root 身份运行(谨慎)
最直接的解决方案。用户root可以为文件设置任意所有者和组。为此,请rsync通过运行命令sudo。这看似合乎逻辑,但存在一定的风险。rsync具有权限的命令root可能会覆盖关键的系统文件。您需要绝对确定源路径和目标路径。不容有任何差错。
例子:
sudo` rsync `-a` server:/var/www/html/ ./backup/
`sudo` rsync `-a` /source/ user@server:/dest/`
2. 使用 --usermap****和 --groupmap
这些选项允许您在复制过程中直接重新分配 UID/GID 或用户/组名称。这是针对高级场景的解决方案。
-
--usermap=STRING:重新分配所有者。
-
--groupmap=STRING:重新分配组。
让我们看一个示例场景:在源系统上,文件的所有者是www-data,但在目标系统上没有这样的用户,并且您希望文件归用户 所有webadmin。
例子:
`rsync `-a` `--usermap=`www-data:webadmin `--groupmap=`www-data:webadmin server:/var/www/ ./backup/`
您可以指定多个规则(以逗号分隔)并使用特殊值:
`
rsync `-a` `--usermap=`*:backupuser `--groupmap=`*:backupuser /source/ /dest/`
3.通过ACL保留权限(如果具体所有者不重要)
通常,重要的不是所有者名称本身,而是访问权限(权限:读取、写入、执行)。-a(存档模式)选项已包含-p( --perms),用于保留这些权限(例如755、644)。如果随后在恢复的系统上手动设置所需的所有者,这通常足以用于备份。
4.使用 --numeric-ids
此选项强制rsync使用数字 UID 和 GID 而不是用户/组名称来执行操作。
假设源系统和目标系统具有相同的用户,且 UID/GID 相同(例如,两者的www-dataUID 均为 33)。然而,由于某种原因,两个系统上的用户名不同。
例子:
`rsync `-a` `--numeric-ids` server:/var/www/ ./backup/`
在目标系统上,这些文件的所有者是 UID=33 和 GID=33,而不是 username www-data。如果目标系统上的 UID 33 也属于 username www-data,则一切正常。
结果:
-
为了精确复制系统文件或部署应用程序,最好
rsync从root(通过sudo)运行,仔细检查命令。 -
对于具有不同用户的系统之间的复杂迁移场景,请使用
--usermap/--groupmap或--numeric-ids。
3.2. 大型目录(数百万个文件)的问题
rsync- 它不是一根魔杖。当处理包含数十万或数百万个文件的目录时,它会遇到性能限制。
有什么问题?
-
扫描时间:在开始传输之前,
rsync必须构建源端和目标端内存中所有文件的列表,以便进行比较。此操作的复杂度是线性的,有时甚至是二次的。500,000 个文件意味着 500,000 次调用stat()和比较操作。 -
内存消耗:整个文件列表及其元数据必须存储在 RAM 中。文件越多,所需的内存就越大。这可能会导致崩溃
rsync并出现错误out of memory。 -
磁盘负载:持续的元数据读取(inode 搜索)会造成巨大的磁盘负载 (IOPS),尤其是在文件系统碎片化的情况下。这会导致磁盘利用率达到 100%,而数据传输速率却几乎为零。
解决方案和解决方法
1. 使用方法 --no-recursive****及手动旁路
这个想法是放弃自动递归rsync并手动遍历子目录,例如使用find。这会将一个大操作分解成许多较小的操作。
示例:同步具有多个子文件夹的目录
`
rsync `-a` `--no-recursive` /source/ /destination/
`find` /source/ `-type` d `-exec` rsync `-a` {} /destination/ \;`
这种方法有其优点和缺点。让我们考虑一下:
-
优点:减少峰值内存消耗,实现更好的过程控制。
-
缺点:速度极慢,需要
rsync运行数千次。对于单个目录中的大量小文件无效。
2. 打包成档案( tar**)**
这通常是通过网络传输大量小文件的最佳解决方案。其思路是:在源端,将文件打包成单个流tar,然后通过网络传输,并在目标端解压缩。
例子:
`
tar `-cf` `-` /path/to/source/ | `ssh` user@host `'cd /destination/ && tar -xf -'`
tar `-czf` `-` /path/to/source/ | `ssh` user@host `'cd /destination/ && tar -xzf -'
优点:
-
元数据少得多:
rsync/ssh只能看到一个数据流,而不是数百万个文件。 -
磁盘和网络的负载明显减少。
-
目标位置将创建与存档文件数量完全相同的 inode 数量。
rsync但是,在某些情况下,它可能会创建临时文件。
缺点:
-
增量数据传输会丢失。即使只有一个文件发生更改,也会传输整个数据量。此方法不适用于频繁同步的情况。
-
无重复数据删除功能。无法使用
--link-dest。
3.混合方法(rsync + tar)
虽然方法可以组合使用,但有时应该组合使用。例如,tar每周使用 [backup] 执行一次完整备份,然后每天使用 [backup] 进行增量更新rsync,由于新文件数量较少,因此速度会更快。
4. 分析和有针对性的优化
-
--inplace请谨慎使用--append。这些选项可以在更新大文件时减少磁盘使用量,但它们也有自身的缺点(例如,--inplace它们会破坏续传功能,并且如果中断,可能会导致文件写入不完整)。 -
找到"罪魁祸首"......有时候问题不在于文件数量,而在于几个特定的目录(例如
node_modules或.git)。建议使用--exclude来将它们排除在同步之外。
`rsync `-a` `--exclude='node_modules/'` `--exclude='.git/'` /source/ user@host:/dest/`
结果:
如果rsync它在文件列表构建阶段冻结并且几乎不使用网络/磁盘进行传输,则这肯定表明大量文件存在问题。
-
对于增量同步:尝试排除不必要的子目录并接受较长的处理时间。
-
对于一次性传输或偶尔备份,请使用
tar。对于包含超过 100,000-200,000 个文件的目录,这几乎总是更快、更可靠。 -
对于持续同步:考虑使用专为大量 inode 设计的其他工具(例如,
unison或用于备份的专用文件系统,如restic/borg)。
在处理数十万个小文件时rsync,大部分时间并非花在数据传输上(数据传输量很小),而是花在元数据上。这会导致磁盘访问模式混乱。
有什么问题?
-
对于每个文件
rsync必须:-
在源上读取其元数据(inode)。
-
检查它是否存在于接收器上(另一个元数据读取)。
-
比较元数据(大小、修改时间、校验和)。
-
如果文件已更改,请读取其内容。
-
将元数据和数据写入接收器。
-
-
这些操作迫使磁盘的读取磁头不断"跳跃"整个盘片,以搜索所需的 inode 和数据块。对于传统的 HDD(硬盘)来说,这是致命的,因为寻道时间以毫秒为单位,每个文件。这会导致巨大的延迟。
-
IOPS(每秒输入/输出操作数):问题受到每秒输入/输出操作数的限制,对于 HDD 上的随机操作(数十到数百 IOPS)来说,该限制非常低。
如何监控这个?(iostat)
我们可以使用该实用程序监控磁盘上的负载iostat。
`
iostat `-x` `1
在输出中寻找什么:
-
%util:磁盘利用率百分比。接近 100% 的值表示磁盘是瓶颈。 -
await(毫秒):平均 I/O 操作延迟。高值(例如,对于 HDD,>20 毫秒)表示过载。 -
r/s,w/s:每秒读写操作的次数。处理小文件时,这些值会非常高,而avgrq-sz(平均请求大小)会很低。
显示问题的示例输出:
`Device: ... rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await %util
sda ... 0.00 0.00 850 150 5000 3000 10.05 15.00 50.00 45.00 60.00 100.00`
这里可以看到磁盘sda利用率达到 100% ( %util),而平均请求大小仅为约 10 个扇区(约 5 KB)( avgrq-sz),并且每秒操作次数非常高 ( r/s + w/s = 1000)。这是处理小文件的典型情况。
如何最大程度地减少这个问题?
1. 优先考虑:使用 SSD
。这是一个激进但又最有效的解决方案。SSD 几乎没有随机访问延迟,因此"跳磁头"问题无关紧要。SSD 的 IOPS 性能显著提升。
2.增加请求大小
-
--inplace:在某些情况下可能会有所帮助,因为rsync整个文件的覆盖会减少。 -
--whole-file(-W):强制rsync复制整个文件,而不是增量文件。这减少了源端的读取操作次数(无需计算增量),但无法解决元数据问题。适用于快速 LAN 网络。
3. 改变传输方式
和上一点一样,传输大量小文件的最佳解决方案是将它们打包成tar。
这对我们有什么帮助?tar它将所有文件及其元数据打包成一个连续的流。源端的磁盘磁头按顺序读取数据,而在目标端,它们也按顺序写入或解压缩一个大文件。这样可以最大限度地减少随机操作 (IOPS) 的数量。
举个例子:
`
rsync `-a` /source/with/many/files/ user@host:/dest/
tar `-cf` `-` `-C` /source/with/many/files . | `ssh` user@host `'tar -xf - -C /dest/'
结论:如果我们发现iostat磁盘在运行过程中被成千上万个小型 I/O 操作淹没rsync,那么期待奇迹是没有意义的。我们需要停下来并改变策略。选项包括将数据打包到归档中,或者接受操作将耗时过长的事实。在后一种情况下,以低优先级运行操作是合理的。
3.3. 传输中断问题:恢复下载并检查完整性
传输中断时的默认行为rsync非常严格:它会删除接收端所有未完全传输的文件。这样做是为了避免部分损坏的文件被误认为完整文件。幸运的是,这种行为是可以控制的。
默认行为(问题)
像往常一样,让我们设想一个场景:
-
我们正在传输一个大文件
movie.mkv(假设为 10 GB)。 -
当容量达到 8 GB 时,连接就会断开。
-
rsync因错误而终止。 -
下次启动它时
rsync,我发现接收器上有一个movie.mkv8GB 的文件与源上的文件不匹配。 -
要"从头开始"传输,
rsync请删除此部分文件并从头开始复制。
这是极其低效的,因为我们会丢失所有进度,并被迫重新传输已经传输的千兆字节。
解决方案:标志--partial --append
rsync这两个标志的组合从根本上改变了处理部分传输文件的逻辑。
-
--partial:表示
rsync传输中断时不应删除部分已传输的文件。这些文件仍保留在接收设备上。 -
--append:指示
rsync传输从中断点继续传输部分文件。rsync它会检查接收方已有多少文件,然后从适当的点开始传输。
这是如何运作的:
-
我们正在传输一个大文件
movie.mkv(10 GB)。 -
当文件大小达到 8 GB 时,连接断开。8
movie.mkvGB 的文件仍保留在接收器上(这要归功于--partial)。 -
我们再次运行相同的命令
rsync。 -
rsync``movie.mkv看到接收器上已经有一个8 GB 的文件。 -
由于
--append,它不会从头开始传输,而是检查源上的文件的前 8 GB 是否与接收器上的现有文件相匹配。 -
如果文件开头匹配(快速检查),
rsync则只下载剩余的 2 GB。
不稳定连接的示例命令:
`rsync `-av` `--progress` `--partial` `--append` /path/to/source/movie.mkv user@remote-host:/path/to/dest/`
重要的细微差别和限制
-
--append仅检查大小。此标志假定已传输文件的部分与源文件开头完全相同。它不检查已传输数据的校验和。如果由于某种原因导致传输部分的内容损坏(例如,由于目标磁盘上的错误),则生成的文件也会损坏。 -
并非适用于所有情况。此策略适用于传输过程中在源端不会发生变化的大型单文件对象(磁盘映像、存档、视频)。对于频繁更改的文件或包含大量小文件的目录,使用此策略
--append可能不安全。 -
替代方案:
--partial-dir。更高级的选项是使用--partial-dir=DIR(如上所述)。在这种情况下,部分文件不会保存到目标目录,而是保存到指定的临时文件夹(例如--partial-dir=.rsync-partial)。完整传输成功后,文件将移动到最终位置。这可以防止"损坏"文件出现在主文件夹中。
另外,关于"跳过文件"......rsync它需要快速运行。因此,默认情况下,它会使用一种简单快速的启发式方法来确定文件是否已更改。但这种速度是以潜在的不准确性为代价的。
默认比较算法(快速但不可靠)
默认情况下,rsync它使用两个标准来决定是否复制文件:
-
源和接收器上的文件大小必须匹配。
-
源上文件的最后修改时间戳不得早于目标上的最后修改时间戳。
如果满足这两个条件,rsync它会认为该文件相同并跳过它。
让我们尝试弄清楚为什么这个算法可能是错误的。
场景 1:文件已更改,但 mtime 保持不变。
- 从备份还原文件、克隆存储库或使用不更新修改时间的工具(例如某些版本的
sed -i)手动编辑文件时,这种情况很常见。文件有所不同,但rsync大小和日期相同,因此认为无需进行任何更改。
场景 2:mtime"跳"到未来或过去**
- 例如,文件是从保留了旧时间戳的存档中解压出来的。或者,机器上的时钟显示的时间不同。源上的文件可能较新,但由于时钟不同步,
rsync它将被视为已过时,并且不会被更新。
场景三:大小和mtime冲突。
- 理论上,两个不同的文件可能具有相同的大小和修改时间。
rsync它会错误地认为它们是相同的。
什么是mtime?
解决方案:校验和验证(-c / --checksum)
要强制rsync验证文件的真实 身份,请使用-c( ) 标志。它会计算源文件和目标文件上每个文件的校验和。只有校验和完全匹配时,文件才被视为相同。这是唯一"100%"可靠的确定文件是否已更改的方法。它不依赖于时间、时钟或元数据。--checksum``rsync
缺点:为什么 -c 会使性能降低 100 倍
这种方法的缺点是性能会急剧下降。原因如下:
-
计算校验和
rsync需要在源系统和目标系统上完整读取每个文件。这会给磁盘带来很大的负担(参见 6.5 节 IOPS)。 -
哈希计算算法(通常是 MD5 或 xxHash)需要每个文件的 CPU 时间。
-
当使用远程服务器时,必须在双方计算校验和并比较结果,这会增加网络延迟。
让我们尝试比较一下:
-
不使用
-c:对每个文件rsync进行快速系统调用stat()以获取其大小和日期。这是一个元数据操作,速度非常快。 -
C
-c:rsync读取每个文件的全部内容。对于一个 1GB 的文件,这意味着读取 1GB 的数据,只是为了决定是否应该读取它进行传输。
何时使用--checksum?
由于巨大的开销成本,该标志-c只应在特殊情况下使用:
-
对于错误概率不可接受的关键数据(例如数据库备份)。
-
当怀疑文件可能已更改而未更新 mtime 时。
-
与文件系统或不能可靠处理时间戳的系统同步时(例如,某些网络 FS、FAT32)。
-
检查已同步数据的完整性。
保证身份的示例命令:
`rsync `-avc` `--progress` /source/ user@host:/destination/`
实用建议: 始终从常规同步 ( rsync -av) 开始。如果您发现某些文件未按时更新,请运行 带有标志的相同命令-c以强制检查并重新发送所有必要的内容。请--checksum有选择地使用,而不是默认使用。
3.4. 压缩与加密:当 -z 没有帮助时,反而有害
"用于网络传输"的一般规则-z是正确的,但它也存在一些关键的例外情况。主要的陷阱在于压缩和加密之间的相互作用。
工作原理(简化):
-
rsync -z(压缩):数据在通过网络发送之前被动态压缩。 -
SSH(加密):数据在通过网络发送之前被加密。
-z 何时会减慢传输速度(并消耗 CPU)?
这个问题源于加密的一个基本特性:加密后的数据无法压缩。加密会将数据转换为伪随机流,因此几乎不可能找到有效压缩所需的重复模式。
场景 1:传输已压缩或加密的数据
这是主要的情况,-z它无用且有害。
-
我们传输的内容:档案(
.zip、、)、媒体(、、)、可执行文件(二进制文件.tar.gz、、)、已加密的数据。.7z``.jpg``.mp4``.mp3``.deb``.rpm -
发生了什么:
rsyncCPU 时间被浪费在尝试压缩已压缩的数据上。在最佳情况下,压缩率约为 1:1(0%);在最坏情况下,由于压缩算法的开销,数据会膨胀。 -
结果是:压缩/解压两端的 CPU 负载都增加了,而带宽却没有节省。由于处理时间比发送未压缩数据的时间更长,整体传输时间也增加了。
场景 2:网络速度非常快,但 CPU 速度很慢
-
我们拥有:千兆或更快的局域网(LAN)以及客户端和服务器上相对较弱的处理器(例如旧 NAS、低功耗 VPS)。
-
发生了什么:处理器的数据处理(压缩)速度成为瓶颈,导致快速网络通道无法饱和。数据正在等待压缩,而不是通过网络发送。
-
结果是:不压缩时,数据只会以最大网络速度传输。压缩后,CPU 无法跟上处理速度,导致整体传输速率下降。
-z 何时能加速传输(并节省流量)?
这是创建该选项的经典案例。
-
我们传输的内容:文本文件(代码
.html、、、、配置、日志)、未压缩的数据库、文档文件(.css、、)。.js``.txt``.xml``.json -
实际情况是:这类数据的压缩率极高(有时可达 5-10 倍甚至更高)。带宽节省非常可观。
-
即使考虑到压缩/解压缩的时间,整体传输时间也显著减少,尤其是在慢速链路(例如互联网)上。显著的时间节省证明了 CPU 负载的合理性。
经验法则
| 数据类型 | 使用-z? |
为什么? |
|---|---|---|
| 文本、代码、日志、XML/JSON | 是的,当然。 | 压缩率高,节省大量时间。 |
| 照片(JPG、PNG)、视频(MP4、AVI)、音乐(MP3) | 不 | 文件已经压缩。进一步压缩是没用的。 |
| 档案(ZIP、GZ、RAR) | 不 | 文件已经压缩。进一步压缩是没用的。 |
| 二进制文件、可执行文件 | 可能不是。 | 它们通常压缩效果不佳。最好在特定设备上进行测试。 |
| 混合内容 | 视情况而定 | 如果内容大部分是文本,那么它-z会有所帮助。但如果内容大部分是媒体,那就没有用了。 |
结论:不要-z盲目地将其用于一切用途。重要的是要考虑所传输数据的"性质"。盲目地将其用于-z包含大量媒体和档案的备份,必然会导致系统过载,且没有任何好处。
3.5. 性能监控:找到瓶颈
当rsync您的系统运行缓慢时,原因可能是各种系统组件:处理器、磁盘或网络。适当的监控将帮助您找到需要重点优化的地方。
1. CPU监控(top、htop)
top` `
寻找什么?
-
%Cpu(s): us(用户):用户进程的 CPU 利用率。较高的值表示该进程rsync(或其他进程)正在积极使用处理器。 -
%Cpu(s): sy(系统):系统调用对 CPU 利用率的衡量。如果rsync内核负载较大(例如,处理数百万个文件的元数据时),该值可能会很高。 -
%Cpu(s): wa(I/O 等待):最重要的指标rsync。它显示 CPU 空闲等待 I/O 操作(磁盘、网络)完成的时间百分比。如果wa该值较高(例如 >20%),则表示系统受到磁盘或网络速度的限制,处理器只是在等待数据。
结论:
-
高
us/sy:rsyncCPU 密集型(可能由于-c(校验和)或压缩-z)。 -
高
wa:瓶颈是磁盘或网络。
2. 监控磁盘I/O(iotop、iostat)
sudo` iotop `-o` `
寻找什么iotop?
-
DISK READ和DISK WRITE:每个进程的读写速度。rsync我们应该能看到活动。 -
IO>:当前磁盘负载百分比。显示进程在磁盘子系统上的消耗情况。 -
让我们来看一下整体情况:如果
rsync读/写速度很高,则表示磁盘已达到极限。如果速度很慢,而读/写wa速度top很高,则问题出在 IOPS(参见 6.5 节),这意味着磁盘无法处理大量小请求。
结论:
-
高
MB/s:rsync受磁盘吞吐量限制。 -
高
r/s+w/s低MB/s:rsync"命中"IOPS(处理许多小文件)。
3. 网络监控(nethogs、iftop)
sudo` nethogs
`sudo` iftop `
寻找什么nethogs?
-
该实用程序按进程对流量进行分组。我们可以立即看到和部分
rsync产生了多少流量。Sent``Received -
我们
iftop查看接口上的总流量(在顶部)以及哪些主机正在通信以及通信速度是多少。
结论:
-
网络速度接近最大值(例如,千兆链路上的速度为 95-98 Mbps):
rsync网络带宽有限。在这种情况下,压缩 (-z) 会通过增加 CPU 负载来降低整体性能,但它不会带来任何速度提升,因为我们已经在网络接口的极限下运行了。我们已经"达到极限"了。 -
网络速度很慢,但
rsync瓶颈在于磁盘或 CPU,而不是网络。rsync它根本无法准备传输数据。这时压缩 (-z) 就派上用场了。如果瓶颈在于准备数据(校验和、从磁盘读取)所需的 CPU 时间,那么压缩就无法解决问题。但如果网络速度较慢(例如,10 Mbps 通道)且数据压缩效果良好(文本、日志、代码),压缩将减少传输的数据量,从而提高慢速通道的效率,加快传输速度。
诊断和解决方案汇总表
| 症状(实用程序显示的内容) | 可能的原因 | 可能的解决方案 |
|---|---|---|
top: 高wa iotop: 高r/s, w/s, 低MB/s |
IOPS 问题:小文件太多。硬盘在它们之间跳转。 | 排除不需要的文件(--exclude),tar用于打包,升级到SSD。 |
top:高wa iotop:高MB/s |
磁盘无法读取/写入大文件。 | 这很难解决。可能是其他进程占用了磁盘空间。rsync运行ionice。 |
top: 高us nethogs: 低 网络速度 |
瓶颈是 CPU:处理器无法跟上加密(ssh)、压缩(-z)或哈希计算(-c)。 |
如果可能的话,删除-c或-z。CPU 升级。 |
top:低wa,低us nethogs:低网络速度 |
瓶颈 - 网络:由于信道限制或丢失,数据传输缓慢。 | 用于-z压缩,检查网络质量,--bwlimit如果安装则增加。 |
nethogs:网络速度已接近最大值。 |
理想情况:rsync有效利用可用渠道。 |
什么也不做,等待它完成。 |
让我总结一下。别猜为什么rsync它慢。打开三个终端,运行 [in them] htop,你sudo iotop -o就能sudo nethogs立即发现系统中的薄弱环节。这些信息会告诉你rsync可以使用哪些选项来加快速度。
结论
这些文章中积累的知识足以应对绝大多数日常任务。但是,如果rsync您的需求过于复杂(例如,需要端到端加密、全局重复数据删除或更复杂的备份版本控制),则值得考虑 BorgBackup、Restic 和 Rclone 等替代方案。作为本系列的合理总结,我将在下一篇(也是最后一篇文章)中简要介绍它们。