paper: https://static.googleusercontent.com/media/research.google.com/en//archive/gfs-sosp2003.pdf

GFS 是 Google 设计的一个用于大规模数据密集型应用的、可扩展的分布式文件系统,部署在普通硬件设备上,并且能够为用户提供出色的性能和灾难冗余能力。虽然 Google 并没有将 GFS 开源,但是却在 2004 年发布了一篇论文公布了其一些技术细节,影响了以后的诸多分布式文件系统。

本文是对原论文要点的记录。

设计假设

GFS 与传统文件系统共享某些相同的设计目标,如性能、可伸缩性、可靠性以及可用性。然而,GFS 的设计人员审视了他们的应用环境以及使用场景,并基于此给为 GFS 提供了某些不同的设计假设:

  1. 组件失效是常态事件,而不是意外事件。
    • GFS 部署在几百甚至几千台普通的廉价商业设备上。因此在任何时刻都有可能有某些硬件无法访问或工作异常。因此 GFS 从设计初期考虑到这一点,以将持续监控、错误检测、灾难冗余和自动恢复集成到 GFS 中为目标。
  2. 巨大的文件。
    • 存储在 GFS 中文件通常是巨大的,数 GB 的文件很普遍。因此,需要重新审视分布式文件系统中的某些设计决策,如 I/O 操作和 block 的大小等。
  3. 大部分修改的文件操作以追加写入而不是随机写入的方式。
    • 用户通常对文件进行追加写入,然后大量的顺序读。而不是频繁的随机读写。因此,GFS 特别为追加写入而优化,并保证其是原子性的。
  4. 应用程序和 GFS 协同设计。
    • 通过放宽对一致性的要求,采用弱一致性模型,大大简化了 GFS 的设计。引入了原子性的追加写入操作,不需要额外的同步设计。

设计概述

接口

虽然 GFS 提供了一套与传统文件系统类似的 API 接口,但并非完全符合 POSIX 标准。GFS 支持如创建新文件、删除文件、打开文件、关闭文件、读写文件、快照等操作。

架构

GFS 包含一个单独的 master 节点,以及多个 Chunk 服务器节点。整体架构如图:

figure

具体上来说:

  • GFS 储存的文件都被分成固定大小的 Chunk,并且被复制在多个 Chunk 服务器上。在一个 Chunk 被创建的时候,master 会分给 chunk 一个全局唯一的标识。默认情况下,一个 Chunk 拥有三个储存复制节点。
  • master 节点拥有所有的元数据,如:名字空间、访问控制信息、文件和 Chunk 的映射,Chunk 和位置的映射等。master 还管理系统范围内的活动,如 Chunk lease 管理、垃圾回收、Chunk 迁移等。master 会发送心跳包定期和 Chunk 服务器交互以知晓其状态。
  • 客户端只和 master 交互获得元数据,然后再根据元数据和 Chunk 服务器直接交互。

单一 master

单一 master 简化了整个系统的设计,但在另一种意义上,也很容易成为整个系统的瓶颈。因此必须尽量减少对 master 节点的读写。因此,客户端不从 master 节点获取数据。而是告诉 master 要读取的文件和区域,然后 master 返回这个区域所在的 chunk 服务器的位置。由客户端自己联系 Chunk 服务器获取数据。

Chunk 尺寸

Chunk 的大小是设计要素之一。 GFS 选择一个 Chunk 为 64 MB,远大于常规文件系统的 block size。选取一个大的 Chunk 大小有如下优点:

  1. 减少了客户端和 master 的通信需求。从而降低 master 的工作负载。
  2. 采用较大的尺寸,可以对一个块进行更多的操作。通过与 Chunk 服务器保持长时间 tcp 链接,从而降低网络负载。
  3. 减少了 master 需要保存在内存中的元数据信息。

元数据

master 储存三种类型元数据:

  1. 文件和 Chunk 的命名空间。
  2. 文件和 Chunk 的映射关系。
  3. 每个 Chunk 副本的存放地点。

前两种数据会定期被写入日志文件中,并被同步到远程服务器。当 master 发生崩溃时,就可以借助这些备份以恢复服务。第三种数据由 master 启动或运行时,向每个 Chunk 服务器轮询获取信息。

一致性模型

定义:

  • 一致:如果所有客户端无论从哪个副本读,读到的数据都是一样的,那么就认为这个数据是一致的。
  • 已定义:修改文件之后,相关数据是一致的,并且客户端能够读取到它刚才修改的内容,那么相关数据是已定义的。已定义暗含了一致。

GFS 提供一个弱一致性模型。对于并发随机写入操作来说,数据是一致的,但是未定义的。对于追加写入操作来说,客户端总是可以读取到一致并且已定义的数据,但是实际储存在各个 Chunk 服务器上的数据可能有部分是不一致的。

GFS 通过以下几点保证数据一致性:

  • 对 Chunk 的所有副本修改操作顺序一致。
  • 使用 Chunk 版本号来检测副本因为服务器宕机而失效。
  • 与服务器定期握手来找到失效的 Chunk 服务器。
  • 使用 Checksum 来校验数据是否损坏。

系统交互

lease

GFS 使用 lease 来保证写入写入操作的一致性。master 会选出 chunk 的一个副本来发放 lease,称为副本的主 Chunk 服务器。这个 lease 有效期为 60s。当过了有效期之后,master 可能会选出一个新副本发放 lease。当有写入请求时,master 返回给客户端所有副本的位置以及哪一个副本持有 lease。客户端发送数据给最近的副本,然后数据沿拓扑向流向所有副本。当所有的副本都受到了数据后,客户端发送消息给主 Chunk 服务器。主 Chunk 服务器为收到的所有请求分配连续的序号,这些请求可能来自不同的客户端。它按照这个顺序将改变应用于自己的本地磁盘中,然后将顺序推送给所有从服务器。

figure

所有从 Chunk 服务器返回成功的消息给主 Chunk 服务器之后,主 Chunk 服务器才向客户端返回成功的消息。

如果有任何从 Chunk 服务器执行失败,主 Chunk 服务器会将这个错误报告给客户端,由客户端来重新发起请求。

数据流

数据流与写入操作的控制流程是分离的。在客户端收到 master 发送的位置信息后,它从这些位置中选出一个最近的 Chunk 服务器发送数据。然后数据沿着 Chunk 服务器顺序链发送给所有需要的 Chunk 服务器。

原子追加记录

GFS 保证追加记录的原子性。当存在多个并发的追加记录时,GFS 对于每个追加记录至少有一次是写入成功的。GFS 指定写入的偏移量并且在之后返回。

追加记录在之前描述的控制流程之上加了一些额外的步骤。对于一次指定的追加记录来说,主 Chunk 服务器会检查给定 Chunk 的大小。如果追加记录使得这个 Chunk 的大小超过限制,服务器会填充这个 Chunk 到最大大小然后指示客户端发起请求写入下一个 Chunk。

如果追加记录在任意一个 Chunk 服务器失败了,客户端需要进行重新操作。重新操作的结果是,同一个 Chunk 的副本可能拥有不一致的记录。GFS 不保证所有副本在字节序上是完全一致的,但至少保证有一次成功的写入。

因此,追加记录将使数据是一致的,但可能包含部分不一致的局部数据,如填充和错误。

快照

写时复制。master 维持一个对所有 chunk 服务器的引用计数。当客户端发起一次快照请求时,并不实际进行复制,而是将相应服务器的引用计数加一。当对相应服务器发起写入请求时,master 注意到它的引用计数大于一,然后才复制一个新的 chunk,并指示客户端写入这个新 chunk。引用计数减一。

master 节点操作

名称空间管理和锁

GFS 的名称空间是一个全局路径和元数据映射关系的查找表。这个表使用前缀压缩储存在内存中。在名称空间的属性结构上,每个节点都是都有一个关联的读写锁。

每个 master 节点的操作开始之前,都需要获得其路径上所有节点的读锁,以及最终节点的读写锁。

重新复制、负载均衡

当 master 节点创建一个新的 chunk 时,它会选择放置初始空间的位置,会考虑几个因素:

  • 在低于平均硬盘使用率的 Chunk 服务器上创建副本以平衡硬盘使用率。
  • 限制每个 Chunk 服务器最近的 Chunk 创建次数。因为创建操作通常意味着之后又大量的写入操作。
  • 为了容错性,使 Chunk 分布在多个机架。

当 Chunk 的有效副本数少于用户指定的复制数量时,master 节点会重新复制它。
同时,master 节点会周期性地进行负载均衡,检查当前副本的分布情况,然后移动副本以获得更好的空间利用率。

垃圾回收

GFS 在文件删除之后并不会立刻回收可用的物理空间。而是采用惰性回收的策略。
当删除一个文件时,只是把文件重命名为一个特殊的文件名以使其不可见,在实际删除之前,仍然可以恢复。

失效检测

master 节点维持有所有 Chunk 的版本号。客户机或 Chunk 服务器在执行操作时都会验证版本号。
master 在例行的垃圾回收工作中移除所有失效的副本。

容错

master 节点的复制

为了保证 master 节点的可靠性,master 服务器的状态也要复制。master 服务器的所有操作日志和 checkpoint 会被复制到多台远程机器上。
此外,GFS 中还有”影子” master 服务器,这些影子服务器在 master 宕机时提供只读访问。它们的数据通常比 master 更新得慢一点。

数据完整性

每个 Chunk 服务器使用 Checksum 来检查保存的数据是否损坏。对于读操作来说,在把数据返回给客户端或者其它的 Chunk 服务器之前,Chunk 服务器会校验读取操作涉及的范围内的块的 Checksum,因此 Chunk 服务器不会把错误数据传递到其它机器上。如果某个 Checksum 不正确,Chunk 服务器返回给请求者错误信息并通知 master,master 会重新复制副本以替代错误版本。