大数据理论篇HDFS的基石——Google File System(二)

简介: Google File System但凡是要开始讲大数据的,都绕不开最初的Google三驾马车:Google File System(GFS), MapReduce,BigTable。为这一切的基础的Google File System,不但没有任何倒台的迹象,还在不断的演化,事实上支撑着Google这个庞大的互联网公司的一切计算。以下是原文内容,内容较长,建议详细阅读。

4 Master 节点的操作


Master 节点执行所有的名称空间操作。此外,它还管理着整个系统里所有 Chunk 的副本:它决定 Chunk的存储位置,创建新 Chunk 和它的副本,协调各种各样的系统活动以保证 Chunk 被完全复制,在所有的 Chunk服务器之间的进行负载均衡,回收不再使用的存储空间。本节我们讨论上述的主题。

4.1 名称空间管理和锁

Master 节点的很多操作会花费很长的时间:比如,快照操作必须取消 Chunk 服务器上快照所涉及的所有的 Chunk 的租约。我们不希望在这些操作的运行时,延缓了其它的 Master 节点的操作。因此,我们允许多个 操作同时进行,使用名称空间的 region 上的锁来保证执行的正确顺序。不同于许多传统文件系统,GFS 没有针对每个目录实现能够列出目录下所有文件的数据结构。GFS 也不 支持文件或者目录的链接(即 Unix 术语中的硬链接或者符号链接)。在逻辑上,GFS 的名称空间就是一个全 路径和元数据映射关系的查找表。利用前缀压缩,这个表可以高效的存储在内存中。在存储名称空间的树型 结构上,每个节点(绝对路径的文件名或绝对路径的目录名)都有一个关联的读写锁。每个 Master 节点的操作在开始之前都要获得一系列的锁。通常情况下,如果一个操作涉及/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 集群是高度分布的多层布局结构,而不是平面结构。典型的拓扑结构是有数百个 Chunk 服务器安装 在许多机架上。Chunk 服务器被来自同一或者不同机架上的数百个客户机轮流访问。不同机架上的两台机器 间的通讯可能跨越一个或多个网络交换机。另外,机架的出入带宽可能比机架内所有机器加和在一起的带宽 要小。多层分布架构对数据的灵活性、可靠性以及可用性方面提出特有的挑战。Chunk 副本位置选择的策略服务两大目标:最大化数据可靠性和可用性,最大化网络带宽利用率。为了 实现这两个目的,仅仅是在多台机器上分别存储这些副本是不够的,这只能预防硬盘损坏或者机器失效带来的影响,以及最大化每台机器的网络带宽利用率。我们必须在多个机架间分布储存Chunk的副本。这保证Chunk 的一些副本在整个机架被破坏或掉线(比如,共享资源,如电源或者网络交换机造成的问题)的情况下依然 存在且保持可用状态。这还意味着在网络流量方面,尤其是针对 Chunk 的读操作,能够有效利用多个机架的 整合带宽。另一方面,写操作必须和多个机架上的设备进行网络通信,但是这个代价是我们愿意付出的。

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

Chunk 的副本有三个用途:Chunk 创建,重新复制和重新负载均衡。当 Master 节点创建一个 Chunk 时,它会选择在哪里放置初始的空的副本。Master 节点会考虑几个因素 (1)我们希望在低于平均硬盘使用率的 Chunk 服务器上存储新的副本。这样的做法最终能够平衡 Chunk 服务器之间的硬盘使用率。(2)我们希望限制在每个 Chunk 服务器上“最近”的 Chunk 创建操作的次数。虽然创建操作本身是廉 价的,但是创建操作也意味着随之会有大量的写入数据的操作,因为 Chunk 在 Writer 真正写入数据的时候才 被创建,而在我们的“追加一次,读取多次”的工作模式下,Chunk 一旦写入成功之后就会变为只读的了。(3)如上所述,我们希望把 Chunk 的副本分布在多个机架之间。当 Chunk 的有效副本数量少于用户指定的复制因数的时候,Master 节点会重新复制它。这可能是由几个 原因引起的:一个 Chunk 服务器不可用了,Chunk 服务器报告它所存储的一个副本损坏了,Chunk 服务器的 一个磁盘因为错误不可用了,或者 Chunk 副本的复制因数提高了。每个需要被重新复制的 Chunk 都会根据几 个因素进行排序。一个因素是 Chunk 现有副本数量和复制因数相差多少。例如,丢失两个副本的 Chunk 比丢 失一个副本的 Chunk 有更高的优先级。另外,我们优先重新复制活跃(live)文件的 Chunk 而不是最近刚被 删除的文件的 Chunk(查看 4.4 节)。最后,为了最小化失效的 Chunk 对正在运行的应用程序的影响,我们提 高会阻塞客户机程序处理流程的 Chunk 的优先级。Master 节点选择优先级最高的 Chunk,然后命令某个 Chunk 服务器直接从可用的副本”克隆”一个副本 出来。选择新副本的位置的策略和创建时类似:平衡硬盘使用率、限制同一台 Chunk 服务器上的正在进行的 克隆操作的数量、在机架间分布副本。为了防止克隆产生的网络流量大大超过客户机的流量,Master 节点对 整个集群和每个 Chunk 服务器上的同时进行的克隆操作的数量都进行了限制。另外,Chunk 服务器通过调节 它对源 Chunk 服务器读请求的频率来限制它用于克隆操作的带宽。最后,Master 服务器周期性地对副本进行重新负载均衡:它检查当前的副本分布情况,然后移动副本以便更好的利用硬盘空间、更有效的进行负载均衡。而且在这个过程中,Master服务器逐渐的填满一个新的Chunk服务器,而不是在短时间内用新的 Chunk 填满它,以至于过载。新副本的存储位置选择策略和上面讨论的相同。另外,Master 节点必须选择哪个副本要被移走。通常情况,Master 节点移走那些剩余空间低于平均值的Chunk 服务器上的副本,从而平衡系统整体的硬盘使用率。

4.4 垃圾回收

GFS 在文件删除后不会立刻回收可用的物理空间。GFS 空间回收采用惰性的策略,只在文件和 Chunk 级 的常规垃圾收集时进行。我们发现这个方法使系统更简单、更可靠。

4.4.1 机制

当一个文件被应用程序删除时,Master 节点象对待其它修改操作一样,立刻把删除操作以日志的方式记 录下来。但是,Master 节点并不马上回收资源,而是把文件名改为一个包含删除时间戳的、隐藏的名字。当 Master 节点对文件系统命名空间做常规扫描的时候,它会删除所有三天前的隐藏文件(这个时间间隔是可以 设置的)。直到文件被真正删除,它们仍旧可以用新的特殊的名字读取,也可以通过把隐藏文件改名为正常显 示的文件名的方式“反删除”。当隐藏文件被从名称空间中删除,Master 服务器内存中保存的这个文件的相关 元数据才会被删除。这也有效的切断了文件和它包含的所有 Chunk 的连接。在对 Chunk 名字空间做类似的常规扫描时,Master 节点找到孤儿 Chunk(不被任何文件包含的 Chunk) 并删除它们的元数据。Chunk 服务器在和 Master 节点交互的心跳信息中,报告它拥有的 Chunk 子集的信息, Master 节点回复 Chunk 服务器哪些 Chunk 在 Master 节点保存的元数据中已经不存在了。Chunk 服务器可以任 意删除这些 Chunk 的副本。

4.4.2 讨论

虽然分布式垃圾回收在编程语言领域是一个需要复杂的方案才能解决的难题,但是在 GFS 系统中是非常 简单的。我们可以轻易的得到 Chunk 的所有引用:它们都只存储在 Master 服务器上的文件到块的映射表中。我们也可以很轻易的得到所有Chunk的副本:它们都以Linux文件的形式存储在Chunk服务器的指定目录下。所有 Master 节点不能识别的副本都是“垃圾”。垃圾回收在空间回收方面相比直接删除有几个优势。首先,对于组件失效是常态的大规模分布式系统, 垃圾回收方式简单可靠。Chunk 可能在某些 Chunk 服务器创建成功,某些 Chunk 服务器上创建失败,失败的 副本处于无法被 Master 节点识别的状态。副本删除消息可能丢失,Master 节点必须重新发送失败的删除消息, 包括自身的和 Chunk 服务器的 24 。垃圾回收提供了一致的、可靠的清除无用副本的方法。第二,垃圾回收把 存储空间的回收操作合并到 Master 节点规律性的后台活动中,比如,例行扫描和与 Chunk 服务器握手等。因 此,操作被批量的执行,开销会被分散。另外,垃圾回收在 Master 节点相对空闲的时候完成。这样 Master节点就可以给那些需要快速反应的客户机请求提供更快捷的响应。第三,延缓存储空间回收为意外的、不可 逆转的删除操作提供了安全保障。根据我们的使用经验,延迟回收空间的主要问题是,延迟回收会阻碍用户调优存储空间的使用,特别是 当存储空间比较紧缺的时候。当应用程序重复创建和删除临时文件时,释放的存储空间不能马上重用。我们 通过显式的再次删除一个已经被删除的文件的方式加速空间回收的速度。我们允许用户为命名空间的不同部 分设定不同的复制和回收策略。例如,用户可以指定某些目录树下面的文件不做复制,删除的文件被即时的、 不可恢复的从文件系统移除。

4.5 过期失效的副本检测

当 Chunk 服务器失效时,Chunk 的副本有可能因错失了一些修改操作而过期失效。Master 节点保存了每 个 Chunk 的版本号,用来区分当前的副本和过期副本。无论何时,只要 Master 节点和 Chunk 签订一个新的租约,它就增加 Chunk 的版本号,然后通知最新的 副本。Master 节点和这些副本都把新的版本号记录在它们持久化存储的状态信息中。这个动作发生在任何客 户机得到通知以前,因此也是对这个 Chunk 开始写之前。如果某个副本所在的 Chunk 服务器正好处于失效状 态,那么副本的版本号就不会被增加。Master 节点在这个 Chunk 服务器重新启动,并且向 Master 节点报告它 拥有的 Chunk 的集合以及相应的版本号的时候,就会检测出它包含过期的 Chunk。如果 Master 节点看到一个 比它记录的版本号更高的版本号,Master 节点会认为它和 Chunk 服务器签订租约的操作失败了,因此会选择 更高的版本号作为当前的版本号。Master 节点在例行的垃圾回收过程中移除所有的过期失效副本。在此之前,Master 节点在回复客户机的 Chunk 信息请求的时候,简单的认为那些过期的块根本就不存在。另外一重保障措施是,Master 节点在通知 客户机哪个 Chunk 服务器持有租约、或者指示 Chunk 服务器从哪个 Chunk 服务器进行克隆时,消息中都附带 了 Chunk 的版本号。客户机或者 Chunk 服务器在执行操作时都会验证版本号以确保总是访问当前版本的数据。


5 容错和诊断


我们在设计 GFS 时遇到的最大挑战之一是如何处理频繁发生的组件失效。组件的数量和质量让这些问题 出现的频率远远超过一般系统意外发生的频率:我们不能完全依赖机器的稳定性,也不能完全相信硬盘的可 靠性。组件的失效可能造成系统不可用,更糟糕的是,还可能产生不完整的数据。我们讨论我们如何面对这 些挑战,以及当组件失效不可避免的发生时,用 GFS 自带工具诊断系统故障。

5.1 高可用性

在 GFS 集群的数百个服务器之中,在任何给定的时间必定会有些服务器是不可用的。我们使用两条简单但是有效的策略保证整个系统的高可用性:快速恢复和复制。

5.1.1 快速恢复

不管 Master 服务器和 Chunk 服务器是如何关闭的,它们都被设计为可以在数秒钟内恢复它们的状态并重 新启动。事实上,我们并不区分正常关闭和异常关闭;通常,我们通过直接 kill 掉进程来关闭服务器。客户 机和其它的服务器会感觉到系统有点颠簸 25 ,正在发出的请求会超时,需要重新连接到重启后的服务器,然后 重试这个请求。6.6.2 章节记录了实测的启动时间。

5.1.2 Chunk 复制

正如之前讨论的,每个 Chunk 都被复制到不同机架上的不同的 Chunk 服务器上。用户可以为文件命名空 间的不同部分设定不同的复制级别。缺省是 3。当有 Chunk 服务器离线了,或者通过 Chksum 校验(参考 5.2 节)发现了已经损坏的数据,Master 节点通过克隆已有的副本保证每个 Chunk 都被完整复制 26 。虽然 Chunk 复制策略对我们非常有效,但是我们也在寻找其它形式的跨服务器的冗余解决方案,比如使用奇偶校验、或 者 Erasure codes 27 来解决我们日益增长的只读存储需求。我们的系统主要的工作负载是追加方式的写入和读取 操作,很少有随机的写入操作,因此,我们认为在我们这个高度解耦合的系统架构下实现这些复杂的冗余方 案很有挑战性,但并非不可实现。

5.1.3 Master 服务器的复制

为了保证 Master 服务器的可靠性,Master 服务器的状态也要复制。Master 服务器所有的操作日志和 checkpoint 文件都被复制到多台机器上。对 Master 服务器状态的修改操作能够提交成功的前提是,操作日志 写入到 Master 服务器的备节点和本机的磁盘。简单说来,一个 Master 服务进程负责所有的修改操作,包括后 台的服务,比如垃圾回收等改变系统内部状态活动。当它失效的时,几乎可以立刻重新启动。如果 Master 进 程所在的机器或者磁盘失效了,处于 GFS 系统外部的监控进程会在其它的存有完整操作日志的机器上启动一 个新的 Master 进程。客户端使用规范的名字访问 Master(比如 gfs-test)节点,这个名字类似 DNS 别名,因 此也就可以在 Master 进程转到别的机器上执行时,通过更改别名的实际指向访问新的 Master 节点。此外,GFS 中还有些“影子”Master 服务器,这些“影子”服务器在“主”Master 服务器宕机的时候提 供文件系统的只读访问。它们是影子,而不是镜像,所以它们的数据可能比“主”Master 服务器更新要慢, 通常是不到 1 秒。对于那些不经常改变的文件、或者那些允许获取的数据有少量过期的应用程序,“影子”Master 服务器能够提高读取的效率。事实上,因为文件内容是从 Chunk 服务器上读取的,因此,应用程序不 会发现过期的文件内容。在这个短暂的时间窗内,过期的可能是文件的元数据,比如目录的内容或者访问控 制信息。“影子”Master 服务器为了保持自身状态是最新的,它会读取一份当前正在进行的操作的日志副本,并 且依照和主 Master 服务器完全相同的顺序来更改内部的数据结构。和主 Master 服务器一样,“影子”Master 服务器在启动的时候也会从 Chunk 服务器轮询数据(之后定期拉数据),数据中包括了 Chunk 副本的位置信 息;“影子”Master 服务器也会定期和 Chunk 服务器“握手”来确定它们的状态。在主 Master 服务器因创建 和删除副本导致副本位置信息更新时,“影子”Master 服务器才和主 Master 服务器通信来更新自身状态。

5.2 数据完整性

每个 Chunk 服务器都使用 Checksum 来检查保存的数据是否损坏。考虑到一个 GFS 集群通常都有好几百 台机器、几千块硬盘,磁盘损坏导致数据在读写过程中损坏或者丢失是非常常见的(第 7 节讲了一个原因)。我们可以通过别的 Chunk 副本来解决数据损坏问题,但是跨越 Chunk 服务器比较副本来检查数据是否损坏很 不实际。另外,GFS 允许有歧义的副本存在:GFS 修改操作的语义,特别是早先讨论过的原子纪录追加的操 作,并不保证副本完全相同(alex 注:副本不是 byte-wise 完全一致的)。因此,每个 Chunk 服务器必须独立维 护 Checksum 来校验自己的副本的完整性。我们把每个 Chunk 都分成 64KB 大小的块。每个块都对应一个 32 位的 Checksum。和其它元数据一样, Checksum 与其它的用户数据是分开的,并且保存在内存和硬盘上,同时也记录操作日志。对于读操作来说,在把数据返回给客户端或者其它的 Chunk 服务器之前,Chunk 服务器会校验读取操作 涉及的范围内的块的 Checksum。因此 Chunk 服务器不会把错误数据传递到其它的机器上。如果发生某个块的 Checksum 不正确,Chunk 服务器返回给请求者一个错误信息,并且通知 Master 服务器这个错误。作为回应, 请求者应当从其它副本读取数据,Master 服务器也会从其它副本克隆数据进行恢复。当一个新的副本就绪后, Master 服务器通知副本错误的 Chunk 服务器删掉错误的副本。Checksum 对读操作的性能影响很小,可以基于几个原因来分析一下。因为大部分的读操作都至少要读取 几个块,而我们只需要读取一小部分额外的相关数据进行校验。GFS 客户端代码通过每次把读取操作都对齐 在 Checksum block 的边界上,进一步减少了这些额外的读取操作的负面影响。另外,在 Chunk 服务器上, Checksum 的查找和比较不需要 I/O 操作,Checksum 的计算可以和 I/O 操作同时进行。Checksum 的计算针对在 Chunk 尾部的追加写入操作做了高度优化(与之对应的是覆盖现有数据的写入操 作),因为这类操作在我们的工作中占了很大比例。我们只增量更新最后一个不完整的块的 Checksum,并且 用所有的追加来的新 Checksum块来计算新的 Checksum。即使是最后一个不完整的 Checksum 块已经损坏了,而且我们不能够马上检查出来,由于新的 Checksum 和已有数据不吻合,在下次对这个块进行读取操作的时候, 会检查出数据已经损坏了。相比之下,如果写操作覆盖已经存在的一个范围内的 Chunk,我们必须读取和校验被覆盖的第一个和最 后一个块,然后再执行写操作;操作完成之后再重新计算和写入新的 Checksum。如果我们不校验第一个和最 后一个被写的块,那么新的 Checksum 可能会隐藏没有被覆盖区域内的数据错误。在 Chunk 服务器空闲的时候,它会扫描和校验每个不活动的 Chunk 的内容。这使得我们能够发现很少被 读取的 Chunk 是否完整。一旦发现有 Chunk 的数据损坏,Master 可以创建一个新的、正确的副本,然后把损 坏的副本删除掉。这个机制也避免了非活动的、已损坏的 Chunk 欺骗 Master 节点,使 Master 节点认为它们已 经有了足够多的副本了。

5.3 诊断工具

详尽的、深入细节的诊断日志,在问题隔离、调试、以及性能分析等方面给我们带来无法估量的帮助, 同时也只需要很小的开销。没有日志的帮助,我们很难理解短暂的、不重复的机器之间的消息交互。GFS 的 服务器会产生大量的日志,记录了大量关键的事件(比如,Chunk 服务器启动和关闭)以及所有的 RPC 的请 求和回复。这些诊断日志可以随意删除,对系统的正确运行不造成任何影响。然而,我们在存储空间允许的 情况下会尽量的保存这些日志。RPC 日志包含了网络上发生的所有请求和响应的详细记录,但是不包括读写的文件数据。通过匹配请求 与回应,以及收集不同机器上的 RPC 日志记录,我们可以重演所有的消息交互来诊断问题。日志还用来跟踪 负载测试和性能分析。日志对性能的影响很小(远小于它带来的好处),因为这些日志的写入方式是顺序的、异步的。最近发生 的事件日志保存在内存中,可用于持续不断的在线监控。


6 度量


本节中,我们将使用一些小规模基准测试来展现 GFS 系统架构和实现上的一些固有瓶颈,还有些来自 Google 内部使用的真实的 GFS 集群的基准数据。

6.1 小规模基准测试

我们在一个包含 1 台 Master 服务器,2 台 Master 服务器复制节点,16 台 Chunk 服务器和 16 个客户机组 成的 GFS 集群上测量性能。注意,采用这样的集群配置方案只是为了易于测试。典型的 GFS 集群有数百个 Chunk 服务器和数百个客户机。所有机器的配置都一样:两个 PIII 1.4GHz 处理器,2GB 内存,两个 80G/5400rpm 的硬盘,以及 100Mbps全双工以太网连接到一个 HP2524 交换机。GFS 集群中所有的 19 台服务器都连接在一个交换机,所有 16 台 客户机连接到另一个交换机上。两个交换机之间使用 1Gbps 的线路连接。

6.1.1 读取

N 个客户机从 GFS 文件系统同步读取数据。每个客户机从 320GB 的文件集合中随机读取 4MB region 的 内容。读取操作重复执行 256 次,因此,每个客户机最终都读取 1GB 的数据。所有的 Chunk 服务器加起来总 共只有 32GB 的内存,因此,我们预期只有最多 10%的读取请求命中 Linux 的文件系统缓冲。我们的测试结 果应该和一个在没有文件系统缓存的情况下读取测试的结果接近。

微信图片_20220527230650.png

上边的曲线显示了我们网络拓扑下的合计理论吞吐量上限。下边的曲线显示了观测到的吞吐量。这个曲 线有着 95%的可靠性,因为有时候测量会不够精确。图 3(a)显示了 N 个客户机整体的读取速度以及这个速度的理论极限。当连接两个交换机的 1Gbps 的链 路饱和时,整体读取速度达到理论的极限值是125MB/S,或者说每个客户机配置的100Mbps网卡达到饱和时, 每个客户机读取速度的理论极限值是 12.5MB/s。实测结果是,当一个客户机读取的时候,读取的速度是 10MB/s, 也就是说达到客户机理论读取速度极限值的 80%。对于 16 个客户机,整体的读取速度达到了 94MB/s,大约 是理论整体读取速度极限值的75%,也就是说每个客户机的读取速度是6MB/s。读取效率从80%降低到了75%, 主要的原因是当读取的客户机增加时,多个客户机同时读取一个 Chunk 服务器的几率也增加了,导致整体的 读取效率下降。

6.1.2 写入

N 个客户机同时向 N 个不同的文件中写入数据。每个客户机以每次 1MB 的速度连续写入 1GB 的数据。图 3(b)显示了整体的写入速度和它们理论上的极限值。理论上的极限值是 67MB/s,因为我们需要把每一 byte 写入到 16 个 Chunk 服务器中的 3 个上,而每个 Chunk 服务器的输入连接速度是 12.5MB/s。

一个客户机的写入速度是 6.3MB,大概是理论极限值的一半。导致这个结果的主要原因是我们的网络协 议栈。它与我们推送数据到 Chunk 服务器时采用的管道模式不相适应。从一个副本到另一个副本的数据传输 延迟降低了整个的写入速度。16 个客户机整体的写入速度达到了 35MB/s(即每个客户机 2.2MB/s),大约只是理论极限值的一半。和 多个客户机读取的情形很类型,随着客户机数量的增加,多个客户机同时写入同一个 Chunk 服务器的几率也 增加了。而且,16 个客户机并行写入可能引起的冲突比 16 个客户机并行读取要大得多,因为每个写入都会 涉及三个不同的副本。写入的速度比我们想象的要慢。在实际应用中,这没有成为我们的主要问题,因为即使在单个客户机上 能够感受到延时,它也不会在有大量客户机的时候对整体的写入带宽造成显著的影响。

6.1.3 记录追加

图 3(c)显示了记录追加操作的性能。N 个客户机同时追加数据到一个文件。记录追加操作的性能受限 于保存文件最后一个 Chunk 的 Chunk 服务器的带宽,而与客户机的数量无关。记录追加的速度由一个客户机 的 6.0MB/s 开始,下降到 16 个客户机的 4.8MB/s 为止,速度的下降主要是由于不同客户端的网络拥塞以及网 络传输速度的不同而导致的。我们的程序倾向于同时处理多个这样的文件。换句话说,即N个客户机同时追加数据到M个共享文件中, 这里 N 和 M 都是数十或者数百以上。所以,在我们的实际应用中,Chunk 服务器的网络拥塞并没有成为一个 严重问题,如果 Chunk 服务器的某个文件正在写入,客户机会去写另外一个文件。

6.2 实际应用中的集群

我们现在来仔细评估一下 Google 内部正在使用的两个集群,它们具有一定的代表性。集群 A 通常被上百 个工程师用于研究和开发。典型的任务是被人工初始化后连续运行数个小时。它通常读取数 MB 到数 TB 的 数据,之后进行转化或者分析,最后把结果写回到集群中。集群 B 主要用于处理当前的生产数据。集群 B 的 任务持续的时间更长,在很少人工干预的情况下,持续的生成和处理数 TB 的数据集。在这两个案例中,一 个单独的“任务”都是指运行在多个机器上的多个进程,它们同时读取和写入多个文件。

微信图片_20220527230654.png

6.2.1 存储

如上表前五行所描述的,两个集群都由上百台 Chunk 服务器组成,支持数 TB 的硬盘空间;两个集群虽 然都存储了大量的数据,但是还有剩余的空间。“已用空间”包含了所有的 Chunk 副本。实际上所有的文件都 复制了三份。因此,集群实际上各存储了 18TB 和 52TB 的文件数据。两个集群存储的文件数量都差不多,但是集群 B 上有大量的死文件。所谓“死文件”是指文件被删除了 或者是被新版本的文件替换了,但是存储空间还没有来得及被回收。由于集群 B 存储的文件较大,因此它的 Chunk 数量也比较多。

6.2.2 元数据

Chunk 服务器总共保存了十几 GB 的元数据,大多数是来自用户数据的、64KB 大小的块的 Checksum。保存在 Chunk 服务器上其它的元数据是 Chunk 的版本号信息,我们在 4.5 节描述过。在 Master 服务器上保存的元数据就小的多了,大约只有数十 MB,或者说平均每个文件 100 字节的元数 据。这和我们设想的是一样的,Master 服务器的内存大小在实际应用中并不会成为 GFS 系统容量的瓶颈。大 多数文件的元数据都是以前缀压缩模式存放的文件名。Master 服务器上存放的其它元数据包括了文件的所有 者和权限、文件到 Chunk 的映射关系,以及每一个 Chunk 的当前版本号。此外,针对每一个 Chunk,我们都 保存了当前的副本位置以及对它的引用计数,这个引用计数用于实现写时拷贝(即 COW,copy-on-write)。对于每一个单独的服务器,无论是 Chunk 服务器还是 Master 服务器,都只保存了 50MB 到 100MB 的元 数据。因此,恢复服务器是非常快速的:在服务器响应客户请求之前,只需要花几秒钟时间从磁盘上读取这 些数据就可以了。不过,Master服务器会持续颠簸一段时间–通常是30到60秒–直到它完成轮询所有的Chunk服务器,并获取到所有 Chunk 的位置信息。

6.2.3 读写速率

微信图片_20220527230657.png

表三显示了不同时段的读写速率。在测试的时候,这两个集群都运行了一周左右的时间。(这两个集群最 近都因为升级新版本的 GFS 重新启动过了)。集群重新启动后,平均写入速率小于 30MB/s。当我们提取性能数据的时候,集群 B 正进行大量的写入操 作,写入速度达到了 100MB/s,并且因为每个 Chunk 都有三个副本的原因,网络负载达到了 300MB/s。读取速率要比写入速率高的多。正如我们设想的那样,总的工作负载中,读取的比例远远高于写入的比 例。两个集群都进行着繁重的读取操作。特别是,集群 A 在一周时间内都维持了 580MB/s 的读取速度。集群 A的网络配置可以支持750MB/s的速度,显然,它有效的利用了资源。集群B支持的峰值读取速度是1300MB/s, 但是它的应用只用到了 380MB/s。

6.2.4 Master 服务器的负载

表 3 的数据显示了发送到 Master 服务器的操作请求大概是每秒钟 200 到 500 个。Master 服务器可以轻松 的应付这个请求速度,所以 Master 服务器的处理能力不是系统的瓶颈。在早期版本的 GFS 中,Master 服务器偶尔会成为瓶颈。它大多数时间里都在顺序扫描某个很大的目录 (包含数万个文件)去查找某个特定的文件。因此我们修改了 Master 服务器的数据结构,通过对名字空间进 行二分查找来提高效率。现在 Master 服务器可以轻松的每秒钟进行数千次文件访问。如果有需要的话,我们 可以通过在名称空间数据结构之前设置名称查询缓冲的方式进一步提高速度。

6.2.5 恢复时间

当某个 Chunk 服务器失效了,一些 Chunk 副本的数量可能会低于复制因子指定的数量,我们必须通过克 隆副本使 Chunk 副本数量达到复制因子指定的数量。恢复所有 Chunk 副本所花费的时间取决于资源的数量。在我们的试验中,我们把集群 B 上的一个 Chunk 服务器 Kill 掉。这个 Chunk 服务器上大约有 15000 个 Chunk, 共计 600GB 的数据。为了减小克隆操作对正在运行的应用程序的影响,以及为 GFS 调度决策提供修正空间, 我们缺省的把集群中并发克隆操作的数量设置为 91 个(Chunk 服务器的数量的 40%),每个克隆操作最多允 许使用的带宽是 6.25MB/s(50mbps)。所有的 Chunk 在 23.2 分钟内恢复了,复制的速度高达 440MB/s。在另外一个测试中,我们 Kill 掉了两个 Chunk 服务器,每个 Chunk 服务器大约有 16000 个 Chunk,共 计 660GB 的数据。这两个故障导致了 266 个 Chunk 只有单个副本。这 266 个 Chunk 被 GFS 优先调度进行复 制,在 2 分钟内恢复到至少有两个副本;现在集群被带入到另外一个状态,在这个状态下,系统可以容忍另 外一个 Chunk 服务器失效而不丢失数据。

6.3 工作负荷分析(Workload Breakdown)

本节中,我们展示了对两个 GFS 集群工作负载情况的详细分析,这两个集群和 6.2 节中的类似,但是不 完全相同。集群 X 用于研究和开发,集群 Y 用于生产数据处理。

6.3.1 方法论和注意事项

本章节列出的这些结果数据只包括客户机发起的原始请求,因此,这些结果能够反映我们的应用程序对 GFS 文件系统产生的全部工作负载。它们不包含那些为了实现客户端请求而在服务器间交互的请求,也不包 含 GFS 内部的后台活动相关的请求,比如前向转发的写操作,或者重新负载均衡等操作。我们从 GFS 服务器记录的真实的 RPC 请求日志中推导重建出关于 IO 操作的统计信息。例如,GFS 客 户程序可能会把一个读操作分成几个 RPC 请求来提高并行度,我们可以通过这些 RPC 请求推导出原始的读 操作。因为我们的访问模式是高度程式化,所以我们认为任何不符合的数据都是误差 28 。应用程序如果能够记 录更详尽的日志,就有可能提供更准确的诊断数据;但是为了这个目的去重新编译和重新启动数千个正在运 行的客户机是不现实的,而且从那么多客户机上收集结果也是个繁重的工作。应该避免从我们的工作负荷数据中过度的归纳出普遍的结论 29 。因为Google完全控制着GFS和使用GFS 的应用程序,所以,应用程序都针对 GFS 做了优化,同时,GFS 也是为了这些应用程序而设计的。这样的相 互作用也可能存在于一般程序和文件系统中,但是在我们的案例中这样的作用影响可能更显著。

微信图片_20220527230700.png

表 4 显示了操作按涉及的数据量大小的分布情况。读取操作按操作涉及的数据量大小呈现了双峰分布。小的读取操作(小于 64KB)一般是由查找操作的客户端发起的,目的在于从巨大的文件中查找小块的数据。大的读取操作(大于 512KB)一般是从头到尾顺序的读取整个文件。在集群 Y 上,有相当数量的读操作没有返回任何的数据。在我们的应用中,尤其是在生产系统中,经常 使用文件作为生产者-消费者队列。生产者并行的向文件中追加数据,同时,消费者从文件的尾部读取数据。某些情况下,消费者读取的速度超过了生产者写入的速度,这就会导致没有读到任何数据的情况。集群 X 通 常用于短暂的数据分析任务,而不是长时间运行的分布式应用,因此,集群 X 很少出现这种情况。写操作按数据量大小也同样呈现为双峰分布。大的写操作(超过 256KB)通常是由于 Writer 使用了缓存 机制导致的。Writer 缓存较小的数据,通过频繁的 Checkpoint 或者同步操作,或者只是简单的统计小的写入 (小于 64KB)的数据量(alex 注:即汇集多次小的写入操作,当数据量达到一个阈值,一次写入),之后批量 写入。再来观察一下记录追加操作。我们可以看到集群 Y 中大的记录追加操作所占比例比集群 X 多的多,这是 因为集群 Y 用于我们的生产系统,针对 GFS 做了更全面的调优。

微信图片_20220527230703.png

表 5 显示了按操作涉及的数据量的大小统计出来的总数据传输量。在所有的操作中,大的操作(超过 256KB)占据了主要的传输量。小的读取(小于 64KB)虽然传输的数据量比较少,但是在读取的数据量中仍 占了相当的比例,这是因为在文件中随机 Seek 的工作负荷而导致的。

6.3.3 记录追加 vs. 写操作

记录追加操作在我们的生产系统中大量使用。对于集群 X,记录追加操作和普通写操作的比例按照字节 比是108:1,按照操作次数比是8:1。对于作为我们的生产系统的集群Y来说,这两个比例分别是 3.7:1和 2.5:1。更进一步,这一组数据说明在我们的两个集群上,记录追加操作所占比例都要比写操作要大。对于集群 X, 在整个测量过程中,记录追加操作所占比率都比较低,因此结果会受到一两个使用某些特定大小的 buffer 的 应用程序的影响。如同我们所预期的,我们的数据修改操作主要是记录追加操作而不是覆盖方式的写操作。我们测量了第 一个副本的数据覆盖写的情况。这近似于一个客户机故意覆盖刚刚写入的数据,而不是增加新的数据。对于 集群 X,覆盖写操作在写操作所占据字节上的比例小于 0.0001%,在所占据操作数量上的比例小于 0.0003%。对于集群 Y,这两个比率都是 0.05%。虽然这只是某一片断的情况,但是仍然高于我们的预期。这是由于这 些覆盖写的操作,大部分是由于客户端在发生错误或者超时以后重试的情况。这在本质上应该不算作工作负 荷的一部分,而是重试机制产生的结果。

6.3.4 Master 的工作负荷

微信图片_20220527230707.png

表 6 显示了 Master 服务器上的请求按类型区分的明细表。大部分的请求都是读取操作查询 Chunk 位置信 息(FindLocation)、以及修改操作查询 lease 持有者的信息(FindLease-Locker)。集群 X 和 Y 在删除请求的数量上有着明显的不同,因为集群 Y 存储了生产数据,一般会重新生成数据 以及用新版本的数据替换旧有的数据。数量上的差异也被隐藏在了 Open 请求中,因为旧版本的文件可能在以 重新写入的模式打开时,隐式的被删除了(类似 UNIX 的 open 函数中的“w”模式)。FindMatchingFiles 是一个模式匹配请求,支持“ls”以及其它类似的文件系统操作。不同于 Master 服务 器的其它请求,它可能会检索 namespace 的大部分内容,因此是非常昂贵的操作。集群 Y 的这类请求要多一 些,因为自动化数据处理的任务进程需要检查文件系统的各个部分,以便从全局上了解应用程序的状态。与 之不同的是,集群 X 的应用程序更加倾向于由单独的用户控制,通常预先知道自己所需要使用的全部文件的 名称。


7 经验


在建造和部署 GFS 的过程中,我们经历了各种各样的问题,有些是操作上的,有些是技术上的。起初,GFS 被设想为我们的生产系统的后端文件系统。随着时间推移,在 GFS 的使用中逐步的增加了对 研究和开发任务的支持。我们开始增加一些小的功能,比如权限和配额,到了现在,GFS 已经初步支持了这 些功能。虽然我们生产系统是严格受控的,但是用户层却不总是这样的。需要更多的基础架构来防止用户间 的相互干扰。我们最大的问题是磁盘以及和 Linux 相关的问题。很多磁盘都声称它们支持某个范围内的 Linux IDE 硬盘 驱动程序,但是实际应用中反映出来的情况却不是这样,它们只支持最新的驱动。因为协议版本很接近,所以

大部分磁盘都可以用,但是偶尔也会有由于协议不匹配,导致驱动和内核对于驱动器的状态判断失误。这会导致数据因为内核中的问题意外的被破坏了。这个问题促使我们使用 Checksum 来校验数据,同时我们也修改内核来处理这些因为协议不匹配带来的问题。较早的时候,我们在使用 Linux 2.2 内核时遇到了些问题,主要是 fsync()的效率问题。它的效率与文件的大小而不是文件修改部分的大小有关。这在我们的操作日志文件过大时给出了难题,尤其是在我们尚未实现 Checkpoint 的时候。我们费了很大的力气用同步写来解决这个问题,但是最后还是移植到了 Linux2.4 内核上。另一个和 Linux 相关的问题是单个读写锁的问题,也就是说,在某一个地址空间的任意一个线程都必须在从磁盘 page in(读锁)的时候先 hold 住,或者在 mmap()调用(写锁)的时候改写地址空间。我们发现即使我们的系统负载很轻的情况下也会有偶尔的超时,我们花费了很多的精力去查找资源的瓶颈或者硬件的问题。最后我们终于发现这个单个锁在磁盘线程交换以前映射的数据到磁盘的时候,锁住了当前的网络线程,阻止它把新数据映射到内存。由于我们的性能主要受限于网络接口,而不是内存 copy 的带宽,因此,我们用pread()替代 mmap(),用了一个额外的 copy 动作来解决这个问题。尽管偶尔还是有其它的问题,Linux 的开放源代码还是使我们能够快速探究和理解系统的行为。在适当的 时候,我们会改进内核并且和公开源码组织共享这些改动。


8 相关工作


和其它的大型分布式文件系统,比如 AFS[5]类似,GFS 提供了一个与位置无关的名字空间,这使得数据 可以为了负载均衡或者灾难冗余等目的在不同位置透明的迁移。不同于 AFS 的是,GFS 把文件分布存储到不 同的服务器上,这种方式更类似 Xfs[1]和 Swift[3],这是为了提高整体性能以及灾难冗余的能力。由于磁盘相对来说比较便宜,并且复制的方式比 RAID[9]方法简单的多,GFS 目前只使用复制的方式来进行冗余,因此要比 xFS 或者 Swift 占用更多的裸存储空间(alex 注:Raw storage,裸盘的空间)。与 AFS、xFS、Frangipani[12]以及 Intermezzo[6]等文件系统不同的是,GFS 并没有在文件系统层面提供 任何 Cache 机制。我们主要的工作在单个应用程序执行的时候几乎不会重复读取数据,因为它们的工作方式 要么是流式的读取一个大型的数据集,要么是在大型的数据集中随机 Seek 到某个位置,之后每次读取少量的 数据。某些分布式文件系统,比如 Frangipani、xFS、Minnesota’s GFS[11]、GPFS[10],去掉了中心服务器,只 依赖于分布式算法来保证一致性和可管理性。我们选择了中心服务器的方法,目的是为了简化设计,增加可 靠性,能够灵活扩展。特别值得一提的是,由于处于中心位置的 Master 服务器保存有几乎所有的 Chunk 相关 信息,并且控制着 Chunk 的所有变更,因此,它极大地简化了原本非常复杂的 Chunk 分配和复制策略的实现 方法。我们通过减少 Master 服务器保存的状态信息的数量,以及将 Master 服务器的状态复制到其它节点来保证系统的灾难冗余能力。扩展能力和高可用性(对于读取)目前是通过我们的影子 Master 服务器机制来保证 的。对 Master 服务器状态更改是通过预写日志的方式实现持久化。为此,我们可以调整为使用类似 Harp[7] 中的 primary-copy 方案,从而提供比我们现在的方案更严格的一致性保证。我们解决了一个难题,这个难题类似 Lustre[8]在如何在有大量客户端时保障系统整体性能遇到的问题。不过,我们通过只关注我们的应用程序的需求,而不是提供一个兼容 POSIX 的文件系统,从而达到了简化问 题的目的。此外,GFS 设计预期是使用大量的不可靠节点组建集群,因此,灾难冗余方案是我们设计的核心。GFS 很类似 NASD 架构[4]。NASD 架构是基于网络磁盘的,而 GFS 使用的是普通计算机作为 Chunk 服 务器,就像 NASD 原形中方案一样。所不同的是,我们的 Chunk 服务器采用惰性分配固定大小的 Chunk 的方 式,而不是分配变长的对象存储空间。此外,GFS 实现了诸如重新负载均衡、复制、恢复机制等等在生产环 境中需要的特性。不同于与 Minnesota’s GFS 和 NASD,我们并不改变存储设备的 Model 30 。我们只关注用普通的设备来解 决非常复杂的分布式系统日常的数据处理。我们通过原子的记录追加操作实现了生产者-消费者队列,这个问题类似 River[2]中的分布式队列。River 使用的是跨主机的、基于内存的分布式队列,为了实现这个队列,必须仔细控制数据流;而 GFS 采用可以被 生产者并发追加记录的持久化的文件的方式实现。River 模式支持 m-到-n 的分布式队列,但是缺少由持久化 存储提供的容错机制,GFS 只支持 m-到-1 的队列。多个消费者可以同时读取一个文件,但是它们输入流的区 间必须是对齐的。


9 结束语


Google 文件系统展示了一个使用普通硬件支持大规模数据处理的系统的特质。虽然一些设计要点都是针 对我们的特殊的需要定制的,但是还是有很多特性适用于类似规模的和成本的数据处理任务。首先,我们根据我们当前的和可预期的将来的应用规模和技术环境来评估传统的文件系统的特性。我们 的评估结果将我们引导到一个使用完全不同于传统的设计思路上。根据我们的设计思路,我们认为组件失效 是常态而不是异常,针对采用追加方式(有可能是并发追加)写入、然后再读取(通常序列化读取)的大文 件进行优化,以及扩展标准文件系统接口、放松接口限制来改进整个系统。我们系统通过持续监控,复制关键数据,快速和自动恢复提供灾难冗余。Chunk 复制使得我们可以对 Chunk 服务器的失效进行容错。高频率的组件失效要求系统具备在线修复机制,能够周期性的、透明的修复 损坏的数据,也能够第一时间重新建立丢失的副本。此外,我们使用 Checksum 在磁盘或者 IDE 子系统级别 检测数据损坏,在这样磁盘数量惊人的大系统中,损坏率是相当高的。我们的设计保证了在有大量的并发读写操作时能够提供很高的合计吞吐量。我们通过分离控制流和数据流来实现这个目标,控制流在 Master 服务器处理,而数据流在 Chunk 服务器和客户端处理。当一般的操作涉及到 Master 服务器时,由于 GFS 选择的 Chunk 尺寸较大(alex 注:从而减小了元数据的大小),以及通过 ChunkLease 将控制权限移交给主副本,这些措施将 Master 服务器的负担降到最低。这使得一个简单、中心的 Master不会成为成为瓶颈。我们相信我们对网络协议栈的优化可以提升当前对于每客户端的写入吞吐量限制。GFS 成功的实现了我们对存储的需求,在 Google 内部,无论是作为研究和开发的存储平台,还是作为生产系统的数据处理平台,都得到了广泛的应用。它是我们持续创新和处理整个 WEB 范围内的难题的一个重要工具。

相关实践学习
简单用户画像分析
本场景主要介绍基于海量日志数据进行简单用户画像分析为背景,如何通过使用DataWorks完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps 
相关文章
|
4月前
|
分布式计算 Java 大数据
【大数据技术Hadoop+Spark】HDFS Shell常用命令及HDFS Java API详解及实战(超详细 附源码)
【大数据技术Hadoop+Spark】HDFS Shell常用命令及HDFS Java API详解及实战(超详细 附源码)
146 0
|
4月前
|
存储 分布式计算 Hadoop
【大数据技术Hadoop+Spark】HDFS概念、架构、原理、优缺点讲解(超详细必看)
【大数据技术Hadoop+Spark】HDFS概念、架构、原理、优缺点讲解(超详细必看)
98 0
|
4月前
|
SQL 分布式计算 Hadoop
[AIGC ~大数据] 深入理解Hadoop、HDFS、Hive和Spark:Java大师的大数据研究之旅
[AIGC ~大数据] 深入理解Hadoop、HDFS、Hive和Spark:Java大师的大数据研究之旅
|
4月前
|
分布式计算 Hadoop 大数据
大数据成长之路-- hadoop集群的部署(3)HDFS新增节点
大数据成长之路-- hadoop集群的部署(3)HDFS新增节点
64 0
|
4月前
|
存储 大数据 Java
【云计算与大数据技术】文件存储格式行式、列式、GFS、HDFS的讲解(图文解释 超详细)
【云计算与大数据技术】文件存储格式行式、列式、GFS、HDFS的讲解(图文解释 超详细)
83 0
|
4月前
|
存储 分布式计算 Hadoop
Hadoop系列HDFS详解
Hadoop系列HDFS详解
39 0
|
5月前
|
存储 分布式计算 Hadoop
aws s3常用命令、hdfs dfs/hadoop fs常用命令
aws s3常用命令、hdfs dfs/hadoop fs常用命令
388 0
|
7月前
|
存储 分布式计算 负载均衡
Hadoop学习笔记(二)之HDFS
Hadoop学习笔记(二)之HDFS
|
2天前
|
分布式计算 Hadoop 测试技术
Hadoop【基础知识 05】【HDFS的JavaAPI】(集成及测试)
【4月更文挑战第5天】Hadoop【基础知识 05】【HDFS的JavaAPI】(集成及测试)
26 8
|
2天前
|
分布式计算 资源调度 Hadoop
Hadoop【基础知识 03+04】【Hadoop集群资源管理器yarn】(图片来源于网络)(hadoop fs + hadoop dfs + hdfs dfs 使用举例)
【4月更文挑战第5天】Hadoop【基础知识 03】【Hadoop集群资源管理器yarn】(图片来源于网络)Hadoop【基础知识 04】【HDFS常用shell命令】(hadoop fs + hadoop dfs + hdfs dfs 使用举例)
24 9

热门文章

最新文章