GFS介绍

The Google File System论文发表于2003年,当时工业界的分布式系统最多也就是几十台服务器的 MPI 集群。而这篇论文一下子拿出了一个运作在1000台服务器以上的分布式文件系统。GFS的目标是借助大规模的集群,实现高并发、高吞吐的文件读写功能,集群规模越大,并发和吞吐能力越强。

徐老师在03和04讲分别介绍了GFS读取和写入的操作流程,它们是理解GFS的基础。

GFS在设计上有四个特点,第一、单Master架构,第二、一个文件由多个64M的大Chunk组成,第三、内存存储Master Metadata,第四、放宽的一致性模型。

可能的性能瓶颈

随着GFS集群规模的扩大,可能的性能瓶颈在哪里?我们很容易想到它的Master节点。单Master的架构符合简单的设计原则,不过它的内存、硬盘、CPU、网络会不会成为集群的性能瓶颈,是我们需要考虑的。GFS对此有没有做什么优化?

通过GFS论文第6节 MEASUREMENTS,我们可以来认识下GFS的实际性能。在一个由227个chunkserver构成的生产集群里,存储了73.7万个文件,总大小为155TB,Master Metadata大小为60M。60M的内存非常小,所以Master节点的内存不会率先成为性能瓶颈,当然如果我们在GFS存储非常多的小文件就另当别论,不过这显然违背了GFS的设计初衷。在GFS中,一个文件大概需要100 bytes记录Metadata信息,一台64G内存的Master服务器可以存储6亿多个文件,假设每个文件256M,单台Master可以管理15PB的数据,这足以满足很多应用场景。由于Metadata信息全部存储在内存,所以硬盘也不会成为性能瓶颈。再来看看CPU,GFS论文第6.2.4节 Master Load提到,Master每秒大概需要处理200-500个请求,这对Master来说毫无压力。

正如徐老师所言,GFS真正的性能瓶颈在于网络。04讲中提到,GFS采用了控制流和数据流分离、流水线式传输文件数据和巧妙的Snapshot操作三种方法,减少了Master节点对所有操作地参与,从而减少了网络流量的传输。

结合业务的设计

从05讲的推荐文章《CAP 理论十二年回顾:“规则”变了》可以看出,在现实的业务场景中,分区(P)并不是常态,即便在分区存在的情况下,一致性(C)和可用性(A)并非不可兼得,但要同时支持C、A、P,意味着增加系统的复杂性。如果想让系统保持简单,就需要在一致性(C)和可用性(A)中取舍,GFS的选择是保证可用性(A)的同时,放宽对一致性(C)的要求。

追加写入是GFS中主要的写操作,我们可以通过这个操作来看放宽的一致性模型。追加写入只保证至少一次的语义,每一次写入都需要等待所有副本节点写入成功,如果在任意一个节点上失败,客户端都会得到写入失败的通知,然后发起重试。GFS并不对之前在部分节点上写入的脏数据做处理,而是直接暴露给了应用程序,让应用程序完成去重和排序的工作。这样虽然增加了应用程序的一点复杂性,但是却让GFS保持了简洁,减少了master和chunkserver,chunkserver和chunkserver之间的通信和协同。

高可用设计

GFS通过Master、Backup Master和Shadow Master实现了Master节点的高可用,通过多副本的Chunk机制保证了Chunkserver节点的高可用。论文第6.2.2节 Metadata提到,通过checkpoint和operation log恢复一个60M大小的Metadata,只需要几秒钟,加上等待chunkserver上报chunk信息的时间,大概30-60秒,Backup Master就能够对外提供服务。在此期间,由Shadow Master提供读取服务。虽然Shadow Master是异步复制Master中的数据,但是一般只比Master慢几分之一秒。

论文第6.2.5节 Recovery Time提到,多个chunkserver挂掉后,GFS会优先恢复存活副本数较少的chunk。在一个实验中,测试人员关闭了2个chunkserver,每个chunkserver包含大概16000个chunk和660GB的数据,这个操作导致266个chunk只拥有一个副本,不过GFS在2分钟内就把它们恢复到了至少2个副本。当时的机器带宽是100Mbps,现在的机器普遍达到了1000Mbps,甚至是10000Mbps,恢复速度会更快。

我在读GFS论文的时候注意到一个细节,当系统在执行snapshot操作时,会把当前还需要写入数据的chunk拷贝一份,让后续的写请求将数据写入拷贝出来的chunk。这样的话就可以减少snapshot操作对写操作的影响,不用等到snapshot操作结束才允许写入数据。

系统交互

GFS在设计系统时,尽可能地减少了master在所有操作中的参与程度。我想补充数据变更顺序(Data Mutation Order)和原子性的追加写入(Atomic Record Append)的一些细节和我的思考。在追加写入时,客户端会请求Master,获悉数据要写入哪一个chunk,以及chunk所在的primary chunkserver。Master会给primary chunkserver分配一个lease,这个lease默认持续1分钟,在这1分钟内,由这个chunkserver决定多个客户端写请求的处理优先级。这1分钟内,如果Master和这个chunkserver失联,Master不会立即分配新的primary chunkserver,因为此时客户端可能还可以和这个chunkserver通信,完成写入操作。只有等到lease到期,Master才会考虑重新分配primary chunkserver。

在05讲中提到,如果当前的chunk剩余的空间不能写下要追加的记录,GFS会先把它填满空数据,然后在新的chunk中写入要追加的记录。我想正是因为Master对写入操作参与程度低,所以GFS不能把一次追加写入的操作拆成两份,先把旧的chunk写满,再把剩下的数据写入新的chunk,因为GFS并不保证在这两次写入之间不会有其他操作插进来。

参考资料: