GFS是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上,但可以提供容错功能。它可以给大量的用户提供总体性能较高的服务。
1、设计概览
(1)设计想定
GFS与过去的分布式文件系统有很多相同的目标,但GFS的设计受到了当前及预期的应用方面的工作量及技术环境的驱动,这反映了它与早期的文件系统明显不同的设想。这就需要桐散对传统的选择进行重新检验并进行完全不同的设计观点的探索。
GFS与以往的文件系统的不同的观点如下:
1、 部件错误不再被当作异常,而是将其作为常见的情况加以处理。因为文件系统由成百上千个用于存储的机器构成,而这些机器是由廉价的普通部件组成并被大量的客 户机访问。部件的数量和质量使得一些机器随时都有可能无法工作并且有一部分还可能无法恢复。所以实时地监控、错误检测、容错、自动恢复对系统来说必不可 少。
2、按照传统的标准,文件都非常大。长度达几个GB的文件是很平常的。每个文件通常包含很多应用对象。当经常要处理快速增长的、包含数以 万计的对象、长度达TB的数据集时,我们很难管理成千上万的KB规模的文件块,即使底层文件系统提供支持。因此,设计中 *** 作的参数、块的大小必须要重新考 虑。对大型的文件的管理一定要能做到高效,对小型的文件也必须支持,但不必优化。
3、大部分文件的更新是通过添加新数据完成的,而不是改变已 存在的数据。在一个文件中随机的 *** 作在实践中几乎不存在。一旦写完,文件就只可读,很多数据都有这些特性。一些数据可能组成一个大仓库以供数据分析程序扫 描。有些是运行中的程序连续产生的数据流。有些是档案性质的数据,有些是在某个机器上产生、在另外一个机器上处理的中间数据。由于这些对大型文件的访问方 式,添加 *** 作成为性能优化和原子性保证的焦点。而在客户机中缓存数据块则失去了吸引力。
4、工作量主要由两种读 *** 作构成:对大量数据的流方式 的读 *** 作和对少量数据的随机方式的读 *** 作。在前一种读 *** 作中,可能要读几百KB,通常达 1MB和更多。来自同一个客户的连续 *** 作通常会读文件的一个连续的区域。随机的读 *** 作通常在一个随机的偏移处读几个KB。性能敏感的应用程序通常将对少量 数据的读 *** 作进行分类并进行批处理以使得读 *** 作稳定地向前推进,而不要让它来来回回的读。
5、工作量还包含许多对大量数据进行的、连续的、向文件添加数据的写 *** 作。所写的数据的规模和读相似。一旦写完,文件很少改动。在随机位置对少量数据的写 *** 作也支持,但不必非常高效。
6、系统必须枯猜高效地实现定义完好的大量客户同时向同一个文件的添加 *** 作的语义。
(2)系统接口
GFS提供了一个相似地文件系统界面,虽然它没有向POSIX那样实现标准的API。文件在目录中按层次组织起来并由路径名标识。
(3)体系结构:
一 个GFS集群由一个master和大量的chunkserver构成,并被许多客户(Client)访问。如图1所示。Master和 chunkserver通常是运行用户层服务进程的Linux机器。只要资源和可靠性允许,chunkserver和client可以运行在同一个机器 上。
文件被分成固定大小的块。每个块由一个不变的、全局唯一的64位的chunk-handle标识,chunk-handle是在块创建时 由 master分配的。ChunkServer将块当作Linux文件存储在本地磁盘并可以读和写由chunk-handle和位没轮型区间指定的数据。出于可靠 性考虑,每一个块被复制到多个chunkserver上。默认情况下,保存3个副本,但这可以由用户指定。
Master维护文件系统所以的元 数据(metadata),包括名字空间、访问控制信息、从文件到块的映射以及块的当前位置。它也控制系统范围的活动,如块租约(lease)管理,孤儿 块的垃圾收集,chunkserver间的块迁移。Master定期通过HeartBeat消息与每一个 chunkserver通信,给chunkserver传递指令并收集它的状态。
与每个应用相联的GFS客户代码实现了文件系统的API并与master和chunkserver通信以代表应用程序读和写数据。客户与master的交换只限于对元数据(metadata)的 *** 作,所有数据方面的通信都直接和chunkserver联系。
客 户和chunkserver都不缓存文件数据。因为用户缓存的益处微乎其微,这是由于数据太多或工作集太大而无法缓存。不缓存数据简化了客户程序和整个系 统,因为不必考虑缓存的一致性问题。但用户缓存元数据(metadata)。Chunkserver也不必缓存文件,因为块时作为本地文件存储的。
(4)单master。
只 有一个master也极大的简化了设计并使得master可以根据全局情况作出先进的块放置和复制决定。但是我们必须要将master对读和写的参与减至 最少,这样它才不会成为系统的瓶颈。Client从来不会从master读和写文件数据。Client只是询问master它应该和哪个 chunkserver联系。Client在一段限定的时间内将这些信息缓存,在后续的 *** 作中Client直接和chunkserver交互。
以图1解释一下一个简单的读 *** 作的交互。
1、client使用固定的块大小将应用程序指定的文件名和字节偏移转换成文件的一个块索引(chunk index)。
2、给master发送一个包含文件名和块索引的请求。
3、master回应对应的chunk handle和副本的位置(多个副本)。
4、client以文件名和块索引为键缓存这些信息。(handle和副本的位置)。
5、Client 向其中一个副本发送一个请求,很可能是最近的一个副本。请求指定了chunk handle(chunkserver以chunk handle标识chunk)和块内的一个字节区间。
6、除非缓存的信息不再有效(cache for a limited time)或文件被重新打开,否则以后对同一个块的读 *** 作不再需要client和master间的交互。
通常Client可以在一个请求中询问多个chunk的地址,而master也可以很快回应这些请求。
(5)块规模:
块规模是设计中的一个关键参数。我们选择的是64MB,这比一般的文件系统的块规模要大的多。每个块的副本作为一个普通的Linux文件存储,在需要的时候可以扩展。
块规模较大的好处有:
1、减少client和master之间的交互。因为读写同一个块只是要在开始时向master请求块位置信息。对于读写大型文件这种减少尤为重要。即使对于访问少量数据的随机读 *** 作也可以很方便的为一个规模达几个TB的工作集缓缓存块位置信息。
2、Client在一个给定的块上很可能执行多个 *** 作,和一个chunkserver保持较长时间的TCP连接可以减少网络负载。
3、这减少了master上保存的元数据(metadata)的规模,从而使得可以将metadata放在内存中。这又会带来一些别的好处。
不利的一面:
一个小文件可能只包含一个块,如果很多Client访问改文件的话,存储这些块的chunkserver将成为访问的热点。但在实际应用中,应用程序通常顺序地读包含多个块的文件,所以这不是一个主要问题。
(6)元数据(metadata):
master 存储了三中类型的metadata:文件的名字空间和块的名字空间,从文件到块的映射,块的副本的位置。所有的metadata都放在内存中。前两种类型 的metadata通过向 *** 作日志登记修改而保持不变, *** 作日志存储在master的本地磁盘并在几个远程机器上留有副本。使用日志使得我们可以很简单 地、可靠地更新master的状态,即使在master崩溃的情况下也不会有不一致的问题。相反,mater在每次启动以及当有 chuankserver加入的时候询问每个chunkserver的所拥有的块的情况。
A、内存数据结构:
因为metadata存储在内存中,所以master的 *** 作很快。进一步,master可以轻易而且高效地定期在后台扫描它的整个状态。这种定期地扫描被用于实现块垃圾收集、chunkserver出现故障时的副本复制、为平衡负载和磁盘空间而进行的块迁移。
这 种方法的一个潜在的问题就是块的数量也即整个系统的容量是否受限与master的内存。实际上,这并不是一个严重的问题。Master为每个 64MB的块维护的metadata不足64个字节。除了最后一块,文件所有的块都是满的。类似的,每个文件的名字空间数据也不足64个字节,因为文件名 是以一种事先确定的压缩方式存储的.如果要支持更大的文件系统,那么增加一些内存的方法对于我们将元数据(metadata)保存在内存种所获得的简单 性、可靠性、高性能和灵活性来说,这只是一个很小的代价。
B、块位置:
master并不为chunkserver所拥有的块的副本的保存一个不变的记录。它在启动时通过简单的查询来获得这些信息。Master可以保持这些信息的更新,因为它控制所有块的放置并通过HeartBeat消息来监控chunkserver的状态。
这样做的好处:因为chunkserver可能加入或离开集群、改变路径名、崩溃、重启等,一个集群重有成百个server,这些事件经常发生,这种方法就排除了master与chunkserver之间的同步问题。
另一个原因是:只有chunkserver才能确定它自己到底有哪些块,由于错误,chunkserver中的一些块可能会很自然的消失,这样在master中就没有必要为此保存一个不变的记录。
C、 *** 作日志:
*** 作日志包含了对metadata所作的修改的历史记录。它作为逻辑时间线定义了并发 *** 作的执行顺序。文件、块以及它们的版本号都由它们被创建时的逻辑时间而唯一地、永久地被标识。
*** 作日志是如此的重要,我们必须要将它可靠地保存起来,并且只有在metadata的改变固定下来之后才将变化呈现给用户。所以我们将 *** 作日志复制到数个远程的机器上,并且只有在将相应的日志记录写到本地和远程的磁盘上之后才回答用户的请求。
Master可以用 *** 作日志来恢复它的文件系统的状态。为了将启动时间减至最小,日志就必须要比较小。每当日志的长度增长到超过一定的规模后,master就要检查它的状态,它可以从本地磁盘装入最近的检查点来恢复状态。
创 建一个检查点比较费时,master的内部状态是以一种在创建一个检查点时并不耽误即将到来的修改 *** 作的方式来组织的。Master切换到一个新的日子文 件并在一个单独的线程中创建检查点。这个新的检查点记录了切换前所有的修改。在一个有数十万文件的集群中用一分钟左右就能完成。创建完后,将它写入本地和 远程的磁盘。
(7)数据完整性
名字空间的修改必须是原子性的,它们只能有master处理:名字空间锁保证了 *** 作的原子性和正确性,而master的 *** 作日志在全局范围内定义了这些 *** 作的顺序。
文 件区间的状态在修改之后依赖于修改的类型,不论 *** 作成功还是失败,也不论是不是并发 *** 作。如果不论从哪个副本上读,所有的客户都看到同样的数据,那么文件 的这个区域就是一致的。如果文件的区域是一致的并且用户可以看到修改 *** 作所写的数据,那么它就是已定义的。如果修改是在没有并发写 *** 作的影响下完成的,那 么受影响的区域是已定义的,所有的client都能看到写的内容。成功的并发写 *** 作是未定义但却是一致的。失败的修改将使区间处于不一致的状态。
Write *** 作在应用程序指定的偏移处写入数据,而record append *** 作使得数据(记录)即使在有并发修改 *** 作的情况下也至少原子性的被加到GFS指定的偏移处,偏移地址被返回给用户。
在一系列成功的修改 *** 作后,最后的修改 *** 作保证文件区域是已定义的。GFS通过对所有的副本执行同样顺序的修改 *** 作并且使用块版本号检测过时的副本(由于chunkserver退出而导致丢失修改)来做到这一点。
因为用户缓存了会位置信息,所以在更新缓存之前有可能从一个过时的副本中读取数据。但这有缓存的截止时间和文件的重新打开而受到限制。
在修改 *** 作成功后,部件故障仍可以是数据受到破坏。GFS通过master和chunkserver间定期的handshake,借助校验和来检测对数据的破坏。一旦检测到,就从一个有效的副本尽快重新存储。只有在GFS检测前,所有的副本都失效,这个块才会丢失。
2、系统交互
(1)租约(lease)和修改顺序:
(2)数据流
我们的目标是充分利用每个机器的网络带宽,避免网络瓶颈和延迟
为了有效的利用网络,我们将数据流和控制流分离。数据是以流水线的方式在选定的chunkerserver链上线性的传递的。每个机器的整个对外带宽都被用作传递数据。为避免瓶颈,每个机器在收到数据后,将它收到数据尽快传递给离它最近的机器。
(3)原子性的record Append:
GFS 提供了一个原子性的添加 *** 作:record append。在传统的写 *** 作中,client指定被写数据的偏移位置,向同一个区间的并发的写 *** 作是不连续的:区间有可能包含来自多个client的数 据碎片。在record append中, client只是指定数据。GFS在其选定的偏移出将数据至少原子性的加入文件一次,并将偏移返回给client。
在分布式的应用中,不同机 器上的许多client可能会同时向一个文件执行添加 *** 作,添加 *** 作被频繁使用。如果用传统的write *** 作,可能需要额外的、复杂的、开销较大的同步, 例如通过分布式锁管理。在我们的工作量中,这些文件通常以多个生产者单个消费者队列的方式或包含从多个不同 client的综合结果。
Record append和前面讲的write *** 作的控制流差不多,只是在primary上多了一些逻辑判断。首先,client将数据发送到文件最后一块的所有副本 上。然后向primary发送请求。Primary检查添加 *** 作是否会导致该块超过最大的规模(64M)。如果这样,它将该块扩充到最大规模,并告诉其它 副本做同样的事,同时通知client该 *** 作需要在下一个块上重新尝试。如果记录满足最大规模的要求,primary就会将数据添加到它的副本上,并告诉 其它的副本在在同样的偏移处写数据,最后primary向client报告写 *** 作成功。如果在任何一个副本上record append *** 作失败,client将重新尝试该 *** 作。这时候,同一个块的副本可能包含不同的数据,因为有的可能复制了全部的数据,有的可能只复制了部 分。GFS不能保证所有的副本每个字节都是一样的。它只保证每个数据作为一个原子单元被写过至少一次。这个是这样得出的: *** 作要是成功,数据必须在所有的 副本上的同样的偏移处被写过。进一步,从这以后,所有的副本至少和记录一样长,所以后续的记录将被指定到更高的偏移处或者一个不同的块上,即使另一个副本 成了primary。根据一致性保证,成功的record append *** 作的区间是已定义的。而受到干扰的区间是不一致的。
(4)快照(snapshot)
快照 *** 作几乎在瞬间构造一个文件和目录树的副本,同时将正在进行的其他修改 *** 作对它的影响减至最小。
我 们使用copy-on-write技术来实现snapshot。当master受到一个snapshot请求时,它首先将要snapshot的文件上块上 的lease。这使得任何一个向这些块写数据的 *** 作都必须和master交互以找到拥有lease的副本。这就给master一个创建这个块的副本的机 会。
副本被撤销或终止后,master在磁盘上登记执行的 *** 作,然后复制源文件或目录树的metadata以对它的内存状态实施登记的 *** 作。这个新创建的snapshot文件和源文件(其metadata)指向相同的块(chunk)。
Snapshot 之后,客户第一次向chunk c写的时候,它发一个请求给master以找到拥有lease的副本。Master注意到chunk c的引用记数比1大,它延迟对用户的响应,选择一个chunk handle C’,然后要求每一有chunk c的副本的chunkserver创建一个块C’。每个chunkserver在本地创建chunk C’避免了网络开销。从这以后和对别的块的 *** 作没有什么区别。
3、MASTER *** 作
MASTER执行所有名字空间的 *** 作,除此之外,他还在系统范围管理数据块的复制:决定数据块的放置方案,产生新数据块并将其备份,和其他系统范围的 *** 作协同来确保数据备份的完整性,在所有的数据块服务器之间平衡负载并收回没有使用的存储空间。
3.1 名字空间管理和加锁
与传统文件系统不同的是,GFS没有与每个目录相关的能列出其所有文件的数据结构,它也不支持别名(unix中的硬连接或符号连接),不管是对文件或是目录。GFS的名字空间逻辑上是从文件元数据到路径名映射的一个查用表。
MASTER 在执行某个 *** 作前都要获得一系列锁,例如,它要对/d1/d2…/dn/leaf执行 *** 作,则它必须获得/d1,/d1/d2,…, /d1/d2/…/dn的读锁,/d1/d2…/dn/leaf的读锁或写锁(其中leaf可以使文件也可以是目录)。MASTER *** 作的并行性和数据的 一致性就是通过这些锁来实现的。
3.2 备份存储放置策略
一个GFS集群文件系统可能是多层分布的。一般情况下是成千上万个文件块 服务器分布于不同的机架上,而这些文件块服务器又被分布于不同机架上的客户来访问。因此,不同机架上的两台机器之间的通信可能通过一个或多个交换机。数据 块冗余配置策略要达到连个目的:最大的数据可靠性和可用性,最大的网络带宽利用率。因此,如果仅仅把数据的拷贝置于不同的机器上很难满足这两个要求,必须 在不同的机架上进行数据备份。这样即使整个机架被毁或是掉线,也能确保数据的正常使用。这也使数据传输,尤其是读数据,可以充分利用带宽,访问到多个机 架,而写 *** 作,则不得不涉及到更多的机架。
3.3 产生、重复制、重平衡数据块
当MASTER产生新的数据块时,如何放置新数据 块,要考虑如下几个因素:(1)尽量放置在磁盘利用率低的数据块服务器上,这样,慢慢地各服务器的磁盘利用率就会达到平衡。(2)尽量控制在一个服务器上 的“新创建”的次数。(3)由于上一小节讨论的原因,我们需要把数据块放置于不同的机架上。
MASTER在可用的数据块备份低于用户设定的数 目时需要进行重复制。这种情况源于多种原因:服务器不可用,数据被破坏,磁盘被破坏,或者备份数目被修改。每个被需要重复制的数据块的优先级根据以下几项 确定:第一是现在的数目距目标的距离,对于能阻塞用户程序的数据块,我们也提高它的优先级。最后, MASTER按照产生数据块的原则复制数据块,并把它们放到不同的机架内的服务器上。
MASTER周期性的平衡各服务器上的负载:它检查 chunk分布和负载平衡,通过这种方式来填充一个新的服务器而不是把其他的内容统统放置到它上面带来大量的写数据。数据块放置的原则与上面讨论的相同, 此外,MASTER还决定那些数据块要被移除,原则上他会清除那些空闲空间低于平均值的那些服务器。
3.4 垃圾收集
在一个文件被删除之后,GFS并不立即收回磁盘空间,而是等到垃圾收集程序在文件和数据块级的的检查中收回。
当 一个文件被应用程序删除之后,MASTER会立即记录下这些变化,但文件所占用的资源却不会被立即收回,而是重新给文件命了一个隐藏的名字,并附上了删除 的时间戳。在MASTER定期检查名字空间时,它删除超过三天(可以设定)的隐藏的文件。在此之前,可以以一个新的名字来读文件,还可以以前的名字恢复。 当隐藏的文件在名字空间中被删除以后,它在内存中的元数据即被擦除,这就有效地切断了他和所有数据块的联系。
在一个相似的定期的名字空间检查中,MASTER确认孤儿数据块(不属于任何文件)并擦除他的元数据,在和MASTER的心跳信息交换中,每个服务器报告他所拥有的数据块,MASTER返回元数据不在内存的数据块,服务器即可以删除这些数据块。
3.5 过时数据的探测
在数据更新时如果服务器停机了,那么他所保存的数据备份就会过时。对每个数据块,MASTER设置了一个版本号来区别更新过的数据块和过时的数据块。
当MASTER 授权一个新的lease时,他会增加数据块的版本号并会通知更新数据备份。MASTER和备份都会记录下当前的版本号,如果一个备份当时不可用,那么他的 版本号不可能提高,当ChunkServer重新启动并向MASTER报告他的数据块集时,MASTER就会发现过时的数据。
MASTER在 定期的垃圾收集程序中清除过时的备份,在此以前,处于效率考虑,在各客户及英大使,他会认为根本不存在过时的数据。作为另一个安全措施, MASTER在给客户及关于数据块的应答或是另外一个读取数据的服务器数据是都会带上版本信息,在 *** 作前客户机和服务器会验证版本信息以确保得到的是最新 的数据。
4、容错和诊断
4.1 高可靠性
4.1.1 快速恢复
不管如何终止服务,MASTER和数据块服务器都会在几秒钟内恢复状态和运行。实际上,我们不对正常终止和不正常终止进行区分,服务器进程都会被切断而终止。客户机和其他的服务器会经历一个小小的中断,然后它们的特定请求超时,重新连接重启的服务器,重新请求。
4.1.2 数据块备份
如上文所讨论的,每个数据块都会被备份到放到不同机架上的不同服务器上。对不同的名字空间,用户可以设置不同的备份级别。在数据块服务器掉线或是数据被破坏时,MASTER会按照需要来复制数据块。
4.1.3 MASTER备份
为 确保可靠性,MASTER的状态、 *** 作记录和检查点都在多台机器上进行了备份。一个 *** 作只有在数据块服务器硬盘上刷新并被记录在MASTER和其备份的上 之后才算是成功的。如果MASTER或是硬盘失败,系统监视器会发现并通过改变域名启动它的一个备份机,而客户机则仅仅是使用规范的名称来访问,并不会发 现MASTER的改变。
4.2 数据完整性
每个数据块服务器都利用校验和来检验存储数据的完整性。原因:每个服务器随时都有发生崩溃的可能性,并且在两个服务器间比较数据块也是不现实的,同时,在两台服务器间拷贝数据并不能保证数据的一致性。
每个Chunk按64kB的大小分成块,每个块有32位的校验和,校验和和日志存储在一起,和用户数据分开。
在 读数据时,服务器首先检查与被读内容相关部分的校验和,因此,服务器不会传播错误的数据。如果所检查的内容和校验和不符,服务器就会给数据请求者返回一个 错误的信息,并把这个情况报告给MASTER。客户机就会读其他的服务器来获取数据,而MASTER则会从其他的拷贝来复制数据,等到一个新的拷贝完成 时,MASTER就会通知报告错误的服务器删除出错的数据块。
附加写数据时的校验和计算优化了,因为这是主要的写 *** 作。我们只是更新增加部分的校验和,即使末尾部分的校验和数据已被损坏而我们没有检查出来,新的校验和与数据会不相符,这种冲突在下次使用时将会被检查出来。
相反,如果是覆盖现有数据的写,在写以前,我们必须检查第一和最后一个数据块,然后才能执行写 *** 作,最后计算和记录校验和。如果我们在覆盖以前不先检查首位数据块,计算出的校验和则会因为没被覆盖的数据而产生错误。
在空闲时间,服务器会检查不活跃的数据块的校验和,这样可以检查出不经常读的数据的错误。一旦错误被检查出来,服务器会拷贝一个正确的数据块来代替错误的。
4.3 诊断工具
广 泛而细致的诊断日志以微小的代价换取了在问题隔离、诊断、性能分析方面起到了重大的作用。GFS服务器用日志来记录显著的事件(例如服务器停机和启动)和 远程的应答。远程日志记录机器之间的请求和应答,通过收集不同机器上的日志记录,并对它们进行分析恢复,我们可以完整地重现活动的场景,并用此来进行错误 分析。
6 测量
6.1 测试环境
一台主控机,两台主控机备份,16台数据块服务器,16台客户机。
每台机器:2块PIII1.4G处理器,2G内存,2块80G5400rpm的硬盘,1块100Mbps全双工网卡
19台服务器连接到一个HP2524交换机上,16台客户机俩接到领外一台交换机上,两台交换机通过1G的链路相连。
[编辑本段]Government Flying Service
政府飞行服务队
政府飞行服务队是香港特别行政区政府保安局辖下的纪律部门,专责执行搜索及拯救行动。另外,亦会拍摄照片供制作地图、测量填海工程、以及支援位处偏远山区和离岛的各项政府服务。现时雇有225名公务员。现任政府飞行服务队总监为毕耀明,是服务队的最高指挥官,直接向保安局局长负责。
政府飞行服务队的总部位于香港国际机场,占地84,000平方米,包括一幢办公和工场大楼、一座单层的飞机库、多座附属建筑物、停机坪及其他相关设施等。
理想
香港政府飞行服务队理想是成为“被举世公认为优秀的空中搜救及飞行支援部队”。
历史
于1993年4月1日成立,前身为皇家香港辅助空军。
架构
香港政府飞行服务队共分为五个主要小组:行政组、行动组、训练及标准组、品质保证组及工程组。
服务范围
它们的主要任务为香港政府各部门提供每星期七天及每日二十四小时的直升机和定翼机紧急支援服务。其中包括:
搜索及拯救
空中救护服务
警务支援服务
灭火服务
空中测量服务
一般政府支援服务
警务支援服务
Google File System(简称GFS)是适用于大规模且可扩展的分布式文件系统,可以部署在廉价的商务服务器上,在保证系统可靠性和可用 性的同时,大大降低了系统的成本。GFS的设计目标是高性能、高可靠、高可用性。
GFS把机器故障视腔宏为正常现象,可以很好地处理系统故障。GFS系统通常会部署在上百台甚至上千台廉价服务器上,并会有相当多台廉价服务器上部署GFS Client来访问GFS服务,所以应用故障、 *** 作系统bug、连接故障、网络故障、甚至机器供电故障都是经常发生的故障。GFS系统可以支持系统监控、故障检测、故障容忍和自动恢复,提供了非常高的可靠性。其次,GFS系统中的文件一般都是大文件,且文件 *** 作大部分场景下都是append而不是overwrite。一旦文件写入完成后,大部分 *** 作都是读文件且是顺序读。
GFS提供了非标准(比如POSIX)的文件系统接口,支持 create、delete、open、close、read以及write。另外GFS支持snapshot和record append *** 作。snapshot可以以很低的代价创建文件或者目录树的拷贝,record append可以支持多个client并发地向同一个文件append data,大圆察同时还能保证每个client的append *** 作的原子性。
master记录了文件系统的metadata,包括名字空间、权限控制信息、文件到chunk的mapping以及chunk的分布。master也负责chunk的lease管理、无用chunk的垃圾回收、chunk迁移等。master定期与chunkserver通信,向chunkserver发送指令并搜集chunkserver的状态。GFS client通过GFS的API与GFS系统通信(读写数据)。client向master请求获取metadata,真正的读写数据是直接与chunkserver交互。client和chunkserver都不cache文件数据。因为大部分应用都是基于API来streaming read 大文件且系统的文件数据太多,所以client缓存文件数据没有意义。chunkserver所在机器的Linux的buffer cache以及cache了频繁访问的数据,chunkserver也是没有去cache文件数据的。
单点master大大简化了系统设计,因为master知晓所有的meta信息,所以可以执行更加复杂的chunk位置分配和副本策略。但是,在读写数据时必须降低master的参与,以避免单点的master称为系统瓶颈。client不会通过master来读写文件数据,但是client会向master发送查询chunk位置分布的请求,然后client端缓存chunk的分布信息,然后直接向chunkserver读写数据。大致的读过程如下:
1、client根据文件名、byte offset以及chunk size计算出要读取的文件的chunk index
2、client通过文件名、chunk index向master查询chunk的分布
3、master回复chunk handler以及副本分布
4、client 缓存chunk的meta信息,key由文件名和chunk index组成
5、client从chunk的分布信息中查找距离自己最新的chunkserver,并发送查询请求。查询请求中包括chunk hander以及byte range。后续对相同chunk的查询不需要再次向master查询meta信息,因为client已经缓存了meta信息。
chunk size是GFS系统的关键参数,通常设置为64MB,远大于文件系统的block大小。每个chunk的副本都chunkserver所在机滚茄器上以Linux file存储。之所为将chunk size定为64MB,主要有以下考虑:
1、可以减少client访问master查询meta信息的次数,降低master的访问压力。因为chunk size设计比较大,顺序访问一个超大文件时因为chunk数较少且client缓存了chunk meta信息,所以访问master的次数就会降低。甚至,client可以缓存所有文件的chunk的meta信息,就算是随机读文件,master也不会成为系统性能瓶颈。
2、可以减少网络开销,保持client与chunkserver的TCP连接,可以执行更多的chunk *** 作。
3、可以减少master上需要在内存中记录的meta data数据量,降低master的内存占用。
size大的缺点是:小文件包含很少的chunk,甚至只有一个。这样的话,在多个client高并发查询该小文件时对应的chunk会成为热点。实际上,这种情况在GFS系统中很少发生,因为大部分client的 *** 作都是顺序读大文件。但是,考虑以下场景,我们部署一个服务的二进制文件到GFS系统中,然后数百台的服务器同时查询二进制文件并启动服务,此时该二进制文件副本所在的chunkserver立马就会成为查询瓶颈。当然,可以通过增加副本数和分散服务器的查询时间来解决这种场景下的问题。
master主要存储三种类型的metadata:file和chunk的名字空间,file到chunk的mapping信息以及chunk的副本分布。所有的metadata都在master的内存中存储。前两种meta信息可以持久化存储,将 *** 作日志存储在master的本地磁盘以及将备份日志存储在远端机器上。master不持久化存储chunk的副本分布信息,而是通过与chunkserver交互来获取chunkserver上的chunk信息。
4.1 in-memory data structure
meta信息在内存中,所有master的 *** 作很快。另外,master可以高效地定期在后台scan所有的meta数据,来执行垃圾回收、副本修复、均衡等。metadata都记录在内存中,所以GFS系统会比较关注chunk的数量以及master的可用内存量。但是在实际场景下,这不是问题。每个64MB的chunk的metadata小于64字节,大部分的chunk都是满负荷存储的,除了文件最后一个chunk的空间是没有完全被占用。由于文件的名字空间采用了前缀压缩的方式存储,单个文件的meta信息也是小于64字节。如果需要扩大系统规模的话,可以很简单地通过增大master的内存就可以了。相比于系统的高可靠、高性能和简洁性,增加内存是很最小的代价了。
4.2 chunk 分布
并没有持久化存储chunk的副本分布信息,而是在master启动时向chunkserver查询其chunk信息,然后通过heartbeat来持续更新master的副本分布信息,以与chunkserver数据保持一致。GFS起初设计时尝试将chunk的分布信息持久化存储在master端,随后发现通过master启动时拉取然后通过heartbeat同步chunk信息的方式更简单。因为,当chunkserver加入、退出、名字改变、重启等行为经常发生,这会导致维护master的chunk meta数据的正确性是非常困难的。从另一个角度考虑就是,只有chunkserver汇报的chunk信息才是集群中最真实的chunk分布,因为master不需要自己维护一个chunk分布状态,只需要以chunkserver的状态汇报为准即可。
4.3 *** 作日志
日志记录了GFS集群数据更改的历史记录。 *** 作日志对GFS来说是至关重要的,因为它不仅是metadata的持久化记录,还记录了并发 *** 作的时序。因为 *** 作日志很重要,所以必须可靠地存储。在metadata的change没有持久化之前,client是不能看到的数据的更改。当client修改数据时, *** 作记录需要保存在多个远端机器上,而且只有当 *** 作记录持久化存储在本地和远端以后,才会回复client数据更改成功。
可以通过回放 *** 作日志来恢复文件系统。为了减少系统启动时replay的时间,必须缩减回放的日志量。master可以定期存储metadata的checkpoint,master重启时可以从checkpoint加载metadata,然后回放checkpoint之后的少量日志即可。
1、client向master查询chunk的primary所在的chunkserver以及其他副本的分布,如果没有primary的花,master会选择一个作为该chunk的primary
2、master回复client primary和其他副本的分布信息。client会cache返回的metadata
3、client将数据发送所有的副本。client可以以任意顺序执行。每个chunkserser都会在内存的LRUbuffer中记录数据。
4、当所有的副本都返回已经接收数据成功后,client会向primary发送一个写请求。primary会为每一个数据更改的请求附加一个序列号,数据更改是按照序列号的顺序执行的。
5、primary将数据更改同步到其他副本中,副本也是按照序列号执行数据更改 *** 作。
6、primary接收到其他副本回复的数据 *** 作完成
7、primary返回client结果。期间发生的所有错误都会报给client。
GFS集群一般都会有上百台的chunkserver,分布在多个机架上。chunkserver也会接收来自本机架或者其他机架的上百个client的查询请求。不同机架的服务器通信可能会途径一个或者多个交换机转发。chunk的副本分布选择策略主要目的是尽量提高数据的可靠性和可用性,同时最大化地充分利用网络带宽。所以,仅仅将副本跨机器部署是不够的。GFS将副本是跨机架部署的,这样可以保证在一个机架被损坏或者下线时,chunk至少会有副本是可用的。
chunk的副本在下列情况下会被创建:创建chunk、副本修复、rebalance。当master创建chunk时,会选择存储该chunk副本的chunkserver。主要考虑以下几点:
1、新副本所在chunkserver的磁盘利用率低于系统的平均水平
2、限制每个chunkserver最近一段时间创建chunk的数量
3、每个chunk的所有副本不能都在一个机架
chunk的副本数少于一定数量是,master会复制一个副本。这可能发生在chunkserver宕机或者chunkserver汇报自己的副本损坏或者chunkserver所在机器的磁盘损坏等等。每个chunk 复制任务都有优先级,按照优先级由高到低子master中排队等待执行。master还会定期扫描当前副本的分布情况,一旦发现磁盘使用量或者机器负载不均衡,就会发起负载均衡 *** 作。无论是chunk创建、chunk复制还是负载均衡,选择chunk副本的位置的策略都是相同的,并且需要限制副本修复和均衡的速度,否则会影响系统的正常读写服务。
Google的成功表明单master的设计师可行的。这不仅简化了系统,而且能够较好地实现一致性,给予性能考虑,GFS提出了“记录至少原子性追加一次”的一致性模型。通过租约的方式将每个chunk的修改授权到chunkserver从而减少了master的负载,通过流水线的方式复制多个副本以减少延时。master维护的元数据很多,需要设计高效的数据结构,且要保证占用内存小和支持快照 *** 作。支持COW的B树可以满足需求,但是实现确实相当复杂。
GFS的新颖之处并不在于它采用了多么令人惊讶的新技术,而在于它采用廉价的商用计算机集群构建分布式文件系统,在降低成本的同时经受了实际应用的考验。
如上图所示,一个GFS包括一个主服务器(master)和多个块服务器(chunk server),这样一个GFS能够同时为多个客户端应用程序(Application)提供文件服务。文件被划分为固定的块,由主服务器安排存放到块服务器的本地硬盘上。主服务器会记录存放位置等数据,并负责维护和管理文件系统,包括块的租用、垃圾块的回收以及块在不同块服务器之间的迁移。此外,主服务器还周期性地与每个块服务器通过消息交互,以监视运行状态或下达命令。应用程序通过与主服务器和块服务器的交互来实现对应用数据的读写,应用与主服务器之间的交互仅限于元数据,也就是一些控制数据,其他的数据 *** 作都是直接与块服务器交互的。这种控制与业务相分离的架构,在互联网产品方案上较为广泛,也较为成功。
⑷单master
只 有一个master也极大的简化了设计并使得master可以根据全局情况作出精密的块放置和复制决定。但是我们必须要将master对读和写的参与减至 最少,这样它才不会成为系统的瓶颈。Client从来不会从master读和写文件数据。Client只是询问master它应该和哪个 chunkserver联系。Client在一段限定的时间内将这些信息缓存,在后续的 *** 作中Client直接和chunkserver交互。
以图1解释一下一个简单的读 *** 作的交互。
⒈client使用固定的块大小将应用程序指定的文件名和字节偏激轮移转换成文件的一个块索引(chunk index)。
⒉给master发送一个包含文件名和块索引的请求。
⒊master回应对应的chunk handle和副本的位置(多个副本)。
⒋client以文件名和块索引为键缓存这些信息。(handle和副本的位置)。
⒌Client 向其中一个副本发送一个请求,很可能是最近的一个副本。请求指定了chunk handle(chunkserver以chunk handle标识chunk)和块内的一个字节区间。
⒍除非缓存的信息不再有效(cache for a limited time)或文件被重新打开,否则以后对同一个块的读 *** 作不再需要client和master间的交互。
通常Client可以在一个请求中询问多个chunk的地址,而master也可以很快回应这些请求。
⑸块规模
块规模是设计中的一个关键参数。我们选择的是64MB,这比一般的文件系统的块规模要大的多巧铅喊。每个块的副本作为一个普通的Linux文件存储,在需要的时候可以扩展。
块规模较大的好处有:
⒈减少client和master之间的交互。因为读写同一个块只是要在开始时向master请求块位置信息。对于读写大型文件这种减少尤为重要。即使对于访问少量数据的随机读 *** 作也可以很方便的为一个规模达几个TB的工作集缓缓存块位置信息。
⒉Client在一个给定的块上很可能执行多个 *** 作,和一个chunkserver保持较长时间的TCP连接可以减少网络负载。
⒊这减少了master上保存的元数据(metadata)的规模,从而使得可以将metadata放在内存中。这又会带来一些别的好处。
不利的一面:
一个小文件可能只包含一个块,如果很多Client访问该文件的话,存储这些块的chunkserver将孝野成为访问的热点。但在实际应用中,应用程序通常顺序地读包含多个块的文件,所以这不是一个主要问题。
⑹元数据(metadata)
master 存储了三种类型的metadata:文件的名字空间和块的名字空间,从文件到块的映射,块的副本的位置。所有的metadata都放在内存中。前两种类型 的metadata通过向 *** 作日志登记修改而保持不变, *** 作日志存储在master的本地磁盘并在几个远程机器上留有副本。使用日志使得我们可以很简单 地、可靠地更新master的状态,即使在master崩溃的情况下也不会有不一致的问题。相反,master在每次启动以及当有 chunkserver加入的时候询问每个chunkserver的所拥有的块的情况。
A、内存数据结构:
因为metadata存储在内存中,所以master的 *** 作很快。进一步,master可以轻易而且高效地定期在后台扫描它的整个状态。这种定期地扫描被用于实现块垃圾收集、chunkserver出现故障时的副本复制、为平衡负载和磁盘空间而进行的块迁移。
这 种方法的一个潜在的问题就是块的数量也即整个系统的容量是否受限与master的内存。实际上,这并不是一个严重的问题。Master为每个 64MB的块维护的metadata不足64个字节。除了最后一块,文件所有的块都是满的。类似的,每个文件的名字空间数据也不足64个字节,因为文件名 是以一种事先确定的压缩方式存储的.如果要支持更大的文件系统,那么增加一些内存的方法对于我们将元数据(metadata)保存在内存中所获得的简单 性、可靠性、高性能和灵活性来说,这只是一个很小的代价。
B、块位置:
master并不为chunkserver所拥有的块的副本的保存一个不变的记录。它在启动时通过简单的查询来获得这些信息。Master可以保持这些信息的更新,因为它控制所有块的放置并通过HeartBeat消息来监控chunkserver的状态。
这样做的好处:因为chunkserver可能加入或离开集群、改变路径名、崩溃、重启等,一个集群中有成百个server,这些事件经常发生,这种方法就排除了master与chunkserver之间的同步问题。
另一个原因是:只有chunkserver才能确定它自己到底有哪些块,由于错误,chunkserver中的一些块可能会很自然的消失,这样在master中就没有必要为此保存一个不变的记录。
C、 *** 作日志:
*** 作日志包含了对metadata所作的修改的历史记录。它作为逻辑时间线定义了并发 *** 作的执行顺序。文件、块以及它们的版本号都由它们被创建时的逻辑时间而唯一地、永久地被标识。
*** 作日志是如此的重要,我们必须要将它可靠地保存起来,并且只有在metadata的改变固定下来之后才将变化呈现给用户。所以我们将 *** 作日志复制到数个远程的机器上,并且只有在将相应的日志记录写到本地和远程的磁盘上之后才回答用户的请求。
Master可以用 *** 作日志来恢复它的文件系统的状态。为了将启动时间减至最小,日志就必须要比较小。每当日志的长度增长到超过一定的规模后,master就要检查它的状态,它可以从本地磁盘装入最近的检查点来恢复状态。
创 建一个检查点比较费时,master的内部状态是以一种在创建一个检查点时并不耽误即将到来的修改 *** 作的方式来组织的。Master切换到一个新的日志文件并在一个单独的线程中创建检查点。这个新的检查点记录了切换前所有的修改。在一个有数十万文件的集群中用一分钟左右就能完成。创建完后,将它写入本地和 远程的磁盘。
⑺数据完整性
名字空间的修改必须是原子性的,它们只能有master处理:名字空间锁保证了 *** 作的原子性和正确性,而master的 *** 作日志在全局范围内定义了这些 *** 作的顺序。
文件区间的状态在修改之后依赖于修改的类型,不论 *** 作成功还是失败,也不论是不是并发 *** 作。如果不论从哪个副本上读,所有的客户都看到同样的数据,那么文件 的这个区域就是一致的。如果文件的区域是一致的并且用户可以看到修改 *** 作所写的数据,那么它就是已定义的。如果修改是在没有并发写 *** 作的影响下完成的,那么受影响的区域是已定义的,所有的client都能看到写的内容。成功的并发写 *** 作是未定义但却是一致的。失败的修改将使区间处于不一致的状态。
Write *** 作在应用程序指定的偏移处写入数据,而record append *** 作使得数据(记录)即使在有并发修改 *** 作的情况下也至少原子性的被加到GFS指定的偏移处,偏移地址被返回给用户。
在一系列成功的修改 *** 作后,最后的修改 *** 作保证文件区域是已定义的。GFS通过对所有的副本执行同样顺序的修改 *** 作并且使用块版本号检测过时的副本(由于chunkserver退出而导致丢失修改)来做到这一点。
因为用户缓存了会位置信息,所以在更新缓存之前有可能从一个过时的副本中读取数据。但这有缓存的截止时间和文件的重新打开而受到限制。
在修改 *** 作成功后,部件故障仍可以是数据受到破坏。GFS通过master和chunkserver间定期的handshake,借助校验和来检测对数据的破坏。一旦检测到,就从一个有效的副本尽快重新存储。只有在GFS检测前,所有的副本都失效,这个块才会丢失。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)