GFS Part I

Introduction

  1. 错误是常见的,所以需要做到:持续监控,错误检测,容错,自动恢复等机制。
  2. 要考虑到文件会很大,IO 操作和 Block Size 都要重新设计。
  3. 大多数文件是追加写入而非覆盖写入,写完之后通常是只读并且按顺序读,基于以上角度考虑性能优化和原子操作。
  4. 应用和 API 协同操作,放松了 GFS 的一致性要求,使用原子性追加。

设计概览

预期

主要对以上4点基本概要的细节展开描述,其中阐述了工作负载的主要来源:大规模的流式读和小规模的随机读

接口

GFS 实现了一套与传统相似的文件系统接口,但并没有实现 POSIX 那样完全标准的 API。文件以分层目录的形式组织,用路径名来标识。除了基操之外,还提供快照和追加记录。

架构

一个 GFS 集群包含一个单独的 Master 节点、多台 ChunkServers,并且同时被多个客户端访问,

GFS架构

文件被划分到固定大小的 Chunk 中,不同的 Chunk 由 Master 赋予的 Chunk handle 来区分,ChunkServer 以文件的形式把 Chunks 存储在本地磁盘中,并且根据 Chunk handle 和字节范围来读写 Chunk。每个 Chunk 都在不同的 ChunkServer 中被复制,一般有3个副本。

Master 管理着所有文件的元数据,并且控制着系统级别的活动,例如:租用 Chunk,Chunk 回收,Chunk 迁移等。Master 通过心跳和 Chunk Server 进行通信。

客户端代码实现了 API 接口、 应用与 Master 和 ChunkServer 通讯、以及对数据进行读写操作。客户端和 Master 的通信只获取元数据,所有的数据操作都由客户端直接和 ChunkServer 进行交互。

单 Master

Master 只负责决策,将任务分配给 Chunk ,而不负责读写。

首先,在第一次通讯过程中,客户端把文件名和程序指定的字节偏移,根据固定的 Chunk 大小,计算 Chunk 索引。然后把文件名和 Chunk 索引发送给 Master 。Master 将相应的 Chunk handle和位置信息返回给客户端,客户端将其进行缓存。最后,客户端访问最近的 Chunk 副本,发送请求。避免了多次与 Master 通讯。

Chunk Size

GFS选择了 64MB 作为 Chunk Size,这是一个相对较大的尺寸,优点如下:

  1. 减少了访问 Master 以获取 Chunk 位置的通讯
  2. 对于较大的数据量,客户端可以缓存较少的 Chunk 位置信息
  3. 客户端连续对同一个 Chunk 进行操作时,只需要建立一次 TCP 连接
  4. 减少了 Master 存储的元数据量,以便全部放在内存中 缺点:

许多客户端对同一个小文件进行多次访问时,会产生热点问题,这个小文件对应的一个或几个(总之很少)的 Chunk Server 会被打爆,所以允许客户端从其他客户端访问数据。

元数据

  • 文件和 Chunk 的命名空间
  • 文件和 Chunk 的对应关系
  • 每个 Chunk 副本的存放地点

均存储在内存中,前两个的变更日志存储在磁盘上,并且复制到其他 Master 服务器,以保持简单的同步。Chunk 副本存放地点不会持久化存储,每次启动 Master 时向各 Chunk Server 轮询即可。

=> Q: 存储在内存中会不会内存不够?

=> A: Master 服务器只需要不到 64 个字 节的元数据就能够管理一个 64MB 的 Chunk。所以对内存的消耗是很少的,但是却能极大地提升性能,所以存储在内存中。

=> Q: 为什么不持久化存储 Chunk 副本存放地点?

=> A: 因为 Chunk Server 的各种情况会导致数据不同步,所以干脆不存了,通过心跳监控 Chunk Server的状态。

=> Q: 错误恢复?

=> A: Checkpoint(快照),重演操作日志。

一致性

「一致」:指从所有客户端,从各个副本读取到的文件内容一致 「已定义」:读取到的文件内容一致,并且能看到所有写入操作

系统交互

原则:最小化所有操作和 Master 节点的交互

Leases & Mutation 顺序

Mutation 是指改变 Chunk 内容或元数据的操作,比如写入或追加。

Mutation 在 Chunk 的所有副本上进行,我们通过 Leases 保证 Mutation 顺序一致性。

Master 为主 Chunk 建立一个 Lease,主 Chunk 对 Chunk 的所有更改操作进行序列化,所有副本都遵从这个序列进行修改操作。

Lease 机制减少了 Master 的负担,一般以 60s 为超时,如果 Chunk 变化,可以刷新超时时间,如果 Chunk 失联,则 Master 会重新安排主 Chunk 发放 Lease。

Lease

  1. 客户端询问 Master 谁是当前 Lease 持有者及其他副本位置,如果没有 Lease,则 Master 安排一个
  2. Master 返回主 Chunk (Primary Replica)标识及其他副本(Secondary Replica),客户端进行缓存,接下来,只有当主 Chunk 挂掉的时候或者不再持有 Lease 时,客户端才会再去访问 Master
  3. 客户端把数据无序地推送给所有副本
  4. 当所有副本都收到数据后,客户端将写请求发给主 Chunk,主 Chunk 为所有请求安排序列号,并且在本地按顺序执行这些请求。
  5. 主 Chunk 把写请求发给其他副本。其他副本依照主 Chunk 分配的序列号以相同的顺序执行请求。
  6. 其他副本完成操作后,告诉主 Chunk。
  7. 主 Chunk 把结果返回客户端。

数据流

由于数据流的网络传输负载非常高,通过分离数据流和控制流,我们可以基于网络拓扑情况,利用每台机器的带宽,避免网络瓶颈和高延时连接,对数据流进行规划,提高系统性能。

  • 控制流:客户端 => 主 Chunk => 其他副本
  • 数据流:在管道中沿着设计过的 Chunk 链路推送,这个链路是每台机器选择离自己最近的没有收到数据的机器推送,通过 IP 地址计算距离。(这里看起来还蛮简单的,但是我是个网络菜鸡,不知道有没有坑)

原子的追加记录

GFS提供的原子的追加记录操作叫做:记录追加。

原始的追加:客户端提供偏移量,多个服务器并行写入,导致 region 尾部出现不同客户端写入的数据。 记录追加:客户端只提供数据,GFS 保证至少有一次原子的写入操作成功执行,将数据写入到自己指定的偏移量上,之后将偏移量返回给客户端。

记录追加保证一组数据作为一个整体至少被写入一次。

快照

copy-on-write

后记

我的妈呀,英文Paper读起来太硬核了,我连蒙带猜读了一半都读了好久…还有一半要读到猴年马月,这tm的,这周还轮到轮到我OnCall,被吴抓去trace脏数据,嘤嘤嘤。

Table of Contents