GFS阅读-上

这篇文章是阅读GFS记录的点滴,参考了Charles的博客,上面有很多关于分布式以及其他一些学习内容

Posted on February 28, 2017 in DistributedSystem, ReadPaper



#1 INTRODUCTION

分布式文件存储的目标是:

* performance:高性能,较低的响应时间,较高的吞吐量

* scalability:易于扩展,可以简单地通过增加机器来增大容量

* reliability: 可靠性,系统尽量不出错误

* availability:可用性,系统尽量保持可用


在设计的时候必须注意下面这几个事实:

1. 组件的失效是常态而非偶然

2. 按平时的标准而言分布式存储的文件是比较大的

3. 文件的修改通常是在后面追加内容而不是修改已有的数据

4. 应用与文件系统之间的接口必须灵活,例如可以多个用户并行操作同一文件

GFS能为不同的目的而部署,最大的集群拥有1000个存储节点,存放超过300TB的文化,同时支持上百个用户的并行访问

#2 SESIGN OVERVIEW


##2.1 Assumptions

GFS设计的预期有:

GFS运行在成千上万台便宜的机器上,这意味着节点的故障会经常发生。 必须有一定的容错的机制来应对这些故障。

系统要存储的文件通常都比较大,每个文件大约100MB或者更大, GB级别的文件也很常见。必须能够有效地处理这样的大文件, 基于这样的大文件进行系统优化。

* 读操作有两种模式:

1. large streaming read 通常一次读取数百KB的数据, 更常见的是一次读取1MB甚至更多的数据。 来自同一个client的连续操作通常是读取同一个文件中连续的一个区域。

2. small random read 通常是在文件某个随机的位置读取 几个KB数据。 对于性能敏感的应用通常把一批随机读任务进行排序然后按照顺序批量读取, 这样能够避免在通过一个文件来回移动位置。

为了应用使用方便,多客户端并行地追加同一个文件需要非常高效。文件经常被用于生产者-消费者队列,需要高效地处理多个生产者对同一文件的追加。正是基于这种特殊的应用场景,GFS实现了一个无锁并发append。

* 带宽的重要性大于时延,目标应用是高速读大块数据的应用,对响应时间没有过多的需求。基于这个假设, 可以把读写的任务分布到各个节点, 尽量保证每个节点的负载均衡, 尽管这样会造成一些请求的延迟。


##2.2.Interface

GFS提供类似操作系统文件系统的接口,同样通过pathname来分层次寻找文件,提供的API有create、delete、open、close、read and write。

除此之外,还有下面两个操作:

1. snapshot : 允许复制文件或者文件夹

2. record append : 允许多个用户并行地对同一文件进行追加操作,而且是实现了一个无锁并发append。


##2.3 Architecture

image.png

 GFS由三部分组成,分别是一个单一的**master**,多个**chunkserver**以及**client**

### chunkserver

在GFS chunkserver中,文件都是分成固定大小的chunk来存储的,每个chunk通过全局唯一的64位的chunk handle来标识,chunk handle在chunk创建的时候由GFS master分配。GFS chunkserver把文件存储在本地磁盘中,读或写的时候需要指定文件名和字节范围,然后定位到对应的chunk。为了保证数据的可靠性,一个chunk一般会在多台GFS chunkserver上存储,默认为3份,但用户也可以根据自己的需要修改这个值。

### master

GFS master管理所有的元数据信息,包括namespaces(目录)、文件的访问控制信息,文件到chunk的映射信息,以及chunk的地址信息(即chunk存放在哪台GFS chunkserver上)。其活动通常包括数据块lease的管理,数据块的垃圾回收,数据块的迁移。还与chunkserver保持周期性的心跳以及收集数据。

### client

GFS client是GFS应用端使用的API接口,client和GFS master交互来获取元数据信息,但是所有和数据相关的信息都是直接和GFS chunkserver来交互的。

client与chunkserver都不会缓存文件数据,因为数据太大而且不方便实现一致性。client会缓存元数据。


##2.4 Single Master

GFS架构中只有单个GFS master,这种架构的好处是设计和实现简单,例如,实现负载均衡时可以利用master上存储的全局的信息来做决策。但是,在这种架构下,要避免的一个问题是,应用读和写请求时,要弱化GFS master的参与度,防止它成为整个系统架构中的瓶颈。


从一个请求的流程来讨论上面的问题。首先,应用把文件名和偏移量信息传递给GFS client,GFS client转换成(文件名,chunk index)信息传递给GFS master,GFS master把(chunk handle, chunk位置信息)返回给客户端,客户端会把这个信息缓存起来(客户缓存元数据),这样,下次再读这个chunk的时候,就不需要去GFS master拉取chunk位置信息了。


另一方面,GFS支持在一个请求中同时读取多个chunk的位置信息,这样更进一步的减少了GFS client和GFS master的交互次数,避免GFS master成为整个系统的瓶颈。


##2.5 Chunk Size

Chunk的size设计为64M,比普通文件大得多,其replica保存成一个文件,当需要的时候才进行内存的扩展,这样避免出现内部的碎片,防止内存浪费。

大文件好处

* 减少**client**与**master**的交互次数

减少了网络的开销,由于一个**client**可能对同一个chunk进行操作, 这样可以与**chunkserver**维护一个**TCP长连接**

* 大大减少**master**上元数据的数量,使得元数据能够完全放置在**master**的内存上。


大文件的缺点

chunk size越大时,可能对部分文件来讲只有1个chunk,那么如果多个**client**同时操作这个文件的话,这个时候对该文件的读写就会落到一个**chunkserver**上,使其成为热点。因为如果是小的chunk的话, 一个文件拥有多个chunk,操作同一个文件被分布到多个**chunkserver**。 虽然在实践中,可以通过错开应用的启动的时间来减小同时操作一个文件的可能性。

##2.6 Metadata

**master**存储三种metadata,包括**文件和chunk namespace(目录)信息**,**文件到chunk的映射**以及**chunk的位置信息**。这些metadata都是存储在GFS master的内存中的。对于前两种metadata,还会通过记操作日志的方式持久化存储,操作日志会同步到包括GFS master在内的多台机器上。GFS master不持久化存储chunk的位置信息,每次GFS master重启或者有新的GFS chunkserver加入时,GFS master会要求对应GFS chunkserver把chunk的位置信息汇报给它。

###2.6.1 In-Memory Data Structures

使用内存存储metadata的好处是读取metadata速度快,方便GFS master做一些全局扫描metadata相关信息的操作,例如负载均衡等。


但是,以内存存储的的话,需要考虑的是GFS master的内存空间大小是不是整个系统能存储的chunk数量的瓶颈所在。在GFS实际使用过程中,这一般不会成为限制所在,因为GFS中一个64MBchunk的metadata大小不超过64B,并且,对于大部分chunk来讲都是使用的全部的空间的,只有文件的最后一个chunk会存储在部分空间没有使用,因此,GFS master的内存空间在实际上很少会成为限制系统容量的因素。即使真的是现有的存储文件的chunk数量超过了GFS master内存空间大小的限制,也可以通过加内存的方式,来获取内存存储设计带来的性能、可靠性等多种好处。

###2.6.2 Chunk Locations

GFS master不持久化存储chunk位置信息的原因是,GFS chunkserver很容易出现宕机,重启等行为,这样GFS master在每次发生这些事件的时候,都要修改持久化存储里面的位置信息的数据。数据的位置最终话语权应该交给chunkserver而不是master管理。通过心跳信息可以传递chunk locations。

### 2.6.3 Operation Log

* 作用:

1. 持久化存储metadata

2. 它的存储顺序定义了并行的操作的最终的操作顺序

*  存放方式

operation log会存储在GFS master和多台远程机器上,只有当operation log在GFS master和多台远程机器都写入成功后,GFS master才会向GFS client返回成功。为了减少operation log在多台机器落盘对吞吐量的影响,可以将一批的operation log形成一个请求,然后写入到GFS master和其他远程机器上。

* check point

当operation log达到一定大小时,GFS master会做checkpoint,相当于把内存的B-Tree格式的信息dump到磁盘中。当master需要重启时,可以读最近一次的checkpoint,然后replay它之后的operation log,加快恢复的时间。


做checkpoint的时候,GFS master会先切换到新的operation log,然后开新线程做checkpoint,所以,对新来的请求是基本是不会有影响的。

##2.7 Consistency Model

image.png

###2.7.1 Guarantees by GFS


* consistent 所有的客户端都能看到一样的数据,不管它们从哪个副本读取。

* defined 当一个文件区域发生操作后,client可以看到刚刚操作的所有数据,那么说这次操作是defined。defined一定是consistent的。

undefined。undefined一定是consistent,是多个修改混合在一块。举个例子, 修改a想给文件添加A1,A2,修改b想给文件添加B1,B2,如果最后的结果是A1,A2,B1,B2, 那么就是defined,如果是A1,B1,A2,B2,就是undefined。

* inconsitent。对于不同的replica,包含的是不同的data。

下面分析表格中出现的几种情况。

1. Write(Serial Success),单个写操作,并且返回成功,那么所有副本都写入了这次操作的数据,因此所有客户端都能看到这次写入的数据,所以,是defined。

2. Write(Concurrent Successes),多个写操作,并且返回成功,由于多个客户端写请求发送给priamary后,由primary来决定写的操作顺序,但是,有可能多个写操作可能是有区域重叠的,这样,最终写完成的数据可能是多个写操作数据叠加在一起,所以这种情况是consistent和undefined。

3.  Write(Failure),写操作失败,则可能有的副本写入了数据,有的没有,所以是inconsistent。

4. Record Append(Serial Success and Concurrent Success),由于Record Append可能包含重复数据,因此,是inconsistent,由于整个写入的数据都能看到,所以是defined。

5. Record Append(Failure),可能部分副本append成功,部分副本append失败,所以,结果是inconsistent。

GFS用version来标记一个chunkserver挂掉的期间,是否有client进行了write或者append操作。每进行一次write或者append,version会增加。

需要考虑的点是client会缓存chunk的位置信息,有可能其中某些chunkserver已经挂掉又起来了,这个时候chunkserver的数据可能是老的数据,读到的数据是会不一致的。读流程中,好像没有看到要带version信息来读的。这个论文中没看到避免的措施,目前还没有结果。


###2.7.2 implications for application

应用层需要采用的机制:用append而不是write,做checkpoint,writing self-validating和self-identifying records。具体地,如下:

1. 应用的使用流程是append一个文件,到最终写完后,重命名文件

2. 对文件做checkpoint,这样应用只需要关注上次checkpoint时的文件区域到最新文件区域的数据是否是consistent的,如果这期间发生不一致,可以重新做这些操作。

3. 对于并行做append的操作,可能会出现重复的数据,GFS client提供去重的功能。