我们使用 rsync 来备份服务器。
不幸的是,一些服务器的网络速度很慢。
rsync 最多需要五分钟才能检测到大型目录中没有任何变化。这些大型目录树包含大量小文件(约 80k 个文件)。
我猜测 rsync 客户端会为每个 80k 文件发送数据。
由于网络速度很慢,我想避免发送有关每个文件的 80k 次信息。
有没有办法告诉 rsync 对子目录树进行哈希和?
这样,rsync 客户端只需为巨大的目录树发送几个字节。
更新
到目前为止,我的策略是使用rsync
。但如果有其他工具更适合,我可以切换。两者(服务器和客户端)都在我的控制之下。
更新2
一个目录中有 80k 个文件树. 每个单个目录不超过 2k 个文件或子目录
更新3
网络缓慢的详细信息:
time ssh einswp 'cd attachments/200 && ls -lLR' >/tmp/list
real 0m2.645s
tmp/list 文件的大小:2MByte
time scp einswp:/tmp/list tmp/
real 0m2.821s
结论:scp 速度一样(不足为奇)
time scp einswp:tmp/100MB tmp/
real 1m24.049s
速度:1.2MB/s
2023 年更新
七年前我问过这个问题。今天我知道了:现代(云原生)应用程序不会将数据存储在文件系统中。请使用 S3 之类的 blob 存储。
答案1
一些不相关的观点:
80K 是一个很大的文件。
一个目录中有 80,000 个文件?默认情况下,没有操作系统或应用程序能够很好地处理这种情况。您恰好注意到 rsync 存在此问题。
检查你的 rsync 版本
现代 rsync 处理大型目录比过去好得多。请确保您使用的是最新版本。
即使是旧的 rsync 也能通过高延迟链接很好地处理大型目录......但 80k 文件并不大......它很大!
也就是说,rsync 的内存使用量与树中的文件数量成正比。大型目录会占用大量 RAM。速度缓慢可能是由于任一侧的 RAM 不足造成的。在观察内存使用情况的同时进行测试运行。Linux 使用任何剩余的 RAM 作为磁盘缓存,因此如果 RAM 不足,磁盘缓存就会减少。如果 RAM 用完并且系统开始使用交换,性能将非常糟糕。
确保未使用 --checksum
--checksum
(或-c
)需要读取每个文件的每个块。您可能只需要默认读取修改时间(存储在 inode 中)即可。
将工作分成小批量。
有些项目比如千兆同步它将“通过使用 perl 递归目录树来减少工作量,构建使用 rsync 进行传输的较小文件列表。”
额外的目录扫描将会产生很大的开销,但也许会带来净收益。
操作系统默认设置不适用于这种情况。
如果您使用 Linux/FreeBSD/etc 并采用所有默认设置,则所有应用程序的性能都会很糟糕。默认设置假定目录较小,以免在过大的缓存上浪费 RAM。
调整文件系统以更好地处理大型目录:文件夹大小过大会降低 IO 性能吗?
查看“namei 缓存”
BSD 类操作系统有一个缓存,可以加速查找 inode 的名称(“namei”缓存)。每个目录都有一个 namei 缓存。如果它太小,它就会成为一种阻碍,而不是一种优化。由于 rsync 对每个文件执行 lstat(),因此每个 80k 文件都会访问 inode。这可能会耗尽您的缓存。研究如何调整系统上的文件目录性能。
考虑不同的文件系统
XFS 旨在处理更大的目录。请参阅文件系统单个目录中有大量文件
也许你能做的最好的就是 5 分钟。
考虑计算正在读取多少个磁盘块,并计算预期硬件能够以多快的速度读取那么多块。
也许您的期望太高了。考虑一下在没有更改文件的情况下执行 rsync 必须读取多少个磁盘块:每个服务器都需要读取目录并为每个文件读取一个 inode。我们假设没有缓存任何内容,因为 80k 个文件可能已经耗尽了您的缓存。为了简单起见,我们假设它是 80k 个块。这大约是 40M 的数据,应该可以在几秒钟内读取。但是,如果需要在每个块之间进行磁盘搜索,则可能需要更长的时间。
因此,您需要读取大约 80,000 个磁盘块。您的硬盘能以多快的速度完成这一任务?考虑到这是随机 I/O,而不是长时间的线性读取,5 分钟可能非常不错。这是 1 / (80000 / 600),或每 7.5 毫秒读取一次磁盘。这对您的硬盘来说是快还是慢?这取决于型号。
与类似事物进行基准测试
另一种思考方式是这样的。如果没有文件发生变化,则ls -Llr
执行相同数量的磁盘活动,但从不读取任何文件数据(仅读取元数据)。运行所需的时间ls -Llr
是您的上限。
rsync(没有文件更改)是否比 慢很多
ls -Llr
?那么您使用的 rsync 选项可以改进。也许-c
启用 或读取目录和元数据(inode 数据)以外的其他标志。rsync(不更改任何文件)是否和 一样快
ls -Llr
?那么你已经尽可能地调整了 rsync。你必须调整操作系统、添加 RAM、获取更快的驱动器、更改文件系统等。
与你的开发人员交谈
80k 个文件的设计很糟糕。很少有文件系统和系统工具能够很好地处理如此大的目录。如果文件名是 abcdefg.txt,请考虑将它们存储在 abdc/abcdefg.txt 中(注意重复)。这会将目录拆分成较小的目录,但不需要对代码进行大量更改。
另外...考虑使用数据库。如果目录中有 80k 个文件,也许您的开发人员正在努力解决他们真正想要的是数据库这一事实。MariaDB、MySQL 或 PostgreSQL 将是存储大量数据的更好选择。
嘿,5分钟有什么问题?
最后,5 分钟真的那么糟糕吗?如果您每天运行一次此备份,5 分钟并不算多。是的,我喜欢速度。但是,如果 5 分钟对您的客户来说“足够好”,那么对您来说也足够好。如果您没有书面的 SLA,不妨与您的用户进行非正式讨论,了解他们期望备份需要多长时间。
如果不需要提高性能,我想你就不会问这个问题。但是,如果你的客户对 5 分钟感到满意,那么就宣布胜利,然后继续进行其他需要你努力的项目。
更新:经过一番讨论,我们确定瓶颈在于网络。在我放弃之前,我建议你做两件事 :-)。
- 尝试使用压缩从管道中挤出更多带宽。但是压缩需要更多 CPU,因此如果您的 CPU 超载,则可能会降低性能。尝试使用和不使用 rsync
-z
,并使用和不使用压缩配置 ssh。对所有 4 种组合进行计时,看看其中是否有任何组合的性能明显优于其他组合。 - 观察网络流量,看看是否有任何暂停。如果有暂停,您可以找出导致暂停的原因并进行优化。如果 rsync 始终在发送,那么您确实已经达到极限。您的选择是:
- 更快的网络
- 除了 rsync 之外的其他东西
- 将源和目标移近一点。如果做不到这一点,您可以先 rsync 到本地机器,然后再 rsync 到实际目标吗?如果系统在初始 rsync 期间必须关闭,那么这样做可能会有好处。
答案2
您还可以尝试 lsyncd,它只会在检测到文件系统发生更改时才进行 rsync,并且只同步更改的子目录。我已经在一台不错的服务器上将它用于包含多达 200 万个文件的目录。
答案3
我觉得80k 个文件今天并没有什么特别的。
我对这个问题的解释在于它的工作方式rsync
:参见这里。 他们说:在构建过程中,每个条目都以网络优化的方式传输到接收端。
这会导致在网络上以写入-停止-写入-停止-写入的方式发送数据,这被认为不如先准备完整数据然后全速通过网络发送。写入-停止-写入-停止-写入的顺序可能需要更多网络往返,在最坏的情况下甚至需要 80k 次网络往返...
请参阅有关 TCP 数据包处理、Nagle 算法等的信息。这也与经验证据相符:在设计处理批量数据的系统时,应该使用批处理技术,而不是回避实时系统中使用的单独处理每个项目/记录的技术。
我做了一个实践测试使用确实可以批量工作的同步程序:本地同步器Zaloha.sh
最近已扩展以允许远程备份:Zaloha2.sh
。从获取Fitus/Zaloha.sh,新版本属于Zaloha2网站“三只猫”附近的链接。
该程序通过find
在目录上运行来获取 CSV 文件。find
远程目录上的程序在ssh
会话中运行,并且后完成后,CSV 文件将通过 下载到本地系统scp
。find
本地目录中的 在本地运行。两个 CSV 文件的比较由 GNUsort
和在本地进行mawk
。
我选择了一个与 80k 文件最接近的目录(实际上接近 90k 文件和 3k 目录)。测试中使用的硬件并不特别或“前沿”:一台装有 Linux 的八年旧笔记本电脑和一台大约同龄的装有 Linux 的台式电脑作为远程备份主机。它们之间的连接是普通的家庭 Wi-Fi 网络。
笔记本电脑的数据保存在 USB 连接的(!)外部硬盘上,台式电脑的数据保存在内置硬盘上。
除一个未同步的文件(证明确实检测到它)外,数据处于同步状态(与您的情况相同)Zaloha2.sh
。
实践测试结果:
扫描find
USB 连接的外部硬盘耗时 1 分 7 秒。扫描find
内置硬盘耗时 14 秒。scp
通过 Wi-Fi 传输 CSV 文件并sort
进行mawk
处理耗时 34 秒。
全面的:1分56秒。 确实检测到了一个不同的文件。
有趣的是,再次运行整个测试时,两个find
测试几乎立即完成。我认为这是由于 Linux 内核缓存了目录数据。
第二次测试仅持续35 秒...
希望这可以帮助。
答案4
对于大量文件(其中几乎没有变化)的同步,noatime
在源分区和目标分区上进行设置也是值得的。这可以节省每个未更改文件写入磁盘的时间。