《The Google File System》读后体会

该文章是由 Google 的工程师 Sanjay G.Howard GShun-Tak L. 共同撰写,并于 2003 年 在 SOSP 上发表。他们发明的这个 GFS 文件系统现在已部署在 Google 的服务器上,并得到很好的使用。

GFS 的需求背景包括以下几点:

  • 存储部件的失效已经成为常态而不是意外事件
  • 文件体积已达到比传统的标准大得多的级别,TB级的数据流量已经是家常便饭
  • 大多数文件变更更多的是添加新数据,而不是覆写已有数据
  • 协同设计的应用程序和文件系统 API 通过提高可扩展性,对于整个系统是有益的

同时,GFS 的设计也包括了以下几个主要原则:

  • 系统是建立在大量廉价并容易失效部件上,并且必须能自监控
  • 系统主要存储大文件(GB 级)
  • 工作负荷主要是由大量的流读取和少量的随机读取组成
  • 工作负荷同时也包括大量的连续写,并且是写附加数据,而不是覆写数据
  • 支持生产者-消费者的资源共享模型
  • 高带宽比低延迟更重要,响应时间的需求并不是最主要
  • GFS 提供了常用的文件操作,包括 createdeleteopenclosereadwrite ,另外还包括附加的 snapshotrecord append 等操作。

    Snapshot 是创建文件和目录树结构的映像。

    Record append 则允许多 client 同时对同一个文件进行操作,并进行互斥和操作顺序化,保证每一个 client 的操作都是原子操作。

  • GFS 包括单个 Master 服务器,多个 ChunkServer 服务器以及多个访问的 Client,其访问流程如图所示:

    GFS 中,Master 只是充当一个中心调度的协调者角色,每次 Client 要存取文件,先与 Master 进行沟通,获取到该文件所存储的 chunk 块的存放位置(在哪个 chunkserver 上),然后 Client 就可以直接与 Chunkserver 进行通讯和大流量的文件存取,此时 Master 就已完成任务,不再介入后续的文件操作。

    在GFS中每个文件是被分成一个或多个固定大小为 64MBChunk 数据块,而每个 Chunk 块又由一个 64bitchunk handle 来唯一标识。每个 chunk 块都根据用户设定的冗余数在不同的 chunkserver 上构建冗余备份,缺省是 3 份冗余。大容量的 Chunk 块可以减少 Master 对每一次文件访问的 Chunk 块定位的开销,因为 GFS 设计是针对大量的连续访问,所以只要定位一次,后续的访问都是连续的,不需要再进行定位,降低了 Master 作为一个单点服务的瓶颈效应。

    Master 服务器主要维护命名空间(Namespace)、访问控制信息、文件与 chunk 块映射信息以及当前的 chunk 块的分布定位信息。Master 周期性地与 ChunkServer 进行通讯,获取最新的 chunk 存储信息,通过一个 HeartBeat Message 进行这种周期更新。

    ClientChunkServer 都不会进行文件数据缓冲,因为 GFS 设计的前提是针对大文件存取,所以缓冲区对于这种级别的文件访问用处并不大。

  • GFSMetadata 包含三部分主要信息:

    • 文件和 chunk 块的命名空间
    • 文件到 chunk 块的映射信息
    • chunk 块每一个冗余备份的定位信息

    所有的 Meta 数据都是存储在 Master 服务器的内存中,所以对 Meta 的访问速度是非常快的。前两种 meta 数据的更改信息会作为日志永久记录在磁盘上,而第三种 meta 数据则不会进行日志记录,而是会通过 masterchunkserver 的周期性沟通获取最新的版本。因为 GFS 假定存储部件是易失效的,所以 chunk 的定位信息应该随时不断更新,而不是长时间地保存不变。

  • GFS 引入了一个租约(Lease)的概念。Master 会定期授权 chunk 块的其中一个 replicas 拥有租约,并且设定一个有效期(初始化为 60 秒)。拥有租约的 replica 成为 primary。每当 Client 要对该 Chunk 进行访问时,就会先询问 Master 哪个 replicaprimary,如果当前没有 primary,则 Master 会立即授权一个,然后返回结果告诉 ClientClient 就会将读写请求发到 primary 上,并将数据(如果是写入)推送到所有 replicas 上,采用链式推送法,保证带宽的合理利用。而所有 replicas 则会将数据暂存在 LRU 缓冲区上(每一个 Chunkserver 都会有)。如果同时有多个 client 对该 chunk 进行访问读写,就会由 primary 来统一进行协调,将所有请求进行排序,并把排序结果发到其他非primayreplicas 上,然后所有 Chunkserver 按照该操作顺序将 LRU 中的缓冲数据真正写入到磁盘的 chunk 块上,保证所有操作在所有 replicas 上都是保持同一顺序的。以上过程如图所示:


  • GFSSnapshot 采用 copy-on-write 技术实现。每当 Master 收到 Snapshot 请求时,Master 首先并不是进行映像的复制操作,而是查询租约的拥有者即 Primary 并通知它,保证 Master 可以获取到后续的所有读写操作的请求。当后续产生写操作时,Master 就会通知所有该 chunkreplicas,将该 chunk 进行复制备份,产生另外一个 chunk 映像,然后将映像地址返回给 client 让其进行读写,而原来的 chunk 就自然形成了一个 snapshot,不会再对其进行修改。

  • GFSMaster 上存储的文件结构是采用命名空间的方式,没有目录内存储的文件列表结构,没有链接或别名机制,每一个命名空间的映射条目都是采用文件的绝对路径存储,并且采用前缀压缩技术(Prefix compression)进行压缩存储。这样可以更高效地进行文件的定位和访问。

  • GFScluster 的分布处于多层次而不只是单一层次。为了避免同一主机甚至同一机房的崩溃(掉电和断网之类),GFS 设计部署时要求不同的 replica 可以备份在不同的片区( Rack)的服务器上。在不同主机上分布只能针对硬盘的级别的失效,不同 rack 的分布则可以避免主机级别的失效。

    GFSreplication 的过程采用低优先级的后台操作,当 chunkserver 的磁盘负荷较低时才进行 replication 操作。而由于这种操作也是会多个同时进行,所以其优先次序是有讲究的,一般按照以下的原则进行排序:

    • 与用户设定的冗余目标数 goal 仍未达到的,相差越多的越优先
    • 正在使用的 live 文件优先(相对于最近被删除掉的)
    • 所涉及的 chunk 正阻塞着客户进程的优先

    当然,GFS 还会对 clone 的流量进行限制,避免影响正常的用户流量。同时,master 还会在 chunkserver 空闲时进行replica 的调整和平衡,做到每一个 chunkserver 的磁盘使用情况都比较均衡。


  • GFS 对被删除的文件是采用一种类似“回收站”的方式进行处理,不是直接对其空间进行释放,而是将其文件名改为一个隐藏的名字(Hidden name),在一定的时间后(缺省为 3 天)才删除其 meta 信息,正式释放其所占用的空间。而对于 Chunk 块则采用更为简单的方法处理:定期进行扫描,凡是不能通过任何文件名进行访问的 chunk 块,就会删除其 meta 信息,然后告知 chunkserver 释放空间。因为所有的文件和 chunk 块都会有 Meta 信息存储在 Master 服务器上,所以删除了 meta 信息,就等于不能通过 master 进行访问,等于释放了空间。

    由于 GC 的过程也会产生流量负荷,所以 GC 的优先级是最低的,以免影响其他业务流量。而其延期删除的机制,则保证了存储的安全性和可靠性,避免了意外性的暴力的删除行为。

    但由于这种 GC 机制会导致一定的存储资源的消耗,不能及时回收,所以对于存储资源紧张的用户来说,并不一定是最优选择。所以 GFS 采用了一种折中策略,可以通过修改冗余数来避免磁盘消耗过多,也可以通过显式的重复删除操作,可以将文件所占用空间立即释放,而不再等待延迟过程。

  • GFS 采用快速恢复机制,不对系统或部件失效的原因进行区分和追查,只会在失效后尽快进行重启,然后恢复所有的通讯连接,重新更新 meta 信息。

    同时作者提到 GFS 单单是采用冗余备份的策略还是不够,还需要探索校验等方式来提高可靠性,保证所存储文件的正确性和完整性。

    对于 Master,其所有操作都会以日志方式被记录下来,并保存在多个备份日志服务器上。当 Master 失效时,可通过这些备份日志服务器对 Master 进行异地恢复。同时 GFS 设计了一个影子系统(Shadow master)的机制,该影子 Master 只是处于只读状态,即使当 Master 失效后它接替任务依然如此。但该系统是进行影子操作,而不仅仅是镜像,它会与 Master 进行同样的操作,而且可以提高一定的读可用性。虽然 Shadow 提供的结果会有一点滞后,但因为具体的文件数据读写是通过 Chunkserver 进行的,所以滞后的只是 meta 数据,并不影响 Client 的操作。当 Master 失效时,Shadow 会自动接替,而客户访问由于只是通过 MasterDNS 别名,所以这种 IP 切换由 DNS 来完成,对于 Client 来说完全透明的。

    GFS 的数据完整性,还包括文件校验。由于网络带宽的有限,文件校验不可能通过拷贝其他 Chunkserver 上的文件冗余备份来进行比对,所以一般校验是子啊本地机器上进行,只有在发现了文件损坏时才进行跨机器的拷贝恢复。GFS 将每一个Chunk 块划分为 64KB 大小的数据块,每 64KB 还附加上 32bit 的校验和数据。如同 meta 一样,校验和也是存储在内存中以便可以快速读取和校验的。而每一次对 Chunk 块的读取都会先进行本地校验,防止读取的数据是受损的。而这种校验由于是本地进行,不涉及网络传输,所以是可以和 I/O 同步进行,不会影响 I/O 的操作。而对于覆写操作,也会先对文件所在chunk 块的第一个 64KB 块和最后一个 64KB 块进行校验,因为一个 64KB 块可能不完全是属于同一文件的,所以首尾的数据块不校验的话,有可能会发现不了属于另外一个文件的数据已经受损。