rsync算法原理和工作流程分析

本文通过示例详细分析rsync算法原理和rsync的工作流程,是对rsync官方技术报告和官方推荐文章的解释。本文不会介绍如何使用rsync命令,而是详细解释它如何实现高效的增量传输。
以下是rsync系列篇:
1.rsync(一): rsync基本使用
2.rsync(二): rsync冷备份
3.rsync(三): inotify+rsync详细说明和sersync
4.rsync算法原理和工作流程分析

在开始分析算法原理之前,简单说明下rsync的增量传输功能。
假设待传输文件为A,如果目标路径下没有文件A, 则rsync会直接传输文件A,如果目标路径下已存在文件A,则发送端视情况决定是否要传输文件A。rsync默认使用“quic check”算法,它会比较源文件和目标文件(如果存在)的文件大小和修改时间mtime,如果两端文件的大小或mtime不同,则发送端会传输该文件,否则将忽略该文件。
如果“quick check”算法决定了要传输文件A,它不会传输整个文件A,而是只传源文件A和目标文件A所不同的部分,这才是真正的增量传输。
也就是说,rsync的增量传输体现在两个方便:文件级的增量传输和数据块级别的增量传输。文件级别的增量传输是指源主机上有,但目标主机上没有,则将直接传输该文件。数据库级别的增量传输是指只传输两文件所不同的那一部分数据。但从本质上来说,文件级别的增量传输的特殊情况。通读本文后,很容易理解这一点。

1.1 需要解决的问题

假设主机a上有文件A,主机b上有文件A’(实际上这两个文件是同名文件,此处为了区分所以命名为A和A’), 现在要让A’和A文件保持同步。
最简单的方法是将A文件直接拷贝到b主机上。但如果文件A很大,且A’ 和 A 是很相似的(意味着两文件实际内容只有少部分不同),拷贝整个文件A可能会消耗不少时间。如果可以拷贝 A和A’ 不同的那一小部分,则传输的过程会很快。rsync增量传输算法就充分利用了文件的相似性,解决了远程增量拷贝的问题。
假设文件 A 的内容为 “123xxabc def”, 文件A’ 的内容为“123abcdefg”。A和A’ 相比,相同的数据部分有123/abc/def, A中多出的内容为xx和一个空格,但文件A’ 比文件A多出了数据g,最终的目标是让A’和A的内容相同。
如果采用rsync增量传输算法,a主机只传输文件A中的xx和空格数据给b主机,对于那些相同的内容123/abc/def,b主机会直接从 A’ 文件中拷贝。根据这两个来源的数据,b主机就能组建成一个文件 A 的副本,最后将此副本文件重命名并覆盖掉A’文件就保证了同步。
虽然看上去过程很简单,但其中有很多细节需要去探究。例如,a主机如何知道A文件中哪些部分和A’文件不同,b主机接收a主机发送的 A 、A’ 不同部分的数据,如何组建文件A的副本。

1.2 rsync 增量传输算法原理

假设执行的 rsync 命令是将 A 文件推到b主机上使得 A’ 和 A 文件保持同步,即主机a是源主机,是数据的发送端(sender),b是目标主机,是数据的接收端(receiver).在保证保证 A’ 和 A 文件同步时,大致有以下6个过程:
(1).a主机告诉b主机文件A待传输。

(2).b主机收到信息后,将文件 A’ 划分为一系列大小固定的数据块(建议大小在500-1000字节之间),并以chunk号码对数据块进行编号,同时还会记录数据块的起始偏移地址以及数据块长度。显然最后一个数据块的大小可能更小。

对于文件 A’ 的内容"123abcdefg"来说,假设划分的数据块大小为3字节,则根据字符数划分成了以下几个数据块:

count=4 n=3 rem=1 这表示划分了4个数据块,数据块大小为3字节,剩余1字节给了最后一个数据块

chunk[0]:offset=0 len=3 该数据块对应的内容为123
chunk[1]:offset=3 len=3 该数据块对应的内容为abc
chunk[2]:offset=6 len=3 该数据块对应的内容为def
chunk[3]:offset=9 len=1 该数据块对应的内容为g

当然,实际信息中肯定是不会包括文件内容的。

(3).b主机对文件 A’ 的每个数据块根据其内容都计算两个校验码:32位的弱滚动校验码(rolling checksum)和128位的MD4强校验码(现在版本的rsync使用的已经是128位的MD5强校验码)。并将文件 A’ 计算出的所有rolling checksum和强校验码跟随在对应数据块chunk[N]后形成校验码集合,然后发送给主机a。

也就是说,校验码集合的内容大致如下:其中sum1为rolling checksum,sum2为强校验码。

chunk[0] sum1=3ef2c827 sum2=3efa923f8f2e7
chunk[1] sum1=57ac2aaf sum2=aef2dedba2314
chunk[2] sum1=92d7edb4 sum2=a6sd6a9d67a12
chunk[3] sum1=afe74939 sum2=90a12dfe7485c

需要注意,不同内容的数据块计算出的rolling checksum是有可能相同的,但是概率非常小。

(4).当a主机接收到文件 A’ 的校验码集合后,a 主机将对此校验码集合中的每个rolling checksum计算16位长度的hash值,并将每216个hash值按照hash顺序放入一个hash table中,hash表中的每一个hash条目都指向校验码集合中它所对应的rolling checksum的chunk号码,然后对校验码集合根据hash值进行排序,这样排序后的校验码集合中的顺序就能和hash表中的顺序对应起来。

所以,hash表和排序后的校验码集合对应关系大致如下:假设hash表中的hash值是根据首个字符按照[0-9a-f]的顺序进行排序的。

在这里插入图片描述


同样需要注意,不同rolling checksum计算出的hash值也是有可能会相同的,概率也比较小,但比rolling checksum出现重复的概率要大一些。

(5).随后主机 a 将对文件 A 进行处理。处理的过程是从第1个字节开始取相同大小的数据块,并计算它的校验码和校验码集合中的校验码进行匹配。如果能匹配上校验码集合中的某个数据块条目,则表示该数据块和文件B中数据块相同,它不需要传输,于是主机a直接跳转到该数据块的结尾偏移地址,从此偏移处继续取数据块进行匹配。如果不能匹配校验码集合中的数据块条目,则表示该数据块是非匹配数据块,它需要传输给主机 b,于是主机 a 将跳转到下一个字节,从此字节处继续取数据块进行匹配。注意,匹配成功时跳过的是整个匹配数据块,匹配不成功时跳过的仅是一个字节。可以结合下一小节的示例来理解。

上面说的数据块匹配只是一种描述,具体的匹配行为需要进行细化。rsync算法将数据块匹配过程分为3个层次的搜索匹配过程。

首先,主机 a 会对取得的数据块根据它的内容计算出它的rolling checksum,再根据此rolling checksum计算出hash值。

然后,将此hash值去和hash表中的hash条目进行匹配,**这是第一层次的搜索匹配过程,它比较的是hash值。**如果在hash表中能找到匹配项,则表示该数据块存在潜在相同的可能性,于是进入第二层次的搜索匹配。

**第二层次的搜索匹配是比较rolling checksum。**由于第一层次的hash值匹配到了结果,所以将搜索校验码集合中与此hash值对应的rolling checksum。由于校验码集合是按照hash值排序过的,所以它的顺序和hash表中的顺序一致,也就是说只需从此hash值对应的rolling chcksum开始向下扫描即可。扫描过程中,如果 A 文件数据块的rolling checksum能匹配某项,则表示该数据块存在潜在相同的可能性,于是停止扫描,并进入第三层次的搜索匹配以作最终的确定。或者如果没有扫描到匹配项,则说明该数据块是非匹配块,也将停止扫描,这说明rolling checksum不同,但根据它计算的hash值却发生了小概率重复事件。

第三层次的搜索匹配是比较强校验码。此时将对A文件的数据块新计算一个强校验码(在第三层次之前,只对A文件的数据块计算了rolling checksum和它的hash值),并将此强校验码与校验码集合中对应强校验码匹配,如果能匹配则说明数据块是完全相同的,不能匹配则说明数据块是不同的,然后开始取下一个数据块进行处理。

之所以要额外计算hash值并放入hash表,是因为比较rolling checksum的性能不及hash值比较,且通过hash搜索的算法性能非常高。由于hash值重复的概率足够小,所以对绝大多数内容不同的数据块都能直接通过第一层次搜索的hash值比较出来,即使发生了小概率hash值重复事件,还能迅速定位并比较更小概率重复的rolling checksum。即使不同内容计算的rolling checksum也可能出现重复,但它的重复概率比hash值重复概率更小,所以通过这两个层次的搜索就能比较出几乎所有不同的数据块。假设不同内容的数据块的rolling checksum还是出现了小概率重复,它将进行第三层次的强校验码比较,它采用的是MD4(现在是MD5),这种算法具有"雪崩效应",只要一点点不同,结果都是天翻地覆的不同,所以在现实使用过程中,完全可以假设它能做最终的比较。

数据块大小会影响rsync算法的性能。如果数据块大小太小,则数据块的数量就太多,需要计算和匹配的数据块校验码就太多,性能就差,而且出现hash值重复、rolling checksum重复的可能性也增大;如果数据块大小太大,则可能会出现很多数据块都无法匹配的情况,导致这些数据块都被传输,降低了增量传输的优势。所以划分合适的数据块大小是非常重要的,默认情况下,rsync会根据文件大小自动判断数据块大小,但rsync命令的"-B"(或"–block-size")选项支持手动指定大小,如果手动指定,官方建议大小在500-1000字节之间。

(6).当 a 主机发现是匹配数据块时,将只发送这个匹配块的附加信息给 b 主机。同时,如果两个匹配数据块之间有非匹配数据,则还会发送这些非匹配数据。当 b 主机陆陆续续收到这些数据后,会创建一个临时文件,并通过这些数据重组这个临时文件,使其内容和 A 文件相同。临时文件重组完成后,修改该临时文件的属性信息(如权限、所有者、mtime等),然后重命名该临时文件替换掉 A’ 文件,这样 A’ 文件就和 A 文件保持了同步。

1.3 通过示例分析 rsync 算法

前面说了这么多理论,可能已经看的云里雾里,下面通过 A 和 A’ 文件的示例来详细分析上一小节中的增量传输算法原理,由于上一小节中的过程(1)-(4),已经给出了示例,所以下面将继续分析过程(5)和过程(6)。

先看文件 A’ (内容为"123abcdefg")排序后的校验码集合以及hash表。

在这里插入图片描述


当主机 a 开始处理文件A时,对于文件A的内容"123xxabc def"来说,从第一个字节开始取大小相同的数据块,所以取得的第一个数据块的内容是"123",由于和文件 A’ 的 chunk[0] 内容完全相同,所以α主机对此数据块计算出的rolling checksum值肯定是"3ef2e827",对应的hash值为"e827"。于是 a 主机将此hash值去匹配hash表,匹配过程中发现指向chunk[0]的hash值能匹配上,于是进入第二层次的rolling checksum比较,也即从此hash值指向的chunk[0]的条目处开始向下扫描。扫描过程中发现扫描的第一条信息(即chunk[0]对应的条目)的rolling checksum就能匹配上,所以扫描终止,于是进入第三层次的搜索匹配,这时α主机将对"123"这个数据块新计算一个强校验码,与校验码集合中chunk[0]对应的强校验码做比较,最终发现能匹配上,于是确定了文件A中的"123"数据块是匹配数据块,不需要传输给 b 主机。

虽然匹配数据块不用传输,但匹配的相关信息需要立即传输给 b 主机,否则 b 主机不知道如何重组文件 A 的副本。匹配块需要传输的信息包括:匹配的是 b 文件中的chunk[0]数据块,在文件 A 中偏移该数据块的起始偏移地址为第1个字节,长度为3字节。

数据块"123"的匹配信息传输完成后,a 主机将取第二个数据块进行处理。本来应该是从第2个字节开始取数据块的,但由于数据块"123"中3个字节完全匹配成功,所以可以直接跳过整个数据块"123",即从第4个字节开始取数据块,所以 a 主机取得的第2个数据块内容为"xxa"。同样,需要计算它的rolling checksum和hash值,并搜索匹配hash表中的hash条目,发现没有任何一条hash值可以匹配上,于是立即确定该数据块是非匹配数据块。

此时 a 主机将继续向前取A文件中的第三个数据块进行处理。由于第二个数据块没有匹配上,所以取第三个数据块时只跳过了一个字节的长度,即从第5个字节开始取,取得的数据块内容为"xab"。经过一番计算和匹配,发现这个数据块和第二个数据块一样是无法匹配的数据块。于是继续向前跳过一个字节,即从第6个字节开始取第四个数据块,这次取得的数据块内容为"abc",这个数据块是匹配数据块,所以和第一个数据块的处理方式是一样的,唯一不同的是第一个数据块到第四个数据块,中间两个数据块是非匹配数据块,于是在确定第四个数据块是匹配数据块后,会将中间的非匹配内容(即123xxabc中间的xx)逐字节发送给β主机。

(前文说过,hash值和rolling checksum是有小概率发生重复,出现重复时匹配如何进行?见本小节的尾部)

依此方式处理完A中所有数据块,最终有3个匹配数据块chunk[0]、chunk[1]和chunk[2],以及2段非匹配数据"xx"和" "。这样 b 主机就收到了匹配数据块的匹配信息以及逐字节的非匹配纯数据,这些数据是β主机重组文件A副本的关键信息。它的大致内容如下:

chunk[0] of size 3 at 0 offset=0
data receive 2 at 3
chunk[1] of size 3 at 3 offset=5
data receive 1 at 8
chunk[2] of size 3 at 6 offset=9

为了说明这段信息,首先看文件 A 和文件 A’ 的内容,并标出它们的偏移地址。

在这里插入图片描述

对于"chunk[0] of size 3 at 0 offset=0",这一段表示这是一个匹配数据块,匹配的是文件 A’ 中的chunk[0],数据块大小为3字节,关键词at表示这个匹配块在文件 A’ 中的起始偏移地址为0,关键词offset表示这个匹配块在文件 A 中起始偏移地址也为0,它也可以认为是重组临时文件中的偏移。也就是说,在 b 主机重组文件时,将从文件B的"at 0"偏移处拷贝长度为3字节的chunk[0]对应的数据块,并将这个数据块内容写入到临时文件中的offset=0偏移处,这样临时文件中就有了第一段数据"123"。

对于"data receive 2 at 3",这一段表示这是接收的纯数据信息,不是匹配数据块。2表示接收的数据字节数,“at 3"表示在临时文件的起始偏移3处写入这两个字节的数据。这样临时文件就有了包含了数据"123xx”。

对于"chunk[1] of size 3 at 3 offset=5",这一段表示是匹配数据块,表示从文件 A’ 的起始偏移地址at=3处拷贝长度为3字节的chunk[1]对应的数据块,并将此数据块内容写入在临时文件的起始偏移offset=5处,这样临时文件就有了"123xxabc"。

对于"data receive 1 at 8",这一说明接收了纯数据信息,表示将接收到的1个字节的数据写入到临时文件的起始偏移地址8处,所以临时文件中就有了"123xxabc "。

最后一段"chunk[2] of size 3 at 6 offset=9",表示从文件 A’ 的起始偏移地址at=6处拷贝长度为3字节的chunk[2]对应的数据块,并将此数据块内容写入到临时文件的起始偏移offset=9处,这样临时文件就包含了"123xxabc def"。到此为止,临时文件就重组结束了,它的内容和 a 主机上A文件的内容是完全一致的,然后只需将此临时文件的属性修改一番,并重命名替换掉文件 A’ 即可,这样就将文件B和文件A进行了同步。

整个过程如下图:
在这里插入图片描述

需要注意的是,a 主机不是搜索完所有数据块之后才将相关数据发送给 b 主机的,而是每搜索出一个匹配数据块,就会立即将匹配块的相关信息以及当前匹配块和上一个匹配块中间的非匹配数据发送给β主机,并开始处理下一个数据块,当β主机每收到一段数据后会立即将将其重组到临时文件中。因此,a 主机和 b 主机都尽量做到不浪费任何资源。

1.3.1 hash 值和 rolling checksum 重复时的匹配过程

在上面的示例分析中,没有涉及hash值重复和rolling checksum 重复的情况,但它们有可能会重复,虽然重复后的匹配过程是一样的,但可能不那么容易理解。
还是看 A’ 文件排序后的校验码集合。
在这里插入图片描述

当文件 A 处理数据块时,假设处理的是第2个数据块,它是非匹配数据块,对此数据块会计算rolling checksum和 hash值。假设此数据块的hash值从hash表中匹配成功,例如匹配到了上图中"4939"这个值,于是会将此第二个数据块的rolling checksum与hash值"4939"所指向的chunk[3]的rolling checksum作比较,hash值重复且rolling checksum重复的可能性几乎趋近于0,所以就能确定此数据块是非匹配数据块。

考虑一种极端情况,假如文件 A’ 比较大,划分的数据块数量也比较多,那么B文件自身包含的数据块的rolling checksum就有可能会出现重复事件,且hash值也可能会出现重复事件。

例如chunk[0]和chunk[3]的rolling checksum不同,但根据rolling checksum计算的hash值却相同,此时hash表和校验码集合的对应关系大致如下:
在这里插入图片描述

如果文件 A 中正好有数据块的hash值能匹配到"c827",于是准备比较rolling checksum,此时将从hash值"c827"指向的chunk[0]向下扫描校验码集合。当扫描过程中发现数据块的rolling checksum正好能匹配到某个rolling checksum,如chunk[0]或chunk[3]对应的rolling checksum,则扫描终止,并进入第三层次的搜索匹配。如果向下扫描的过程中发现直到chunk[2]都没有找到能匹配的rolling checksum,则扫描终止,因为chunk[2]的hash值和数据块的hash值已经不同,最终确定该数据块是非匹配数据块,于是 a 主机继续向前处理下一个数据块。
在这里插入图片描述

假如文件B中数据块的rolling checksum出现了重复(这只说明一件事,你太幸运),将只能通过强校验码来匹配。

1.4 rsync工作流程分析

上面已经把rsync增量传输的核心分析过了,下面将分析rsync对增量传输算法的实现方式以及rsync传输的整个过程。在这之前,有必要先解释下rsync传输过程中设计的client/server、sender、receiver、generator 等相关概念。

1.4.1 几个进程和术语

rsync有3种工作方式。一是本地传输方式,二是使用远程shell连接方式,三是使用网络套接字连接rsync daemon模式。
使用远程shell如ssh连接方式时,本地敲下rsync命令后,将请求和远程主机建立远程shell连接如ssh连接,连接建立成功后,在远程主机上将fork远程shell进程调用远程rsync程序,并将rsync所需的选项通过远程shell命令如ssh传递给远程rsync。这样两端就都启动了rsync,之后它们将通过管道的方式(即使它们之间时本地和远程的关系)进行通信。
使用网络套接字连接rsync daemon时,当通过网络套接字和远程已运行好的rsync建立连接时,rsync daemon进程会创建一个子进程来响应该连接并负责后续该连接的所有通信。这样两端也都启动了连接所需的rsync,此后通信方式是通过网络套接字来完成的。

本地传输其实是一种特殊的工作方式,首先rsync命令执行时,会有一个rsync进程,然后根据此进程fork另一个rsync进程作为连接的对端,连接建立之后,后续所有的通信将采用管道的方式。

无论使用何种连接方式,发起连接的一端被称为client,也即执行rsync命令的一段,连接的另一端称为server端。注意,server端不代表rsync daemon端。server端在rsync中是一个通用术语,是相对client端而言的,只要不是client端,都属于server端,它可以是本地端,也可以是远程shell的对端,还可以是远程rsync daemon端,这和大多数daemon类服务的server端不同。

rsync的client和server端的概念存活周期很短,当client端和server端都启动好rsync进程并建立好了rsync连接(管道、网络套接字)后,将使用sender端和receiver端来代替client端和server端的概念。sender端为文件发送端,receiver端为文件接收端。

当两端的rsync连接建立后,sender端的rsync进程称为sender进程,该进程负责sender端所有的工作。receiver端的rsync进程称为receiver进程,负责接收sender端发送的数据,以及完成文件重组的工作。receiver端还有一个核心进程——generator进程,该进程负责在receiver端执行"–delete"动作、比较文件大小和mtime以决定文件是否跳过、对每个文件划分数据块、计算校验码以及生成校验码集合,然后将校验码集合发送给sender端。
rsync的整个传输过程由这3个进程完成,它们是高度流水线化的,generator 进程的输出结果作为sender端的输入,sender端的输出结果作为receiver端的输入。即:

generator进程 --》 sender进程 --》 receiver进程

虽然这3个进程是流水线式的,但不意味着它们存在数据等待的延迟,它们是完全独立、并行工作的。generator计算出一个文件的校验码集合发送给sender,会立即计算下一个文件的校验码集合,而sender进程一收到generator的校验码集合会立即开始处理该文件,处理文件时每遇到一个匹配块都会立即将这部分相关数据发送给receiver进程,然后立即处理下一个数据块,而receiver进程收到sender发送的数据后,会立即开始重组工作。也就是说,只要进程被创建了,这3个进程之间是不会互相等待的。

另外,流水线方式也不意味着进程之间不会通信,只是说rsync传输过程的主要工作流程是流水线式的。例如receiver进程收到文件列表后就将文件列表交给generator进程。

1.4.2 rsync整个工作流程

假设在 a 主机上执行rsync命令,将一大堆文件推送到 b 主机上。

1 首先client和server建立rsync通信的连接,远程shell 连接方式建立的是管道,连接rsync daemon时建立的是网络套接字。

2 rsync连接建立后,sender端的sender进程根据rsync命令行中给出的源文件收集待同步文件,将这些文件放入文件列表(file list)中并传输给 b 主机。在创建文件列表的过程中,有几点需要说明:

  • 创建文件列表时,会先按照目录进行排序,然后对排序后的文件列表中的文件进行编号,以后将直接使用文件编号来引用文件。

  • 文件列表中还包含文件的一些属性信息,包括:权限mode,文件大小len,所有者和所属组uid/gid,最近修改时间mtime等,当然,有些信息是需要指定选项后才附带的,例如不指定"-o"和"-g"选项将不包含uid/gid,指定"–checksum"公还将包含文件级的checksum值。

  • 发送文件列表时不是收集完成后一次性发送的,而是按照顺序收集一个目录就发送一个目录,同理receiver接收时也是一个目录一个目录接收的,且接收到的文件列表是已经排过序的。

  • 如果rsync命令中指定了exclude或hide规则,则被这些规则筛选出的文件会在文件列表中标记为hide(exclude规则的本质也是hide)。带有hide标志的文件对receiver是不可见的,所以receiver端会认为sender端没有这些被hide的文件。

3 receiver端从一开始接收到文件列表中的内容后,立即根据receiver 进程fork出generator进程。generator进程将根据文件列表扫描本地目录树,如果目标路径下文件已存在,则此文件成为basis file。

generator 的工作总体上分为3步:

  • 如果rsync 命令中制定了“–delete”选项,则首先在b主机上执行删除动作,删除源路径下没有,但目标路径下有的文件。
  • 然后根据file list中的文件顺序,逐个与本地对应文件的文件大小和mtime做比较。如果发现本地文件的大小或mtime与file list中的相同,则表示该文件不需要传输,将直接跳过该文件。
  • 如果发现本地文件的大小或mtime不同,则表示该文件是需要传输的文件,generator将立即对此文件划分数据库并编号,并对每个数据库计算弱滚动校验码(rolling checksum)和强校验码,并将这些校验码跟随数据块编号组合在一起形成校验码集合,然后将此文件的编号和校验码集合一起发送给sender端。发送完毕后开始处理file list 中的下一个文件。

需要注意,a主机上有而b主机上没有的文件,generator会将文件列表中的此文件的校验码设置为空发送给sender。如果指定了 “–whole-file”选项,则generator 会将file list 中的所有文件的校验码都设置为空,这样使得rsync强制采用全量传输功能,而不再使用增量传输功能。

从下面的步骤4开始,这些步骤在前文分析rsync算法原理时已经给出了非常详细的解释,所以此处仅概括性地描述,如有不解之处,请翻到前文查看相关内容。

4 sender进程收到generator发送的数据,会读取文件编号和校验码集合。然后根据校验码集合中的弱滚动校验码(rolling checksum)计算hash值,并将hash值放入hash表中,且对校验码集合按照hash值进行排序,这样校验码集合和hash表的顺序就能完全相同。

5 sender进程对校验码集合排序完成后,根据读取到的文件编号处理本地对应的文件。处理的目的是找出能匹配的数据块(即内容完全相同的数据块)以及非匹配的数据。每当找到匹配的数据块时,都将立即发送一些匹配信息给receiver进程。当发送完文件的所有数据后,sender进程还将对此文件生成一个文件级的 whole-file校验码 给receiver。

6 receiver进程接收到sender发送的指令和数据后,立即在目标路径下创建一个临时文件,并按照接收到的数据和指令重组该临时文件,目的是使该文件和 a 主机上的文件完全一致。重组过程中,能匹配的数据块将从basis file中copy并写入到临时文件,非匹配的数据则是接收自sender端。

7 临时文件重组完成后,将对此临时文件生成文件级的校验码,并与sender端发送的 whole-file 校验码 比较,如果能匹配成功则表示此临时文件和源文件是完全相同的,也就表示临时文件重组成功,如果校验码匹配失败,则表示重组过程中可能出错,将完全从头开始处理此源文件。

8 当临时文件重组成功后,receiver进程将修改该临时文件的属性信息,包括权限、所有者、所属组、mtime等。最后将此文件重命名并覆盖掉目标路径下已存在的文件(即basis file)。至此,文件同步完成。

1.5 根据执行过程分析 rsync 工作原理

为了更加直观地感受上文所解释的rsync算法原理和工作流程,下面将给出两个rsync执行过程的示例,并分析工作流程,一个是全量传输的示例,一个是增量传输的示例。
要查看 rsync 执行的过程,执行在rsync命令行中加上 “-vvvv” 选项即可。

1.5.1 全量传输执行过程分析

要执行的命令:

rsync -a -vvvv /etc/cron.d/ /var/log/puppet /etc/issue ci-user@10.176.6.24:/home/ci-user/tmp

目的是将 /etc/cron.d 目录、/var/log/puppet 目录和 /etc/issue 文件传输到10.176.6.24 主机上的/tmp目录下,由于/tmp目录下不存在这些文件,所以整个过程是全量传输的过程。但其本质仍然是采用增量传输的算法,只不过generator发送的校验码集合全为空而已。
以下是 /etc/cron.d 和 /var/log/puppet 目录的层次结构

[root@clickhouse-backup-test01 ~]# tree -C /etc/cron.d/  /var/log/puppet
/etc/cron.d/
|-- 0hourly
|-- hardening_audit
|-- infosec_ntpstat
|-- puppet-cron
|-- puppet-reboot
`-- sysstat
/var/log/puppet
`-- puppet-agent.log

0 directories, 7 files

以下是执行过程

[root@dev8 ~]# rsync -a -vvvv /etc/cron.d/ /var/log/puppet /etc/issue ci-user@10.176.6.24:/home/ci-user/tmp
# 使用ssh(ssh为默认的远程shell)执行远程 rsync 命令建立连接 
cmd=<NULL> machine=10.176.6.24 user=ci-user path=/home/ci-user/tmp
cmd[0]=ssh cmd[1]=-l cmd[2]=ci-user cmd[3]=10.176.6.24 cmd[4]=rsync cmd[5]=--server cmd[6]=-vvvvlogDtpre.iLsfxC cmd[7]=. cmd[8]=/home/ci-user/tmp
opening connection using: ssh -l ci-user 10.176.6.24 rsync --server -vvvvlogDtpre.iLsfxC . /home/ci-user/tmp  (9 args)
msg checking charset: ANSI_X3.4-1968
*******************************************************************************
                             NOTICE:

This system/network is for the use of authorized users for legitimate
business purposes only. The unauthorized access, use or modification of
this system or of the data contained therein or in transit to/from it
is a criminal violation of federal and state laws.

All individuals using this computer system are subject to having their
activities on this system monitored and recorded by systems personnel.
Anyone using this system expressly consents to such monitoring. Any
evidence of suspected criminal activity revealed by such monitoring may
be provided to law enforcement officials by systems personnel.
*******************************************************************************

Password:
# 双方互相发送协议版本号,并协商使用两者版本较低版本,我们这里是 rsync  version 3.1.2  protocol version 31
(Server) Protocol versions: remote=31, negotiated=31
(Client) Protocol versions: remote=31, negotiated=31

# sender 端生成文件列表并发送给 receiver 端
sending incremental file list
[sender] make_file(.,*,0)      # 第一个要传输的文件目录: .   源路径 /etc/cron.d/ 下, 要注意 /etc/cron.d/ 和 /var/log/puppet 的区别
[sender] pushing local filters for /etc/cron.d/
[sender] make_file(0hourly,*,2)    # 第二个要传输的文件目录:0hourly 
[sender] make_file(sysstat,*,2)    # 第三个要传输的文件目录:sysstat
[sender] make_file(puppet,*,0)     # 第四个要传输的文件目录:puppet文件,注意,此处 puppet 是待传输的文件,而不认为是目录
[sender] make_file(issue,*,0)      # 第五个要传输的文件目录:issue文件

# 指明从文件列表的第1项开始,并确定这次要传输给 receiver 的项共有 5 个
[sender] flist start=1, used=5, low=0, high=4
# 为这 5 项生成列表信息,包括此文件 id,所在目录,权限模式,长度, uid/gid, 最后还有一个修饰符
[sender] i=1 /etc/cron.d ./ mode=040500 len=36 uid=0 gid=0 flags=1005
[sender] i=2 /etc/cron.d 0hourly mode=0100644 len=128 uid=0 gid=0 flags=0
[sender] i=3 /etc issue mode=0100644 len=805 uid=0 gid=0 flags=1005
[sender] i=4 /etc/cron.d sysstat mode=0100600 len=233 uid=0 gid=0 flags=1000
[sender] i=5 /var/log puppet/ mode=040755 len=30 uid=0 gid=0 flags=1005
send_file_list done
file list sent
# 唯一需要注意的是文件所在目录,例如/var/log puppet/,但实际在命令行中指定的是/var/log/puppet。
# 此处信息中 log 和 puppet 使用空格分开了,这个空格非常关键。空格左边的表示隐含目录(见man rsync的"-R"选项),
# 右边的是待传输的整个文件或目录,默认情况下将会在receiver端生成puppet/目录,但左边隐含目录则不会创建。
# 但可以通过指定特殊选项(如"-R"),让rsync也能在receiver端同时创建隐含目录,以便创建整个目录层次结构。
# 举个例子,如果A主机的/a目录下有b、c等众多子目录,并且b目录中有d文件,现在只想传输/a/b/d并保留/a/b的目录层次结构,
# 那么可以通过特殊选项让此处的文件所在目录变为"/ a/",关于具体的实现方法,见"rsync -R选项示例"。
 
############ sender端发送文件属性信息 #####################
# 由于前面的文件列表中有两个条目是目录,因此还要为目录中的每个文件生成属性信息并发送给receiver端
send_files starting
[sender] pushing local filters for /var/log/puppet/
[sender] make_file(puppet/puppet-agent.log,*,2)
[sender] flist start=7, used=1, low=0, high=0
[sender] i=7 /var/log puppet/puppet-agent.log mode=0100644 len=891 uid=52 gid=52 flags=1000
[sender] flist_eof=1

############## server端相关活动内容 ################
# 首先在server端启动rsync进程
server_recv(2) starting pid=24070
process has 2 gids:  991 1010
gid 0() maps to 0
recv_file_name(.)
recv_file_name(0hourly)
recv_file_name(sysstat)
recv_file_name(puppet)
recv_file_name(issue)
received 5 names
[Receiver] flist start=1, used=5, low=0, high=4
[Receiver] i=1 0 ./ mode=040500 len=36 gid=(0) flags=1405
[Receiver] i=2 1 0hourly mode=0100644 len=128 gid=(0) flags=400
[Receiver] i=3 1 issue mode=0100644 len=805 gid=(0) flags=1400
[Receiver] i=4 1 sysstat mode=0100600 len=233 gid=(0) flags=1400
[Receiver] i=5 1 puppet/ mode=040755 len=30 gid=(0) flags=1405
recv_file_list done
# 第一次接收数据完成
# 在receiver 端启动 generator 进程
get_local_name count=5 /home/ci-user/tmp  # 获取本地路径名
generator starting pid=24070  # 启动 generator 进程
delta-transmission enabled    # 启用增量传输算法
# generator 进程设置完毕

# 一共接收到 5 个文件
recv_generator(.,0)
set modtime of . to (1567013864) Wed Aug 28 10:37:44 2019
recv_generator(.,1) 
recv_generator(0hourly,2)
recv_generator(issue,3)
recv_generator(sysstat,4)
recv_generator(puppet,5)
recv_files(5) starting
send_files(0, /etc/cron.d/.)   # generator 收到 receiver 进程通知的文件 id=1 文件  .
./
send_files(2, /etc/cron.d/0hourly)
count=0 n=0 rem=0     #   此项为目标主机上的文件 0hourly 分割的数据库信息,count 表示数量, n 表示数据块的固定大小, rem 是remain 的意思,表示剩余的数据长度,也即是最好一个数据块的大小,
                      #   此处因为目标端不存在 0hourly 文件,因此全部设置为0
send_files mapped /etc/cron.d/0hourly of size 128  # sender 端映射 /etc/cron.d/0hourly 文件,使得 sender 可以获取到该文件的相关内容
calling match_sums /etc/cron.d/0hourly   # sender 端调用校验码匹配功能
0hourly
sending file_sum                         # 匹配结束后,再发送文件级的 checksum 给 receiver 端
false_alarms=0 hash_hits=0 matches=0     # 输出数据块匹配的相关统计信息
sender finished /etc/cron.d/0hourly  
# 文件 /etc/cron.d/0hourly 发送完毕,因为目标上不存在 0hourly 文件,所以整个过程非常简单,直接传输 0hourly 中的全部数据即可

# 发送文件 /etc/issue
send_files(3, /etc/issue)
count=0 n=0 rem=0
send_files mapped /etc/issue of size 805
calling match_sums /etc/issue
issue
sending file_sum
false_alarms=0 hash_hits=0 matches=0
sender finished /etc/issue     

# 发送文件 /etc/crond.d/sysstat
send_files(4, /etc/cron.d/sysstat)
count=0 n=0 rem=0
send_files mapped /etc/cron.d/sysstat of size 233
calling match_sums /etc/cron.d/sysstat
sysstat
sending file_sum
false_alarms=0 hash_hits=0 matches=0
sender finished /etc/cron.d/sysstat

# 接收文件 /puppet/puppet-agent.log
[receiver] receiving flist for dir 1
uid 52(puppet) maps to 52
gid 52(puppet) maps to 52
recv_file_name(puppet/puppet-agent.log)
received 1 names
[receiver] flist start=7, used=1, low=0, high=0
[receiver] i=7 2 puppet/puppet-agent.log mode=0100644 len=891 gid=(52) flags=1400
recv_file_list done
[receiver] flist_eof=1
recv_files(.)
recv_files(0hourly)
data recv 128 at 0
got file_sum
set modtime of .0hourly.vGJ3Rk to (1560075748) Sun Jun  9 03:22:28 2019
renaming .0hourly.vGJ3Rk to 0hourly
recv_files(issue)
data recv 805 at 0
got file_sum
set modtime of .issue.RTRDTB to (1566935887) Tue Aug 27 12:58:07 2019
renaming .issue.RTRDTB to issue
recv_files(sysstat)
data recv 233 at 0
got file_sum
set modtime of .sysstat.pEYfVS to (1567013864) Wed Aug 28 10:37:44 2019
renaming .sysstat.pEYfVS to sysstat

# generator 开始处理文件
[generator] receiving flist for dir 1
uid 52(puppet) maps to 52
gid 52(puppet) maps to 52
recv_file_name(puppet/puppet-agent.log)
received 1 names
[generator] flist start=7, used=1, low=0, high=0
[generator] i=7 2 puppet/puppet-agent.log mode=0100644 len=891 gid=(52) flags=1400
recv_file_list done
recv_generator(puppet,6)
set modtime of puppet to (1568192553) Wed Sep 11 02:02:33 2019
recv_generator(puppet/puppet-agent.log,7)
touch_up_dirs: . (0)
set modtime of . to (1567013864) Wed Aug 28 10:37:44 2019
[generator] flist_eof=1
generate_files phase=1
send_files(6, /var/log/puppet)
puppet/
send_files(7, /var/log/puppet/puppet-agent.log)
count=0 n=0 rem=0
send_files mapped /var/log/puppet/puppet-agent.log of size 891
calling match_sums /var/log/puppet/puppet-agent.log
puppet/puppet-agent.log
sending file_sum
false_alarms=0 hash_hits=0 matches=0
sender finished /var/log/puppet/puppet-agent.log
recv_files(puppet)
recv_files(puppet/puppet-agent.log)
data recv 891 at 0
got file_sum
set modtime of puppet/.puppet-agent.log.JN0aX9 to (1568192556) Wed Sep 11 02:02:36 2019
renaming puppet/.puppet-agent.log.JN0aX9 to puppet/puppet-agent.log
touch_up_dirs: puppet (1)
set modtime of puppet to (1568192553) Wed Sep 11 02:02:33 2019


send_files phase=1
recv_files phase=1
generate_files phase=2
send_files phase=2
send files finished # sender 进程消失,并输出匹配的统计信息以及传输的总的纯数据量
total: matches=0  hash_hits=0  false_alarms=0 data=2057
recv_files phase=2
recv_files finished
generate_files phase=3
generate_files finished
client_run waiting on 24985

sent 2,440 bytes  received 3,055 bytes  845.38 bytes/sec
total size is 2,057  speedup is 0.37
[sender] _exit_cleanup(code=0, file=main.c, line=1179): entered
[sender] _exit_cleanup(code=0, file=main.c, line=1179): about to call exit(0)
[root@dev8 ~]#

1.5.2 增量传输执行过程分析

要执行的命令为:

[yuatang@dev8 a]$ rsync -a -vvvv /mnt/disks/2/a/ ci-user@10.176.6.24:/data01/dev8-ck-backup/a
# 使用 ssh(ssh为默认的远程shell)执行远程rsync命令建立连接
cmd=<NULL> machine=10.176.6.24 user=ci-user path=/data01/dev8-ck-backup/a
cmd[0]=ssh cmd[1]=-l cmd[2]=ci-user cmd[3]=10.176.6.24 cmd[4]=rsync cmd[5]=--server cmd[6]=-vvvvlogDtpre.iLsfxC cmd[7]=. cmd[8]=/data01/dev8-ck-backup/a
opening connection using: ssh -l ci-user 10.176.6.24 rsync --server -vvvvlogDtpre.iLsfxC . /data01/dev8-ck-backup/a  (9 args)
msg checking charset: ANSI_X3.4-1968
The authenticity of host '10.176.6.24 (10.176.6.24)' can't be established.
ECDSA key fingerprint is SHA256:8gCByzMuPJc6E3bieuGbsqXT3TeGnd2M5+yRwaW8oEs.
ECDSA key fingerprint is MD5:05:a1:11:0e:1b:cb:6b:64:fe:ec:7f:56:4b:3e:87:2c.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.176.6.24' (ECDSA) to the list of known hosts.
*******************************************************************************
                             NOTICE:

This system/network is for the use of authorized users for legitimate
business purposes only. The unauthorized access, use or modification of
this system or of the data contained therein or in transit to/from it
is a criminal violation of federal and state laws.

All individuals using this computer system are subject to having their
activities on this system monitored and recorded by systems personnel.
Anyone using this system expressly consents to such monitoring. Any
evidence of suspected criminal activity revealed by such monitoring may
be provided to law enforcement officials by systems personnel.
*******************************************************************************

Password:
# 双方互相发送协议版本号,并协商使用两者较低版本
(Server) Protocol versions: remote=31, negotiated=31
(Client) Protocol versions: remote=31, negotiated=31
sending incremental file list
# 5个增量文件需要发送
[sender] make_file(.,*,0)
[sender] pushing local filters for /mnt/disks/2/a/
[sender] make_file(paypal,*,2)
[sender] make_file(c.txt,*,2)
[sender] make_file(b,*,2)
[sender] make_file(guangzhou.txt,*,2)
# # 指明从文件列表的第1项开始,并确定这次要传输给receiver的项共有5个,4表示啥意思?
[sender] flist start=1, used=5, low=0, high=4
[sender] i=1 /mnt/disks/2/a ./ mode=040755 len=4,096 uid=999 gid=999 flags=1005
[sender] i=2 /mnt/disks/2/a c.txt mode=0100644 len=0 uid=999 gid=999 flags=1000
[sender] i=3 /mnt/disks/2/a guangzhou.txt mode=0100644 len=84 uid=999 gid=999 flags=1000
[sender] i=4 /mnt/disks/2/a b/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
[sender] i=5 /mnt/disks/2/a paypal/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
send_file_list done
# 至此把需要传输的文件名存入list中发送完毕
file list sent

# 开始发送文件内容相关信息
send_files starting
[sender] pushing local filters for /mnt/disks/2/a/b/
[sender] make_file(b/c,*,2)
[sender] make_file(b/ccc.txt,*,2)
[sender] flist start=7, used=2, low=0, high=1
[sender] i=7 /mnt/disks/2/a b/ccc.txt mode=0100644 len=0 uid=999 gid=999 flags=1000
[sender] i=8 /mnt/disks/2/a b/c/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
[sender] pushing local filters for /mnt/disks/2/a/b/c/
[sender] make_file(b/c/e,*,2)
[sender] flist start=10, used=1, low=0, high=0
[sender] i=10 /mnt/disks/2/a b/c/e/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
[sender] pushing local filters for /mnt/disks/2/a/b/c/e/
[sender] make_file(b/c/e/f,*,2)
[sender] flist start=12, used=1, low=0, high=0
[sender] i=12 /mnt/disks/2/a b/c/e/f/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
[sender] pushing local filters for /mnt/disks/2/a/b/c/e/f/
[sender] make_file(b/c/e/f/shanghai.txt,*,2)
[sender] flist start=14, used=1, low=0, high=0
[sender] i=14 /mnt/disks/2/a b/c/e/f/shanghai.txt mode=0100644 len=22 uid=999 gid=999 flags=1000
[sender] pushing local filters for /mnt/disks/2/a/paypal/
[sender] make_file(paypal/ceshi.txt,*,2)
[sender] make_file(paypal/a,*,2)
[sender] flist start=16, used=2, low=0, high=1
[sender] i=16 /mnt/disks/2/a paypal/ceshi.txt mode=0100644 len=227 uid=999 gid=999 flags=1000
[sender] i=17 /mnt/disks/2/a paypal/a/ mode=040755 len=4,096 uid=999 gid=999 flags=1004
[sender] pushing local filters for /mnt/disks/2/a/paypal/a/
[sender] make_file(paypal/a/a.txt,*,2)
[sender] flist start=19, used=1, low=0, high=0
[sender] i=19 /mnt/disks/2/a paypal/a/a.txt mode=0100644 len=0 uid=999 gid=999 flags=1000
[sender] flist_eof=1

# 在 receiver端启动 generator 进程
server_recv(2) starting pid=5337
uid 999(polkitd) maps to 999
process has 2 gids:  991 1010
gid 999(input) maps to 999
recv_file_name(.)
recv_file_name(paypal)
recv_file_name(c.txt)
recv_file_name(b)
recv_file_name(guangzhou.txt)
received 5 names
[Receiver] flist start=1, used=5, low=0, high=4
[Receiver] i=1 0 ./ mode=040755 len=4,096 gid=(999) flags=1405
[Receiver] i=2 1 c.txt mode=0100644 len=0 gid=(999) flags=1400
[Receiver] i=3 1 guangzhou.txt mode=0100644 len=84 gid=(999) flags=1400
[Receiver] i=4 1 b/ mode=040755 len=4,096 gid=(999) flags=1404
[Receiver] i=5 1 paypal/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
# 第一次接收数据完成

get_local_name count=5 /data01/dev8-ck-backup/a # 获取本地路径名
# generator 进程启动
generator starting pid=5337
# 启用增量传输算法
delta-transmission enabled
# 以上 generator 进程设置完毕

# 首先处理接收到的普通文件
recv_generator(.,0)
set modtime of . to (1631167647) Wed Sep  8 23:07:27 2021
# generator 收到 receiver 进程通知的文件 id=1 的文件目录 .
recv_generator(.,1)
recv_generator(c.txt,2)
c.txt is uptodate  # c.txt 文件已经是最新的
recv_generator(guangzhou.txt,3)
gen mapped guangzhou.txt of size 75
generating and sending sums for 3
count=1 rem=75 blength=700 s2length=2 flength=75
chunk[0] offset=0 len=75 sum1=e6d21c1e
recv_generator(b,4)
recv_generator(paypal,5)
send_files(0, /mnt/disks/2/a/.)
./
send_files(2, /mnt/disks/2/a/c.txt)
send_files(3, /mnt/disks/2/a/guangzhou.txt)
# 此项为目标主机上的文件 /mnt/disks/2/a/guangzhou.txt 分割的数据块的信息,count表示数量,n表示数据块的固定大小(700),rem表示剩余的数据长度,也即是最后一个数据块的大小
count=1 n=700 rem=75
# chunk[0]表示第一个数据块,长度为75, offset=0表示
chunk[0] len=75 offset=0 sum1=e6d21c1e
send_files mapped /mnt/disks/2/a/guangzhou.txt of size 84
calling match_sums /mnt/disks/2/a/guangzhou.txt  # 调用校验码匹配功能
guangzhou.txt
built hash table   # 构建 hash 表
hash search b=700 len=84
sum=ca581e7a k=84
hash search s->blength=700 len=84 count=1
done hash search
sending file_sum  # 数据块匹配结束后,再发送文件级别的 checksum 给 receiver 端
false_alarms=0 hash_hits=0 matches=0  # 输出匹配过程中的统计信息
sender finished /mnt/disks/2/a/guangzhou.txt  # guangzhou.txt 文件传输完成

recv_files(5) starting
[receiver] receiving flist for dir 1
recv_file_name(b/c)
recv_file_name(b/ccc.txt)
received 2 names
[receiver] flist start=7, used=2, low=0, high=1
[receiver] i=7 2 b/ccc.txt mode=0100644 len=0 gid=(999) flags=1400
[receiver] i=8 2 b/c/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
# b目录下的文件信息接收完毕

[receiver] receiving flist for dir 3
recv_file_name(b/c/e)
received 1 names
[receiver] flist start=10, used=1, low=0, high=0
[receiver] i=10 3 b/c/e/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
# b/c/e/文件目录信息接收完毕 

[receiver] receiving flist for dir 4
recv_file_name(b/c/e/f)
received 1 names
[receiver] flist start=12, used=1, low=0, high=0
[receiver] i=12 4 b/c/e/f/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
# b/c/e/f 文件目录信息接收完毕 

[receiver] receiving flist for dir 5
recv_file_name(b/c/e/f/shanghai.txt)
received 1 names
[receiver] flist start=14, used=1, low=0, high=0
[receiver] i=14 5 b/c/e/f/shanghai.txt mode=0100644 len=22 gid=(999) flags=1400
recv_file_list done
# /b/c/e/f/shanghai.txt 文件信息接收完毕

[receiver] receiving flist for dir 2
recv_file_name(paypal/ceshi.txt)
recv_file_name(paypal/a)
received 2 names
[receiver] flist start=16, used=2, low=0, high=1
[receiver] i=16 2 paypal/ceshi.txt mode=0100644 len=227 gid=(999) flags=1400
[receiver] i=17 2 paypal/a/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
[receiver] receiving flist for dir 6
recv_file_name(paypal/a/a.txt)
received 1 names
[receiver] flist start=19, used=1, low=0, high=0
[receiver] i=19 3 paypal/a/a.txt mode=0100644 len=0 gid=(999) flags=1400
recv_file_list done
[receiver] flist_eof=1

# generator进程开始对b/c b/ccc.txt进行校验码比对处理
[generator] receiving flist for dir 1
recv_file_name(b/c)
recv_file_name(b/ccc.txt)
received 2 names
[generator] flist start=7, used=2, low=0, high=1
[generator] i=7 2 b/ccc.txt mode=0100644 len=0 gid=(999) flags=1400
[generator] i=8 2 b/c/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
recv_generator(b,6)
recv_generator(b/ccc.txt,7)
b/ccc.txt is uptodate
recv_generator(b/c,8)
[generator] receiving flist for dir 3
recv_file_name(b/c/e)
received 1 names
[generator] flist start=10, used=1, low=0, high=0
[generator] i=10 3 b/c/e/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
recv_generator(b/c,9)
recv_generator(b/c/e,10)
[generator] receiving flist for dir 4
recv_file_name(b/c/e/f)
received 1 names
[generator] flist start=12, used=1, low=0, high=0
[generator] i=12 4 b/c/e/f/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
recv_generator(b/c/e,11)
recv_generator(b/c/e/f,12)
[generator] receiving flist for dir 5
recv_file_name(b/c/e/f/shanghai.txt)
received 1 names
[generator] flist start=14, used=1, low=0, high=0
[generator] i=14 5 b/c/e/f/shanghai.txt mode=0100644 len=22 gid=(999) flags=1400
recv_file_list done
recv_generator(b/c/e/f,13)
recv_generator(b/c/e/f/shanghai.txt,14)
b/c/e/f/shanghai.txt is uptodate
# 由于 /b/c/e/f/shanghai.txt 更新处理完成

# generator进程 开始处理 文件列表 paypal/ceshi.txt paypal/a
[generator] receiving flist for dir 2
recv_file_name(paypal/ceshi.txt)
recv_file_name(paypal/a)
received 2 names
[generator] flist start=16, used=2, low=0, high=1
[generator] i=16 2 paypal/ceshi.txt mode=0100644 len=227 gid=(999) flags=1400
[generator] i=17 2 paypal/a/ mode=040755 len=4,096 gid=(999) flags=1404
recv_file_list done
recv_generator(paypal,15)
set modtime of paypal to (1631167681) Wed Sep  8 23:08:01 2021
recv_generator(paypal/ceshi.txt,16)
gen mapped paypal/ceshi.txt of size 118
generating and sending sums for 16
count=1 rem=118 blength=700 s2length=2 flength=118
chunk[0] offset=0 len=118 sum1=30e828ec
recv_generator(paypal/a,17)
[generator] receiving flist for dir 6
recv_file_name(paypal/a/a.txt)
received 1 names
[generator] flist start=19, used=1, low=0, high=0
[generator] i=19 3 paypal/a/a.txt mode=0100644 len=0 gid=(999) flags=1400
recv_file_list done
recv_generator(paypal/a,18)
recv_generator(paypal/a/a.txt,19)
paypal/a/a.txt is uptodate
# 由于 paypal/a/a.txt 没有变动,已是最新的
[generator] flist_eof=1
generate_files phase=1
send_files(6, /mnt/disks/2/a/b)
send_files(7, /mnt/disks/2/a/b/ccc.txt)
send_files(9, /mnt/disks/2/a/b/c)
send_files(11, /mnt/disks/2/a/b/c/e)
send_files(13, /mnt/disks/2/a/b/c/e/f)
send_files(14, /mnt/disks/2/a/b/c/e/f/shanghai.txt)
send_files(15, /mnt/disks/2/a/paypal)
paypal/
send_files(16, /mnt/disks/2/a/paypal/ceshi.txt)
count=1 n=700 rem=118
chunk[0] len=118 offset=0 sum1=30e828ec
send_files mapped /mnt/disks/2/a/paypal/ceshi.txt of size 227
calling match_sums /mnt/disks/2/a/paypal/ceshi.txt
paypal/ceshi.txt
built hash table
hash search b=700 len=227
sum=f23d3e41 k=227
hash search s->blength=700 len=227 count=1
done hash search
sending file_sum
false_alarms=0 hash_hits=0 matches=0
sender finished /mnt/disks/2/a/paypal/ceshi.txt
send_files(18, /mnt/disks/2/a/paypal/a)
send_files(19, /mnt/disks/2/a/paypal/a/a.txt)
recv_files(.)
recv_files(c.txt)
recv_files(guangzhou.txt)
recv mapped guangzhou.txt of size 75
data recv 84 at 0
got file_sum
set modtime of .guangzhou.txt.2XJrfW to (1631167646) Wed Sep  8 23:07:26 2021
renaming .guangzhou.txt.2XJrfW to guangzhou.txt
touch_up_dirs: . (0)
set modtime of . to (1631167647) Wed Sep  8 23:07:27 2021
touch_up_dirs: b (1)
touch_up_dirs: b/c (3)
touch_up_dirs: b/c/e (4)
touch_up_dirs: b/c/e/f (5)
recv_files(b)
recv_files(b/ccc.txt)
recv_files(b/c)
recv_files(b/c/e)
recv_files(b/c/e/f)
recv_files(b/c/e/f/shanghai.txt)
recv_files(paypal)
recv_files(paypal/ceshi.txt)
recv mapped paypal/ceshi.txt of size 118
data recv 227 at 0
got file_sum
set modtime of paypal/.ceshi.txt.Nk2NXb to (1631167681) Wed Sep  8 23:08:01 2021
renaming paypal/.ceshi.txt.Nk2NXb to paypal/ceshi.txt
touch_up_dirs: paypal (2)
set modtime of paypal to (1631167681) Wed Sep  8 23:08:01 2021
touch_up_dirs: paypal/a (6)
recv_files(paypal/a)
recv_files(paypal/a/a.txt)
send_files phase=1
recv_files phase=1
generate_files phase=2
send_files phase=2
send files finished
total: matches=0  hash_hits=0  false_alarms=0 data=311
recv_files phase=2
recv_files finished
generate_files phase=3
generate_files finished
client_run waiting on 21757

sent 821 bytes  received 6,800 bytes  461.88 bytes/sec
total size is 333  speedup is 0.04
[sender] _exit_cleanup(code=0, file=main.c, line=1179): entered
[sender] _exit_cleanup(code=0, file=main.c, line=1179): about to call exit(0)

1.6 从工作原理分析 rsync 的使用场景

(1)rsync两端耗费计算机的什么资源比较严重?
从前文中已经知道,rsync的sender端因为要多次计算、多次比较各种校验码而对cpu的消耗很高,receiver端因为要从basis file中复制数据而对io的消耗很高。但这只是rsync增量传输时的情况,如果是全量传输(如第一次同步,或显式使用了全量传输选项"–whole-file"),那么sender端不用计算、比较校验码,receiver端不用copy basis file,这和scp消耗的资源是一样的。
(2)rsync不适合对数据库文件进行实时同步。
像数据库这样的大文件,且是频繁访问的文件,如果使用 rsync 实时同步,sender端要计算、比较的数据块校验码非常多,cpu会长期居高不下,从而影响数据库提供服务的性能。另一方面,receiver端每次都要从巨大的basis file(一般提供服务的数据库文件至少都几十G)中复制大部分相同的数据块重组新文件,这几乎相当于直接cp了一个文件,它一定无法扛住巨大的io压力,再好的机器也扛不住。
所以,对频繁改变的单个大文件只适合用rsync偶尔同步一次,也就是备份的功能,它不适合实时同步。像数据库文件,要实时同步应该使用数据库自带的replication功能。

(3) 可以使用 rsync 对大量小文件进行实时同步
由于 rsync 是增量同步,所以对于 receiver 端已经存在的和 sender 端相同(文件小和最后的修改时间)的文件,sender 端是不会发送的,这样就使得 sender 端和 receiver 端都只需要处理少量的文件,由于文件小,所以无论是 sender 端的 cpu 还是 receiver 端的 io 都不是问题。
但是,rsync 的实时同步功能是借助工具来实现的,如 inotify+rsync, sersync, 所以这些工具要设置合理,否则实时同步一样效率低下,不过这不是 rsync 导致的效率低,而是这些工具配置的问题。

摘录自:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值