5. 容错和诊断
我们在设计GFS时遇到的最大挑战之一是如何处理频繁发生的组件失效。组件的数量和质量让这些问题出现的频率远远超过一般系统意外发生的频率: 我们不能完全依赖机器的稳定性,也不能完全相信硬盘的可靠性。组件的失效可能造成系统不可用,更糟糕的是,还可能产生不完整的数据。我们讨论我们如何面对 这些挑战,以及当组件失效不可避免的发生时,用GFS自带工具诊断系统故障。
5.1 高可用性
在GFS集群的数百个服务器之中,在任何给定的时间必定会有些服务器是不可用的。我们使用两条简单但是有效的策略保证整个系统的高可用性:快速恢复和复制。
5.1.1 快速恢复
不管Master服务器和Chunk服务器是如何关闭的,它们都被设计为可以在数秒钟内恢复它们的状态并重新启动。事实上,我们并不区分正常关闭和异常关闭;通常,我们通过直接kill掉进程来关闭服务器。客户机和其它的服务器会感觉到系统有点颠簸 (alex注:a minor hiccup),正在发出的请求会超时,需要重新连接到重启后的服务器,然后重试这个请求。6.6.2章节记录了实测的启动时间。
5.1.2 Chunk复制
正如之前讨论的,每个Chunk都被复制到不同机架上的不同的Chunk服务器上。用户可以为文件命名空间的不同部分设定不同的复制级别。缺省是 3。当有Chunk服务器离线了,或者通过Chksum校验(参考5.2节)发现了已经损坏的数据,Master节点通过克隆已有的副本保证每个 Chunk都被完整复制(alex注:即每个Chunk都有复制因子制定的个数个副本,缺省是3)。虽然Chunk复制策略对我们非常有效,但是我们也在寻找其它形式的跨服务器的冗余解决方案,比如使用奇偶校验、或者Erasure codes(alex注:Erasure codes用来解决链接层中不相关的错误,以及网络拥塞和buffer限制造成的丢包错误)来解决我们日益增长的只读存储需求。我们的系统主要的工作负载是追加方式的写入和读取操作,很少有随机的写入操作,因此,我们认为在我们这个高度解耦合的系统架构下实现这些复杂的冗余方案很有挑战性,但并非不可实现。
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服务器上,Chunksum的查找和比较不需要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的文件系统缓冲。我们的测试结果应该和一个在没有文件系统缓存的情况下读取测试的结果接近。
图三:合计吞吐量:上边的曲线显示了我们网络拓扑下的合计理论吞吐量上限。下边的曲线显示了观测到的吞吐量。这个曲线有着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的数据集。在这两个案例中,一个单独的”任务”都是指运行在多个机器上的多个 进程,它们同时读取和写入多个文件。
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,我们都保存了当前的副本位置 以及对它的引用计数,这个引用计数用于实现写时拷贝(alex注:即COW,copy-on-write)。
对于每一个单独的服务器,无论是Chunk服务器还是Master服务器,都只保存了50MB到100MB的元数据。因此,恢复服务器是非常快速 的:在服务器响应客户请求之前,只需要花几秒钟时间从磁盘上读取这些数据就可以了。不过,Master服务器会持续颠簸一段时间–通常是30到60秒–直 到它完成轮询所有的Chunk服务器,并获取到所有Chunk的位置信息。
6.2.3 读写速率
表三显示了不同时段的读写速率。在测试的时候,这两个集群都运行了一周左右的时间。(这两个集群最近都因为升级新版本的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请求推导出原始的读操作。因为我们的访问模式是高度程式化,所以我们认为任何不符合的数据都是误差 (alex注:Since our access patterns are highly stylized, we expect any error to be in the noise)。应用程序如果能够记录更详尽的日志,就有可能提供更准确的诊断数据;但是为了这个目的去重新编译和重新启动数千个正在运行的客户机是不现实的,而且从那么多客户机上收集结果也是个繁重的工作。
应该避免从我们的工作负荷数据中过度的归纳出普遍的结论 (alex注:即不要把本节的数据作为基础的指导性数据)。因为Google完全控制着GFS和使用GFS的应用程序,所以,应用程序都针对GFS做了优化,同时,GFS也是为了这些应用程序而设计的。这样的相互作用也可能存在于一般程序和文件系统中,但是在我们的案例中这样的作用影响可能更显著。
6.3.2 Chunk服务器工作负荷
表4显示了操作按涉及的数据量大小的分布情况。读取操作按操作涉及的数据量大小呈现了双峰分布。小的读取操作(小于64KB)一般是由查找操作的客户端发起的,目的在于从巨大的文件中查找小块的数据。大的读取操作(大于512KB)一般是从头到尾顺序的读取整个文件。
在集群Y上,有相当数量的读操作没有返回任何的数据。在我们的应用中,尤其是在生产系统中,经常使用文件作为生产者-消费者队列。生产者并行的向文 件中追加数据,同时,消费者从文件的尾部读取数据。某些情况下,消费者读取的速度超过了生产者写入的速度,这就会导致没有读到任何数据的情况。集群X通常 用于短暂的数据分析任务,而不是长时间运行的分布式应用,因此,集群X很少出现这种情况。
写操作按数据量大小也同样呈现为双峰分布。大的写操作(超过256KB)通常是由于Writer使用了缓存机制导致的。Writer缓存较小的数据,通过频繁的Checkpoint或者同步操作,或者只是简单的统计小的写入(小于64KB)的数据量(alex注:即汇集多次小的写入操作,当数据量达到一个阈值,一次写入),之后批量写入。
再来观察一下记录追加操作。我们可以看到集群Y中大的记录追加操作所占比例比集群X多的多,这是因为集群Y用于我们的生产系统,针对GFS做了更全面的调优。
表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的工作负荷
表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 (alex注:对这两个文件系统不了解,因为不太明白改变存储设备的Model用来做什么,这不明白这个model是模型、还是型号)。我们只关注用普通的设备来解决非常复杂的分布式系统日常的数据处理。
我们通过原子的记录追加操作实现了生产者-消费者队列,这个问题类似River[2]中的分布式队列。River使用的是跨主机的、基于内存的 分布式队列,为了实现这个队列,必须仔细控制数据流;而GFS采用可以被生产者并发追加记录的持久化的文件的方式实现。River模式支持m-到-n的分 布式队列,但是缺少由持久化存储提供的容错机制,GFS只支持m-到-1的队列。多个消费者可以同时读取一个文件,但是它们输入流的区间必须是对齐的。
9. 结束语
Google文件系统展示了一个使用普通硬件支持大规模数据处理的系统的特质。虽然一些设计要点都是针对我们的特殊的需要定制的,但是还是有很多特性适用于类似规模的和成本的数据处理任务。
首先,我们根据我们当前的和可预期的将来的应用规模和技术环境来评估传统的 文件系统的特性。我们的评估结果将我们引导到一个使用完全不同于传统的设计思路上。根据我们的设计思路,我们认为组件失效是常态而不是异常,针对采用追加 方式(有可能是并发追加)写入、然后再读取(通常序列化读取)的大文件进行优化,以及扩展标准文件系统接口、放松接口限制来改进整个系统。
我们系统通过持续监控,复制关键数据,快速和自动恢复提供灾难冗余。 Chunk复制使得我们可以对Chunk服务器的失效进行容错。高频率的组件失效要求系统具备在线修复机制,能够周期性的、透明的修复损坏的数据,也能够 第一时间重新建立丢失的副本。此外,我们使用Checksum在磁盘或者IDE子系统级别检测数据损坏,在这样磁盘数量惊人的大系统中,损坏率是相当高 的。
我们的设计保证了在有大量的并发读写操作时能够提供很高的合计吞吐量。我们通过分离控制流和数据流来实现这个目标,控制流在Master服务器处理,而数据流在Chunk服务器和客户端处理。当一般的操作涉及到Master服务器时,由于GFS选择的Chunk尺寸较大(alex注:从而减小了元数据的大小),以及通过Chunk Lease将控制权限移交给主副本,这些措施将Master服务器的负担降到最低。这使得一个简单、中心的Master不会成为成为瓶颈。我们相信我们对网络协议栈的优化可以提升当前对于每客户端的写入吞吐量限制。
GFS成功的实现了我们对存储的需求,在Google内部,无论是作为研究和开发的存储平台,还是作为生产系统的数据处理平台,都得到了广泛的应用。它是我们持续创新和处理整个WEB范围内的难题的一个重要工具。
致谢
We wish to thankt he following people for their contributions to the system or the paper. Brain Bershad (our shepherd) and the anonymous reviewers gave us valuable comments and suggestions. Anurag Acharya, Jeff Dean, and David des-Jardins contributed to the early design. Fay Chang worked on comparison of replicas across chunkservers. Guy Edjlali worked on storage quota. Markus Gutschke worked on a testing frameworkan d security enhancements. David
Kramer worked on performance enhancements. Fay Chang, Urs Hoelzle, Max Ibel, Sharon Perl, Rob Pike, and Debby Wallach commented on earlier drafts of the paper. Many of our colleagues at Google bravely trusted their data to a new file system and gave us useful feedback. Yoshka helped with early testing.
参考
[1] Thomas Anderson, Michael Dahlin, Jeanna Neefe, David Patterson, Drew Roselli, and Randolph Wang. Serverless networkfil e systems. In Proceedings of the 15th ACM Symposium on Operating System Principles, pages 109–126, Copper Mountain Resort, Colorado, December 1995.
[2] Remzi H. Arpaci-Dusseau, Eric Anderson, Noah Treuhaft, David E. Culler, Joseph M. Hellerstein, David Patterson, and Kathy Yelick. Cluster I/O with River: Making the fast case common. In Proceedings of the Sixth Workshop on Input/Output in Parallel and Distributed Systems (IOPADS ’99), pages 10–22, Atlanta, Georgia, May 1999.
[3] Luis-Felipe Cabrera and Darrell D. E. Long. Swift: Using distributed disks triping to provide high I/O data rates. Computer Systems, 4(4):405–436, 1991.
[4] Garth A. Gibson, David F. Nagle, Khalil Amiri, Jeff Butler, Fay W. Chang, Howard Gobioff, Charles Hardin, ErikR iedel, David Rochberg, and Jim Zelenka. A cost-effective, high-bandwidth storage architecture. In Proceedings of the 8th Architectural Support for Programming Languages and Operating Systems, pages 92–103, San Jose, California, October 1998.
[5] John Howard, Michael Kazar, Sherri Menees, David Nichols, Mahadev Satyanarayanan, Robert Sidebotham, and Michael West. Scale and performance in a distributed file system. ACM Transactions on Computer Systems, 6(1):51–81, February 1988.
[7] Barbara Liskov, Sanjay Ghemawat, Robert Gruber, Paul Johnson, Liuba Shrira, and Michael Williams. Replication in the Harp file system. In 13th Symposium on Operating System Principles, pages 226–238, Pacific Grove, CA, October 1991.
[9] David A. Patterson, Garth A. Gibson, and Randy H. Katz. A case for redundant arrays of inexpensive disks (RAID). In Proceedings of the 1988 ACM SIGMOD International Conference on Management of Data, pages 109–116, Chicago, Illinois, September 1988.
[10] FrankS chmuck and Roger Haskin. GPFS: A shared-diskfi le system for large computing clusters. In Proceedings of the First USENIX Conference on File and Storage Technologies, pages 231–244, Monterey, California, January 2002.
[11] Steven R. Soltis, Thomas M. Ruwart, and Matthew T.O’Keefe. The Gobal File System. In Proceedings of the Fifth NASA Goddard Space Flight Center Conference on Mass Storage Systems and Technologies, College Park, Maryland, September 1996.
[12] Chandramohan A. Thekkath, Timothy Mann, and Edward K. Lee. Frangipani: A scalable distributed file system. In Proceedings of the 16th ACM Symposium on Operating System Principles, pages 224–237, Saint-Malo, France, October 1997