深度解析GFS

本文通过对《The Google File System》的阅读,来了解GFS的设计、实现和应用,以及设计上的缺陷。

1.简介

GFS(The Google File System),一个面向大规模分布式数据密集型应用、弹性的分布式文件系统。作为谷歌三驾马车之一(2003年的GFS、2004年的MapReduce、2006年的Bigtable),对分布式系统的工程实现具有开创性的先驱意义。

GFS的设计与传统的文件系统有四点不同:

  1. 组件异常被认为是常态而不是异常。GFS构建在成百上千的廉价机之上,各个组件无论是硬盘、内存、网络等出问题是非常常见的,GFS会持续监控,识别出错误,进行容灾设计和异常自动恢复机制。
  1. 巨大的文件,GB、TB级别的存储,且数据增长速度极快。
  1. 大多数的文件是使用追加式写入而不是覆盖式写入,随机写基本是不存在的,大部分的读取也是顺序读取,同时也支持小部分的随机读。
  1. 应用程序和文件系统API协同进行设计,极大的提高了整个系统的灵活性。传统的文件系统和应用基本是独立的设计,导致各自的能力受到了限制。

2.设计

2.1 预期

  1. 实时监控到错误,并快速的进行恢复(组件失效是常态)。
  1. 由大量的大文件(100MB+)构成和小规模的小文件。
  1. 支持大规模的顺序读和小规模的随机读。
  1. 支持大规模的顺序写和小规模的随机写。
  1. 高效并且语义明确的支持数据的并发写入。
  1. 高性能的稳定带宽比低延迟更重要。

2.2 接口

类似传统文件系统的函数,但是并不严格遵守POSIX等标准API。文件是以目录树+文件的形式组成。支持常用的操作,例如创建文件、删除文件、打开文件、关闭文件、读/写文件。

同时也提供了快照和追加操作。快照(snapshort)可以快速且低成本创建文件或目录的拷贝,记录追加(record append)操作则保证多客户端并发操作是原子的。

2.3 架构

gfs架构图

  • Client

    • GFS被多个应用访问,应用使用GFS提供的客户端sdk
    • 实现了文件访问的接口,但不是POSIX API
    • client与Master进行元数据的访问,与ChunkServer进行文件数据的读写
    • 缓存metadata信息(减少和master的交互),不缓存文件信息(数据量巨大,缓存成本大且没意义)
  • ChunkServer

    • 多个ChunkServer,用于数据存储,每个ChunkServer有多个Chunk
    • 不缓存文件信息(底层的linux系统已经有一层buffer了)
  • Chunk

    • 文件被分割为固定大小的Chunk(64MB)
    • 每个Chunk有一个唯一的不可变标识:chunk handle, 在chunk创建时由master分配。
    • 为了稳定性,Chunk都会有副本分布在多个ChunkServer上,默认是三副本(支持在file 级定制副本数)

2.4 master 设计

从上图中可以看到,GFS是单机的master节点,设计简单,但是也需要一些机制保障单机不成为瓶颈,比如数据交互直接从client到chunk server,也有shadow master的存在,可以提供只读访问,缓解单机master的读取压力,并且在master挂掉时,可以做容错切换操作。

master的存储了三个非常重要的metadata ( 元数据 ) ,元数据都会在matser的内存中

  1. 文件命名空间(file namespace,目录树结构);nv;
  1. 文件到Chunk的映射(chunk mapping);nv;
  1. Chunk到ChunkServer的映射(chunk location);v;会在 master启动或有新的chunk server加入集群时进行重建

其中1和2是nv(non-volatile,非易失),3是v(volatile,易失)。

为什么GFS不持久化chunk的坐标信息?

  1. matser在心跳中进行chunk server信息的确定,这个开销很低。
  1. 最主要的原因:集群的变更是非常频繁的,如果做持久化,很难保障持久化的内容和真实的集群状态保持一致。

为了保证文件命名空间和chunk mapping的持久化,gfs使用了操作日志(operation log)来实现metadata的持久化,操作日志必须先进行持久化(同时包括本机磁盘和远程多个shadow matser的磁盘),再去响应客户端,并且在持久化完成后,元数据的变化才对客户端是可见的,这也是经典的WAF(write ahead log)语义,保证数据不丢失。master也会将日志批收集后再处理,减少写入磁盘和复制到远程GFS整体性能影响。

master通过回放操作日志来恢复文件系统的状态,为了减少启动的时间,master选择的是:全量备份+增量日志。在日志增长到一定量级时会对系统状态做一次checkpoint,将所有的状态保存到checkpoint文件中(check point文件会以b树的形式压缩),在恢复状态时使用checkpoint文件直接初始化,再回放checkpoint之后操作日志就完成了。

元数据全部在内存中,可以方便的实现全部数据的扫描,扫描全部meta有以下三点好处:

  • 回收垃圾chunk
  • 在chunk server失效时重新进行复制
  • 迁移chunk来平衡多个chunk server的磁盘开销和服务器负载(比如有两个chunk server,一个服务负载特别高但是磁盘使用少,一个服务负载特别低但是磁盘使用很多,就可以对这两个chunk server进行负载均衡)
  • matser也具有一些系统级的功能,如下列表,我们会在后边的内容中详细介绍。
    • Chunk租约管理 (chunk lease)
    • 垃圾Chunk回收(garbage collect)
    • Chunk跨ChunkServer迁移(chunk migration)
    • 与每一个ChunkServer有心跳消息来指令的传递和状态的收集 (HeartBeat)

2.5 文件读取

避免因为单机的设计导致的瓶颈,client不会通过master来读取数据,client会从master拿到ChunkServer信息,然后直接跟ChunkServer交互,同时也会缓存这些metadata,一个典型的读取流程如下:
  1. Client确定想要读的文件和偏移量
  1. 因为GFS使用固定大小的Chunk,所以可以在客户端上就计算出要从当前文件的第几个chunk开始读(chunk index)
  1. Client将文件名和chunk index发送给master,master将chunk handle和数据副本的位置返回给Client,Client使用文件名+chunk index作为key来缓存这些信息,同时因为实际情况下基本是顺序读取多个chunk的场景,所以为了避免Client和master的多次交互,Client一般会一次请求多个连续的chunk信息。
  1. Client选择一个最近的副本节点进行数据的读取。

2.6 一致性模型

metadata的一致性会有锁来保证,我们在后文介绍。 gfs对文件数据的写操作有下述的一致性模型,可以看到gfs并不是一个强一致的模型,保证了弱一致性,其中的错误通过应用程序解决。 | | 指定位置写 | 记录追加写 | | ---------------- | ------------------- | -------- | | 串行成功 | 已定义(defined) | 已定义部分不一致 | | 并发(comcurrent)成功 | 一致(consistent)但是未定义 | | | 失败 | 不一致 | | 先解释下defined和consistent的概念。
  • consistent,所有的客户端无论从哪个副本读取文件,都可以看到相同的数据
  • defined ,有多次客户端写入操作,在consistent的基础上,客户端还可以看到所有写入后的数据
数据修改操作分为两种操作
  • 写入,把数据写到客户端你指定的文件偏移上。
  • 记录追加,gfs的记录追加写操作保证的语义是原子性的至少写一次 (atomically at least once), 但是记录之间可能有一些padding,这些padding是不一致的区域。
GFS使用三种方式保证副本间的数据一致:
  1. 多副本按照变更顺序执行数据修改操作
  1. chunk的版本号机制
  1. 定时握手携带的checksum信息

3.系统交互

整个gfs系统的交互中最重要的一点就是最小化master节点的参与。接下来让我们看一下客户端、master、chunkserver对数据写入、原子记录追加和快照的实现。

3.1 租约和变更顺序

数据的变更需要在chunk的所有副本上都执行,我们使用租约机制来保证多个副本的数据变更的一致性。Master节点为其中一个副本创建一个租约,我们把这个副本叫做*primary*,由primary对chunk的更改进行顺序化。所有的副本都遵循这个顺序进行更改操作。 租约的默认超时时间是60s,不过只要chunk被修改了,primary就可以继续申请租期。这个租约申请也会放到master和chunk serve的心跳消息中。 下图是gfs的数据写入流程,可以看到数据写入流程中,也有设计非常好的数据流和控制流分离操作。 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d2c32d56a564bb09e39f8f8b1e03d17~tplv-k3u1fbpfcp-watermark.image?) 1. client询问master哪个副本持有租约以及其他副本的位置信息。
  1. master将parmary和所有secondary的信息告诉client,client将这些信息写入自己的本地缓存。
  1. client开始推送数据,chunk server收到数据后会把这些数据保存到自己的LRU缓存上,client会先把数据推到离自己最近的且没有接收到数据的chunk server上,这个chunk server在接收数据时也会继续把数据推到下一个更近的节点,直到全部节点收到数据,这样gfs采用链式的方式进行数据的推送,由于我们的网络是全双工网络,"推“不影响“收”,在理想情况下,传送B字节的数据到R个副本的理想时间是B/T + RL,T是网络的吞吐量,L是两台机器间数据传输的延迟。
  1. 所有节点都收到数据后,client会发送写请求给primary,primary会将之前的一系列写操作进行序列化。
  1. primary将变更序号广播到所有的Secondary上。
  1. 所有的secondary告诉primary,他们执行完了写入操作。
  1. primary将写入结果(成功or失败)告诉客户端,如果失败了,客户端会进行重试,重新进行 3 ~ 7的操作,这也就导致了部分的不一致(at least once语义)。
在这过程中如果有client去并发操作,由于有写入序号的存在,系统整体还是一致的,但是客户端可能会看到其他客户端的写入,所以是未定义的。

3.2 快照

gfs使用快照(snapshot)技术来快速的进行文件/目录的复制或备份。gfs使用cow(写时复制 copy on write)技术来实现快照操作,当master节点收到快照请求后:
  1. 取消要快照的文件的租约(用来控制写操作,取消后续的写入)。
  1. 将操作记录写入log落盘。
  1. 复制一份metadata,新的快照文件和源文件指向相同的chunk。
  1. 如果master后续收到修改操作,会先检查chunk的引用计数,如果发现计数大于1,master会让全部的持有改chunk的chunk server创建一个新的chunk,再修改文件的指针指向。

4.Master的操作

4.1 命名空间和锁

gfs使用命名空间上的区域锁来保证操作的正确性。在逻辑上,gfs的命名空间就是一个文件path到metadata的映射,使用前缀压缩,gfs可以把这个映射都放到内存中,在命名空间这个树形结构上,每个节点都会有一个关联的读写锁。 master在操作前都要按层级顺序从前往后获取一系列的锁,比如这个例子,假设一个操作要操作`/d1/d2/.../dn/leaf`,在操作前要申请`/d1`、`/d1/d2`、`/d1/d2/...dn`的读锁和`/d1/d2/.../dn/leaf`的写锁。

4.2 副本

副本演进是有一个过程的,多机副本 -> 多机架副本 -> 异地多机房副本,每提升一个层级都会提升整体系统的可靠性,gfs当时主要设计点是多机架副本。gfs的副本原则就是可用性、可靠性和带宽利用率。 当master节点创建一个chunk时,会有以下三点考虑来选择将副本放到哪里
  1. 选择磁盘利用率低的chunk server
  1. 选择最近一段时间内没有创建过chunk的chunk server(因为创建chunk也就意味着数据写入)
  1. 将副本分布到多个机架上。
多个chunk的复制操作也会有优先级,如下:
  1. 现有副本数与复制因子差值越大优先级越高(丢失两个副本的chunk要比丢失一个副本的chunk优先级高)
  1. 优先复制活跃文件的chunk,而不是复制最近删除过文件的chunk。
  1. 优先复制阻塞client操作的chunk副本。
当一个chunk的有效副本个数少于复制因子时,gfs会进行副本复制操作,gfs也会周期性的进行负载均衡,主要是平衡磁盘利用率。

4.3 失效副本

当chunk server失效时,chunk也有可能错过一些修改操作而失效(数据不一致),master保存了每个chunk的版本号,用来识别当前副本和过期副本。 只要master和chunk签订新的租约,就会增加chunk的版本号,然后通知其他的副本。Master节点和这些副本都把版本号做持久化,master和副本交互时就会检查版本号,如果看到更高的版本号,低版本的副本都会被标记为失效,在后续的垃圾回收时,会被回收掉。

4.4 垃圾回收

gfs在删除文件/chunk时,不会立刻删除,而是会被垃圾收集器进行周期性的懒删除。 对于文件会将文件标记为隐藏文件并打上删除的时间戳,在master扫描命名空间时,会删除过老的隐藏文件。在对chunk server做扫描时,master会找到孤儿chunk(不被任何文件引用),并进行删除操作。 垃圾回收比直接删除有以下几点好处:
  1. 更可靠,比如直接删除操作如果失败了还需要做一些重试。
  1. 将垃圾回收操作合并到规律性的活动中,例如握手和命名空间扫描,所以操作可以批量、打散的执行,降低系统开销和负载。
  1. 提供了恢复机制。
不过也会导致磁盘的占用率有一些提升,gfs也允许对隐藏的文件进行删除操作,实现实时的文件删除。

5.容错

5.1 高可用

  1. master和chunkserver可以快速重启。
  1. chunk副本,默认是3,并且副本分布到多个机架上。
  1. 多机备份master的数据,在master挂掉时,会由守护进程在另外一台有master完整数据的机器上重新拉起一个master。
  1. 提供shadow master,作为master挂掉时的只读副本。

5.2 完整性校验

每个chunk(64mb)分成多个64kb的块,每个块都有一个32位的checksum。对于写操作,在写完后会同时把checksum值写入。对于读操作,在client从chunk server读取数据时,会去校验checksum,如果不正确,会向master报告,并且从其他副本读取

总结

GFS在当年是一个划时代的存在,GFS和构建在GFS之上的Bigtable、以及计算框架MapReduce构成了谷歌的三驾马车,也奠定了分布式系统工业实现的基础,为大数据的发展提供了基础。不过GFS也有一些时代的局限性,最大的问题就是他的单Master设计,足够简洁但也带来了一些问题。 参考文档:
  1. 《The Google File System》:https://pdos.csail.mit.edu/6.824/papers/gfs.pdf
  1. 分布式文件系统架构对比: https://juicefs.com/zh-cn/blog/engineering/distributed-filesystem-comparison
  1. 【MIT 6.824】学习笔记GFS:https://zhuanlan.zhihu.com/p/354450124
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值