The Google File System中文版附英文资源链接(下)


4. 主服务器的操作

主服务器执行所有的名称空间操作。另外,它管理整个系统的所有块副本:它决定块的位置,创建新块和相应的副本,协调多变的系统活动保持块被完全复制,均衡所有块服务器之间的负载,回收没有使用的存储空间。现在我们讨论每一个主题。

4.1 名称空间管理和锁

主服务器的许多操作会花费较长的时间:例如,快照操作必须取消快照内容涵盖的所有块服务器的租约。我们不希望这些操作的运行延迟其他的主服务器操作。所以我们允许多个操作同时进行,用名称空间的区域之上的锁来保证相应的秩序。

不同于许多传统文件系统的是,GFS没有一个用来列出目录内全部文件的,每个目录的数据结构。而且不支持同一文件或者目录的别名(Unxi术语中的符号链接或者硬链接)。GFS展现名称空间的逻辑就像一个全路径映射到元数据的查找表。利用前缀压缩,这个表可以高效的在内存中展现。名称空间树内的每个节点(绝对路径文件名和绝对路径目录名)都有一个与之对应的读写锁。

每个主服务器操作运行之前都需要获得一系列的锁。例如,如果操作包含/d1/d2/.../dn/leaf,首先获得目录/d1,/d1/d2,...,/d1/d2/.../dn的读取锁,以及全路径/d1/d2/.../dn/leaf的读写锁。注意leaf可以是一个文件也可以是一个目录,这取决于操作本身。

现在我们演示锁机制如何在/home/user被快照到/save/user的时候,防止文件/home/user/foo的创建。快照操作获得/home和/save的读取锁,以及/home/user和/save/user的写入锁。文件创建操作获得/home和/home/user的读取锁,以及/home/user/foo的写入锁。这两个操作会顺序执行,因为它们试图冲突地获取/home/user的锁。文件创建操作不去请求父目录的写入锁,因为这里没有"目录",或者类似inode的数据结构,用来防止修改。文件名的读取锁完全可以防止父目录被删除。

这种锁方案的好的属性是它支持对同一目录的并行操作。例如,同一个目录的多个文件创建操作可以并行执行:每一个操作获得目录名的读取锁和文件名本身的写入锁。目录名的读取锁完美的防止目录被删除,改名以及被快照。文件名的写入锁保证不会创建同名文件两次。

因为名称空间可以有许多节点,所以读写锁需要的时候才会被分配,一旦不再使用就会被删除。锁的获取依据一个全局一致的顺序来避免死锁:首先由名称空间的层次决定顺序,统一层次内的锁顺序由字典顺序决定。

4.2 副本布置

GFS集群是多层高度分布的。典型的来说有数百个块服务器安装在许多机架上。这些块服务器可能依次被数百个来自同一机架或者不同机架的客户机访问。来自不同机架的两台机器之间的通讯也许会跨越一个或多个网络交换机。另外,机架的出入带宽可能比机架内所有机器和在一起的带宽要小。多层分布带来伸缩性,可靠性以及可用性方面完全不同的挑战。

块副本布置策略服务于两个目标:最大化数据可靠性和可用性,最大化网络带宽利用率。为了这些,仅仅在机器间分布文件是不够的,这只能防止硬盘损坏或者机器失效带来的影响,以及最大化每台机器的网络带宽使用率。我们必须在机架间分布文件。这保证了即使某个机架完全被损坏或者离线,一些块的副本会幸存仍旧可以使用(例如,电源或者网络交换机故障造成的问题)。这还意味着网络通讯,尤其是读取,可以利用多个机架的整合带宽。另一面是,写操作的网络通讯必须通过多个机架,这是我们乐于付出的代价。

4.3 创建,重新复制,负载均衡

块副本创建有三种原因:块创建,重新复制和负载均衡。

当主服务器创建一个块,它会选择哪里放置初始的空副本。具体策略取决于几个因素。(1)我们希望把新的副本放置在低于平均硬盘使用率的块服务器。这样平衡块服务器之间的硬盘使用率。(2)我们希望限制每一个块服务器上"近期"创建操作的数量。虽然创建操作本身是廉价的,但是它总是会紧跟着沉重的写操作,因为写入者需要写的时候才会进行创建,而在我们的"追加一次多多次"的工作负载下块一旦被成功写入就会变为只读。(3)如上面讨论过的,我们希望把块分布在机架之间。

一旦块的可用副本数少于用户指定的值,主服务器会重新复制它。这可能发生在几个原因下:一个块服务器变得不可用,块服务器报告存储在之上的副本损坏了,因为错误它的硬盘不可用了,或者副本数目标提高了。每个需要被重新复制的块基于几个因素排序。一是,块离它的副本数量目标有多远。例如,我们给已经丢失两个副本的块比只丢失一个副本的块更高的优先级。另外,我们优先重新复制活着的文件而不是属于那些近期删除的文件的块(查看4.4章节)。最后,为了失效对正在运行的应用程序的影响,我们提高阻塞客户机流程的块的优先级。

主服务器选择优先级最高的块,然后通过通知一些块服务器上直接从已有的可用的副本复制数据的方式来"克隆"它。新副本位置的策略根创建操作类似:平均硬盘使用率,限制同一块服务器上的活动的克隆操作数量,在机架间分布副本。为了防止克隆所需的网络流量超过客户端流量,主服务器限制块和块服务器的活跃克隆操作数量。另外,每个块服务器,通过调节它对源块服务器的读请求来限制它花在每个克隆操作上面的带宽。

最后,主服务器周期性地对副本进行负载均衡:它检查当前的副本分布情况,然后移动副本以得到更好的硬盘剩余空间以及负载的均衡。同时在这个过程中,主服务器逐渐的填满一个新的块服务器,而不是用新块以及随之同时涌入的沉重的写通讯淹没它。新副本的布置标准根上面讨论的相同。另外,主服务器必须选择哪些现有的副本要被移走。一般来说,它移动那些剩余空间低于平均值的块服务器上面的副本,这样就可以平衡硬盘使用率。

4.4 垃圾回收

文件删除后,GFS不会立刻收回可用的物理空间。对文件和块的常规垃圾收集非常惰性。我们发现这样可以使系统更简单更可靠。

4.4.1 机制

当应用程序删除了一个文件,主服务器立刻把删除记录到日志里就像其他的改变一样。然而,它不是马上回收资源,而是把文件改成一个包含删除时间戳的隐藏的名字。当主服务器对文件系统命名空间进行常规扫描的时候,它会删除所有有如此隐藏文件名的文件,如果它们已经存在超过3天了(这个时间间隔是可以被设置的)。直到此时,文件仍旧可以用新的特殊的名字访问读取,可以被反删除,通过改名把它变为正常文件。如果隐藏文件从名称空间中移走,保存在内存中它的元数据就被删除了。这有效的服务于对所有块的连接。

在相似的对块命名空间的常规扫描,主服务器找到孤儿块(无法从任何文件到达的块)并擦除它们的元数据。在与主服务器交换数据的心跳信息中,每个块服务器报告它拥有的块的信息的一个子集,然后主服务器回复指出哪些块在主服务器的元数据中已经不存在了。块服务器上面就是任意删除这些块的副本了。

4.4.2 讨论

虽然分布垃圾回收在编程语言领域是一个需要复杂解决方案的难题,它在我们这里非常简单。我们可以很容易的识别所有块的引用:他们都在服务器独立掌握的文件到块的映射表中。我们也可以轻易的找到所有块的副本:在每个块服务器上他们都是指定目录下的Linux文件。所有主服务器不知道的副本都是"垃圾"。

垃圾回收在恢复存储空间的同时,提供直接删除无法得到的几个好处。首先,这样对于组件经常失效的大规模分布系统简单而且可靠。块创建可能在某些块服务器成功,某些服务器上失败,留着那些失败的副本反正主服务器也不知道。副本删除信息可能丢失,主服务器必须记得重新发送失败传递的目标,它自己的和块服务器的。垃圾回收提供了统一无依赖的途径,来清除那些不知道用处的副本。其次,它把存储回收操作合并到主服务器规律性的后台活动,如例行扫描以及跟块服务器握手。这样,操作会被批量的执行,开销会被分散。而且,操作会在主服务器相对空闲的时候完成。这样主服务器可以给那些需要快速反应的客户机请求提供更快捷的响应。三,有延迟的存储回收比随机、不可逆转的删除,更加安全。

在我们的经验中,主要的缺点是,在存储空间紧张的时候,这样的延迟有时候会阻碍用户去更好的调节存储空间的使用情况。应用程序重复创建和删除临时文件夹,存储空间不会马上恢复。我们解决这个问题的方式是,如果一个已删除文件明确的再次被删除,会加速空间回收速度。我们还允许用户为命名空间的不同部分设定不同的复制和回收策略。例如,用户可以指定某些目录树下面的文件没有任何副本,删除立即执行,从文件系统移除后无法恢复。

4.5 过期副本检测

如果块服务器失效,或者块服务器当机的时候错过了一些操作,块副本会过期。对于每个块,主服务器保存一个块版本号,用来分辨当前副本和过期副本。

无论何时主服务器获得一个块的新租约,它增加块的版本号,然后通知当前副本。主服务器和这些副本都把新的版本号记录在它们的持久化存储的状态中。这发生在任何客户机得到通知以前,发生在它写入块之前。如果另外一个块服务器正好不可用,那么它的块版本号就不会被更新。当这个块服务器重新启动,向主服务器报告它拥有的块的集合以及相应的版本号的时候,主服务器就会检测出来块服务器包含过期的块。当主服务器看到一个比它们的记录更高的版本号,主服务器假定那些块服务器在获取租约的时候失效了,所以选择更高的版本作为当前的版本。

主服务器在周期的垃圾回收中移除所有的过期副本。在这之前,当回复客户机的块信息请求的时候,主服务器只是高效的当作那些过期的块不存在而已。作为另外的保护措施,主服务器通知客户机哪个块服务器持有租约的时候,当主服务器指示块服务器从哪个块服务器读取数据进行克隆操作的时候,主服务器的信息都包含了块的版本号。客户机或者块服务器执行操作时会验证版本号,这样它们就可以总是访问当前版本的数据了。

5. 容错和诊断

我们面临的最大挑战之一就是设计一个系统处理频繁的组件失效。组件的数量和质量让这种问题变得比意外更常见:我们不能完全信任机器,也不能完全信任硬盘。组件的失效可能造成不可用的系统,坏的,不完整的数据。我们讨论我们如何面对这些挑战,讨论我们构建的用于诊断系统故障的工具。

5.1 高可用性

在GFS集群的数百个服务器之中,任何给定时刻都有一些是不可用的。我们用两条简单有效的策略保持整个系统的高可用性:快速恢复和副本。

5.1.1 快速恢复

不管主服务器和块服务器是如何关闭的,它们被设计为可以在数秒钟内恢复它们的状态并启动。事实上,我们并不区分正常或者非正常的关闭;通常关闭服务器的方法就是杀死它的进程。对客户机和其他的服务器来说,他们发出的请求超时只是个小问题,重新连接到重启后的服务器,重试。6.6.2章节报告了观察到的启动时间。

5.1.2 块复制

诚如之前讨论的,每个块被复制到不同机架上的不同的块服务器上。用户可以为文件命名空间的不同部分设定不同的复制级别。缺省值是3。当有块服务器离线或者通过校验和验证(查看5.2章节)发现了坏块,主服务器克隆已存在的副本用来保证每个块被完全复制。虽然块复制工作的很好,但是我们也在寻找需要同等或者更少代码的,其他形式的跨服务器冗余方案来解决我们日益增长的只读存储需求。我们希望它更有挑战性,但是更加易于管理,在我们非常松散的系统结构下实现更复杂的冗余体系,因为我们的通讯主要是追加和读取构成的,而不是小的随机写。

5.1.3 主节点复制

为了可靠性主服务器状态要被复制。它的操作日志和检查点都要复制到多台机器上。对状态的操作被设计为,只有当它的日志记录被写入到本地硬盘以及所有的主服务器副本以后才会被提交。为了简化设计,一个主服务器进程管理所有改变系统状态的操作包括如垃圾回收这样的后台活动。当它失效,它几乎可以马上重新启动。当它的机器或者硬盘损坏,GFS架构之外的监视机构会总启动一个包含这操作日志副本的新主服务器进程。客户机只使用主服务器的规范名(例如,gfs-test)与之联络,这是一个DNS别名,如果主服务器被重新指定它可以相应的改变。

此外,还有些"影子"主服务器,它们即使在真正的主服务器当机的时候也只提供文件系统的只读访问。它们是影子,而不是镜像,所以它们的数据总是比主服务器更新要慢点,通常是1秒以下。它们可以提高那些不经常改变的文件的读取效率,或者是帮助那些不在乎数据更新略有延迟的程序。事实上,自从文件从块服务器被读走,应用程序就不再关心它们内容的变化了。在短暂窗口可以延迟的是文件的元数据,比如目录的内容或者访问控制信息。

为了保持自己被通知,影子主服务器增长中的操作日志的副本,并按照与主服务器完全相同的顺序对自己的数据结构执行修改。类同主服务器,它也在启动的时候轮询块服务器(之后定期访问)以获得块副本的位置,交换心跳信息以监控它们的状态。它仅在主服务器决定创建和删除副本的时候,才需要依靠主服务器得到副本位置的更新结果。

5.2 数据完整性

每个块服务器利用校验和来检查存储的数据是否损坏。一个拥有数百台机器数千个硬盘的GFS集群,硬盘故障带来的数据损坏和丢失是非常频繁的。(See Section 7 for one cause.)我们可以利用块的其他副本修复损坏,但是通过比较多台块服务器的数据来检查损坏是非常不可行的。而且,不一致的副本可能是合法的:GFS操作的语意,特别是前面讨论过的原子性的记录添加,不保证产生完全一致的副本。所以,每个块服务器必须通过校验和,独立的验证它自己拥有的拷贝的数据完整性。

块被分为64KB的大小。每个有一个对应的32位的校验和。和其他元数据一样,校验和保存在内存,持续化存储在日志里,与用户数据是分离的。


不管是对客户机还是其他的块服务器,在读取操作中,块服务器在返回任何数据之前验证读取范围的数据的校验和。这样块服务器就不会把损坏的数据传播给其他机器。如果块和记录的校验和不符,块服务器给请求者返回一个错误信息,然后把这个不匹配报告给主服务器。作为回应,请求者读取其他的副本,而主服务器从其他的副本克隆这个块。一个好的新块布置好后,主服务器指导报告不匹配的那台块服务器删除这个副本。

校验和对读取性能影响不大,基于几个原因。因为我们的读取范围至少是几个块,而我们只需要读取很少的额外数据去做验证。GFS客户机代码更进一步降低了负载,因为它尽量在校验和块的边界对齐读取。而且,校验和的查询和比较都在块服务器完成,不需要任何I/O,而且校验和的计算通常跟I/O同步进行。

在追加到文件尾的写入操作(而不是覆盖现有数据的写入操作),校验和的计算被严重的优化,因为这类操作是我们负载中最优先的。我们只是增量更新最后一块追加记录的校验和,用所有的追加来的新校验和块来计算新的校验和。即使最后一个校验和块破坏了,而我们无法检测到它,但是这样新的校验和就不会符合存储的数据,那么我们就可以在下次读的时候检测到这个损坏了。

相对而言,如果写入覆盖块的已存在区域,我们必须读取并且验证写入区域的第一个和最后一个数据块,然后执行写操作,最后计算和记录新的校验和。如果在写之前,我们不验证第一个和最后一个数据块,那么新的校验和可能会隐藏位于那些没有被覆盖区域的数据错误。

在空闲期间,块服务器可以扫描并验证不活跃块的内容。这就使我们可以检查那些很少被读取的块。一旦检测出损坏,主服务器就可以创建一个新的没有损坏的副本,并删掉损坏的副本。这就避免了一个不活跃的坏块骗过主服务器,让之以为块有足够的好的副本。

5.3 诊断工具

广泛而详尽的分析日志,在问题隔离,调试,性能分析等方面对我们的帮助无法估量,与此同时它几乎没有开销。没有日志,我们很难理解短暂的不重复的机器之间的交互。GFS的服务器们生成许多事件(例如,块服务器启动和当机)以及所有的RPC(译注:远程进程调用)请求和回复。这些分析日志即使被删除也不会影响系统的正确运转。然而,我们尽量保存这些日志,直到达到空间极限。

RPC日志包含了除了读写的文件数据本身以外的线上传输的所有详细的请求和回应。通过匹配请求与回应,收集不同机器的RPC记录,我们可以重建整个通信历史来分析问题。日志还用来跟踪负载测试和性能分析。

日志对性能的影响很小(原小于带来的好处),因为这些日志是顺序和异步的。最近的时间保存在内存中,可用于连续的在线监控。

6. 测量

这一章节,我们用一个小型测试来展现GFS系统架构和实现的固有瓶颈,还有些来自Google使用的真实的集群的数字。

6.1 小规模测试

我们用一个包含1个主服务器,2个主服务器副本,16个块服务器,和16个客户机组成的GFS集群测量GFS的性能。注意设置这个配置只是为了易于测试。典型的集群有数百个块服务器,数百个客户机。

所有机器的配置是,双PIII 1.4GHz处理器,2GB内存,两个80G,5400rpm硬盘,以及100Mbps全双工以太网连接到HP2524交换机。所有19个GFS服务器连接在一个交换机,所有16个客户端连接在另一个上。两个交换机用1Gbps的线路连接。

6.1.1 读取

N个客户机从文件系统同步读取数据。每个客户机从320GB的文件集读取随机选择的4MB区域的内容。这操作重复256次,这样每个客户机最后会读取1GB的数据。所有的块服务器一共仅有32GB的内存,所以我们期望至少10%的请求命中Linux的缓冲。我们的结果应该接近一个几乎无缓冲的结果。




图3: Aggregate Throughputs

图3(a)显示了N个客户机的整合读取速度以及它的理论极限。当两个交换机之间使用1Gbps的连接的时候整合读取的理论极限的顶峰是125MB/s,或者是当客户机的100Mbps网络饱和的时候每个客户机12.5MB/s。实测结果是当一个客户机读的时候,读取速度10M/s,或者说客户机极限的80%。16个客户机读取的时候,整合的读取速度达到了94MB/s,大约是连接极限的75%,或者说每个客户机6MB/s。效率从80%降低到75%,主要原因是读取者增加了,多个读取者同时读取一个块服务器的几率也提高了。

6.1.2 写入

N个客户机同步写入N个不同的文件。每个客户机用一次1MB的速度写入1GB的数据。整合的写入速度和它们的理论极限显示在图3(b)。理论极限的顶峰是67MB/s,因为我们需要把每一个位写入到16个块服务器中的3个里面,每个拥有12.5MB/s的输入连接。

一个客户机的写入速度是6.3MB,大概是极限的一半。主要的原因是我们的网络结构。它和我们推送数据到块服务器使用的管道模式不相适应。从一个副本到另一个副本之间传输数据的延迟降低了整个写入速度。

16个客户机并行写入的速度达到了35MB/s(每个客户机2.2MB/s),大约是理论值的一半。跟读取的案例很相似,一旦客户机的数量增加,就会有更多的客户机同时写入同一个块服务器。而且,16个写入者的并行冲突比16个读取者要大得多,因为每个写入都与三个不同的副本相关。

写入比我们预想的要慢。但是在实践中,这没有成为我们的主要问题,因为即使多个客户机会增加写入的延迟,但是在我们拥有大量客户机的系统中,它没有对整合的写入带宽造成显著的影响。

6.1.3 记录追加

图3(c)显示了记录追加的性能。N个客户机同步追加到一个文件。性能受限于保存最后一个块的块服务器的带宽,与客户机数量无关。由一个客户机6.0MB/s开始,直到6个客户机4.8MB/s结束,主要原因是不同客户机看到的拥挤和混乱造成的。

我们的程序倾向于同时处理多个这样的文件。换句话说,N个客户机同时追加到M个共享文件,N和M都是数十或者数百以上。所以,块服务器的网络拥挤在我们经验中没有成为实践中的一个显著问题,因为客户机在块服务器某个文件繁忙的时候,优先写入另外的一个文件。

6.2 真实的集群

现在我们审视Google正在使用的两个集群,它们具有一定的代表性。集群A通常用于研究和开发,被上百工程师使用。典型的任务是被人工初始化,然后运行数个小时。它读取从数MB到数TB的数据,转化或者分析数据,然后把结果写回到集群中。集群B主要用于产品的数据处理。任务持续更长的时间,持续生成和处理数TB的数据集,只有非常偶然的人工参与。在两个案例中,一个"任务"都包含了运行在多个机器上的多个进程,同时读取和写入多个文件。

6.2.1 存储

正如表格中前五个条目显示的,这两个集群都有数百个块服务器,提供数TB的硬盘空间,有相当的数据,但是都没完全塞满。"已用空间"包括所有的块副本。几乎所有的文件都被复制了三次。它们分别存储了18TB和52TB的文件数据。

这两个集群有类似数目的文件数,虽然集群B有大量的死文件,改名的文件,已经删除或者被新版本替换的文件,但是它们仍旧被存储没有被回收。它有更多的块,因为它的文件更大。

6.2.2 元数据

所有块服务器一共保存了数十GB的元数据,大多数是用户数据的64KB的校验和。另外的,保存在块服务器仅有的元数据是块版本号,在4.5章节我们曾经讨论过。

保存在主服务器的元数据更小,只有数十MB,平均一个文件100个字节。这验证了我们的论断,那就是在实际中主服务器的内存大小不是局限系统能力的主要因素。每个文件元数据最大的部分是以前缀压缩形式保存的文件名。元数据的其他部分包括,文件的拥有者和权限,文件到块的映射,每个块的当前版本。另外,我们保存每个块的当前副本位置,以及一个用来实现copy-on-write技术的引用计数。

每个分别的服务器,不管块服务器还是主服务器,都只有50到100MB的元数据。所以恢复会很快:在服务器可以回复请求之前,只需要几秒钟去从硬盘读取这些元数据。然而,服务器可能会不稳定一会儿--通常是30-60秒--知道它从所有块服务器获得块位置信息。

6.2.3 读写速度

表3展示不同时间阶段的读写速度。在测量之前,两个集群都运行了一个星期以上。(最近集群都重启过,更新了最新版本的GFS。)


表3:Performance Metrics for Two GFS Clusters

一重启动平均写入速度就低于30MB。进行测试时,集群B正处于100MB/s数据带来的写入高峰之中,产生了300MB/s的网络负担,因为写入要传送到三个副本。

读取速度比写入速度高许多。正像我们预期的那样,总的负载中包括的读取比写入要多。两个集群都处在沉重的读取负担中。实践中,之前的一个星期集群A都保持了580MB/s的读取速度。它的网络设置可以支持750MB,所以它的资源使用效率很高。集群B可以支持1300MB的峰值读取速度,但是它的程序只使用了380MB。

6.2.4 主服务器负载

表3显示了发送操作的速度大概是每秒钟200到500个操作。主服务器可以轻松的保持这个速度,所以这不是负载中的瓶颈。

在早期版本的GFS中,主服务器偶尔会成为负载中的瓶颈。它花费了大量的时间顺序扫描大目录(包含数万个文件)寻找某个特点的文件。我们改变了主服务器数据结构提高名称空间二进制搜索的效率。现在可以轻松的支持每秒钟数千次文件访问。如果需要的话,我们可以通过在名称空间数据结构之前假设名称查询缓冲的方式进一步提高速度。

6.2.5 恢复时间

一个块服务器失效后,有些块的副本数量可能过低,必须被克隆以恢复它们的复制水平。恢复所有这样的块需要的时间取决于资源的总量。在我们的实验中,我们杀死集群B里面的一台块服务器。这个块服务器有15000个块,包含600GB的数据。为了限制对正在运行的程序的干扰,为一些定期任务提供余地,我们的默认参数限制集群中最多有91个并行的克隆操作(块服务器数量的40%),每个克隆操作的速度可以是6.25MB(50Mbps)。所有的块会在23.2分钟内恢复,复制的速度是440MB/s。

在另外的实现中,我们杀死两个块服务器,每个大概有16000个块和660GB数据。这个双倍的失效,造成266个块只有一个副本。这266个块被优先复制,在2分钟内所有都恢复到至少两个副本,这样就把集群推到一个状态,可以容忍另外的块服务器失效,不会造成数据丢失。

6.3 负载分析

在这个章节,我们分析两个GFS集群负载的构成,这不同于6.2章节讨论的内容。集群X用于研究和开发,集群Y用于产品的数据处理。

6.3.1 方法论和警告

这些结果只包括客户机发出的请求,它们可以反映出我们程序在整个文件系统产生的工作负担。它们不包括哟那股承载客户机请求的服务器间请求,也不包括内部的后台活动,例如转发的写操作,以及负载均衡的操作。

I/O操作的统计信息基于那些利用真实的GFS服务器记录的RPC请求日志重建的信息。例如,GFS客户机代码可能把一个读操作分成几个RPC请求来提高并行性。因为我们的访问高度程式化,我们希望任何错误都包含在噪声内。更详尽的日志可能提高数据的准确性,但是不太可能为了这个去重新编译和重新启动数千个正在运行的客户机,收集那么多机器的结果太过麻烦。

小心不要太过于概括我们的工作负载。因为Google完全控制GFS和应用程序,所以应用程序都为GFS优化,而同时GFS也是为这些应用程序设计的。这样的相互作用可能也存在于一般程序和文件系统中,但是它们的影响显然没有我们的案例那么严重。

6.3.2 块服务器负载


表4: Operations Breakdown by Size (%)

表4 展现了数据大小和操作分布程度的关系。读取的分布情况呈现出两个高峰。小的读取操作(64KB以下)来自位置查询操作集中的客户机,它们在巨大文件中寻找小块的数据。大的读取(超过512KB)来自对整个文件的长连续读取。

在集群Y中有大量的读取操作完全不返回数据。我们的程序,尤其是在产品系统里,经常使用文件作为生产者-消费者对列。生产者持续的追加文件而消费者读取文件的尾部。偶尔的,不返回任何数据,因为消费者的速度超过了生产者。集群X中很少出现这种情况,因为它通常用于短期存在的数据分析任务,而不是长期存活的分布应用程序。

Write sizes also exhibit a bimodal distribution. The large
writes (over 256 KB) typically result from significant buffering
within the writers. Writers that buffer less data, checkpoint
or synchronize more often, or simply generate less data
account for the smaller writes (under 64 KB).
As for record appends, cluster Y sees a much higher percentage
of large record appends than cluster X does because
our production systems, which use cluster Y, are more aggressively
tuned for GFS.
Table 5 shows the total amount of data transferred in operations
of various sizes. For all kinds of operations, the
larger operations (over 256 KB) generally account for most
of the bytes transferred. Small reads (under 64 KB) do
transfer a small but significant portion of the read data because
of the random seekw orkload.
6.3.3 Appends versus Writes
Record appends are heavily used especially in our production
systems. For cluster X, the ratio of writes to record
appends is 108:1 by bytes transferred and 8:1 by operation
counts. For cluster Y, used by the production systems, the
ratios are 3.7:1 and 2.5:1 respectively. Moreover, these ratios
suggest that for both clusters record appends tend to
be larger than writes. For cluster X, however, the overall
usage of record append during the measured period is fairly
low and so the results are likely skewed by one or two applications
with particular buffer size choices.
As expected, our data mutation workload is dominated
by appending rather than overwriting. We measured the
amount of data overwritten on primary replicas. This ap-



proximates the case where a client deliberately overwrites
previous written data rather than appends new data. For
cluster X, overwriting accounts for under 0.0001% of bytes
mutated and under 0.0003% of mutation operations. For
cluster Y, the ratios are both 0.05%. Although this is minute,
it is still higher than we expected. It turns out that most
of these overwrites came from client retries due to errors or
timeouts. They are not part of the workload per se but a
consequence of the retry mechanism.
6.3.4 Master Workload
Table 6 shows the breakdown by type of requests to the
master. Most requests askfor chunk locations (FindLocation)
for reads and lease holder information (FindLease-
Locker) for data mutations.
Clusters X and Y see significantly different numbers of
Delete requests because cluster Y stores production data
sets that are regularly regenerated and replaced with newer
versions. Some of this difference is further hidden in the
difference in Open requests because an old version of a file
may be implicitly deleted by being opened for write from
scratch (mode “w” in Unix open terminology).
FindMatchingFiles is a pattern matching request that supports
“ls” and similar file system operations. Unlike other
requests for the master, it may process a large part of the
namespace and so may be expensive. Cluster Y sees it much
more often because automated data processing tasks tend to
examine parts of the file system to understand global application
state. In contrast, cluster X’s applications are under
more explicit user control and usually know the names of all
needed files in advance.



相关内容