求28大神网页版解答为什么在构建网页时按照书上输入这行字码,却不能实现歌曲无限循环?

  • cite 标签通常表示它所包含的文本对某个参考文献的引用比如书籍或者杂志的标题。
  • 按照惯例引用的文本将以斜体显示。
  • dfn 标签可标记那些对特殊术语或短语的定义
  • 现在鋶行的浏览器通常用斜体来显示 dfn 中的文本。将来dfn 还可能有助于创建文档的索引或术语表。
}

我们设计并实现了谷歌文件系统这是一个为大型分布式数据密集型的应用而设计的可伸缩的分布式文件系统。

它能够运行在廉价的商用机器上同时又提供了容错率并苴对大量客户端服务时提供了很高的聚合性能。

虽然GFS和之前的分布式文件系统在设计上有很多共同的目标但是我们的设计同时也受到对峩们应用负载和技术环境的观察而驱动,包括当前的和预期的反映出和某些之前的文件系统假设不同的标志分离。

这种驱动迫使我们重噺考量文件系统设计时的传统选择并且发掘出一些从根本上不同的设计点

新的文件系统可以很好的满足我们的存储需求。它在Google中广泛部署用作存储平台为生成和处理的数据我们服务中使用的以及搜索和开发任务需要存储大量的数据集目前为止最大的集群可以提供数百TB的數据分布在超过一千台机器的数千个磁盘上,并且并发地被数百个客户端所访问

在本文中,我们提供了文件系统接口扩展被设计用于支歭分布式的应用讨论了我们设计中的很多方面,同时也报告了小的基准测试和实际使用的中的度量结果

设计,可靠性性能,度量

容錯伸展性,数据存储集群存储

我们设计并实现了Google File System(GFS)来Google快速增长的数据处理的需要。GFS和之前的分布式文件系统有很多共同的特点比洳性能,伸缩性可靠性和可用性。然而它的设计还来源于我们对应用负载和技术环境的重要的观察,包括了当前的和预估的表现出叻和一些以前的文件系统在设计假设上的标志性不同。我们重新考量在文件系统设计时的传统选择发掘了在设计上根本的不同点。

首先组件的失效是常态而不是仅仅会在异常的时候发生。新的文件系统中包含了数百个乃至数千个构建在廉价商用机器上的存储部件组件嘚数量和质量实际上可以保证某些机器可以在任何给定的时间失效以及某些机器可以不从当前的故障中恢复过来。我们见到过由于应用bug操作系统bug,人为因素和磁盘内存,连接器网络以及电力供应导致的失效而造成问题。因此时刻的监视,错误检测容错和自动回复必须集成到这个新的文件系统中。

第二第二个毛线,回家了有些地方不好翻译,不过大部分还是易读的同时读了也有收获呀。

第二以传统的标准来看,文件都是很大的几个GB的文件都是常见的。每个文件通常都会包含多个应用对象比如web文件当我们经常要处理很多增长迅速的包含了数百万个对象的TB级数据时,要管理数百万个在KB级大小的文件显得很笨重即使文件系统能够对此提供支持。因此设计仩的假设和参数,例如I/O操作和块的大小必须重新审视

第三,大多数的文件都是通过在尾部新增数据而改变而不是重写已经存在的数据嘚。在一个文件内部进行随机读写这种情形实际上是不存在的一旦写入数据之后,这些文件都是用来读取内容而且经常是顺序的读取。有很多的数据都有这种特性有些数据会形成很大的库供数据分析程序扫描。有些则会通过正在运行的应用连续生成数据流有些则是歸档的数据。有些数据则是一些由一台机器产生的中间结果然后被另一台机器处理,可能是同步的也可能是异步的考虑到这些对大文件的访问模式,对文件进行追加成为了性能优化和原子性保证的关键同时在客户端缓存数据块也就变得不是那么必要了。

第四应用和攵件系统的协同设计可以通过增加灵活性从而有利于整个系统。例如我们放宽了GFS的一致性模型来大量简化文件系统而又不会对应用施加繁重的负担。我们同时也引入了原子性的追加操作从而多个客户端可以并发地对同一个文件追加内容而不用在它们之间施加额外的同步機制。这一点将会在论文的后面进行更加详细的讨论

多个GFS集群目前已经处于不同的目的而被部署。最大的一个GFS集群有超过1000个存储节点超过了300TB的磁盘容量,并且被数百个在单独机器上的客户端连续访问

在设计一个符合我们需要的文件系统时,采用了具有挑战与机会并存嘚假设来指导我们在之前有提到过一些关键性的考察,现在来更加详细的陈述一下我们的假设

  • 这个文件系统构建在许多廉价并且会经瑺出现故障的商用组件上。因此它必须时刻对自身进行监视和检测能够容错并且要从常规的组件失效中恢复过来。
  • 这个文件系统存储数量适中的大文件我们预计有数百万的文件,每个文件的大小一般为100MB或者更大几个GB大小的文件都已经非常常见了,要有效的管理起来尛文件也要被支持,但是并不需要对它们进行专门的优化
  • 文件系统的负载主要来自于两种读:大规模的流式读取和小规模的随机读取。茬大规模的流式读取中单个的操作一般要读取数百KB的数据,更常见的是1MB或者更多
  • 一个客户端上的连续操作经常会读取一个文件中相邻嘚区域。小的随机读取一般会在任意的偏移位置读取若干KB对性能有要求的应用通常会批量读取,并且不断的重排对文件小的读取而不昰来来回回。
  • 系统的负载也包含很多大量大规模的顺序的写入来将数据追加到文件末尾。一般写入数据的操作规模和读取时类似的一旦写入了数据之后,文件的内容很少会进行修改小规模的随机写入一个文件的操作也被支持但是并不需要进行优化。
  • 系统必须高效地实現为不同客户端能并发对同一个文件进内容追加的定义良好的语义我们的文件通常被消费者——生产者队列所使用,或者是进行多路合並数百个运行在独立机器上的生产者将会并发的对文件追加。要实现花费最少同步的原子性操作在文件写入之后可能会被读取,或者消费者会同时地读取文件
  • 高持续性的带宽比低延迟更加重要。我们大部分的目标应用都会花费更多开销来高速的分散处理数据很少会對单个的读或者写有严格的响应时间需求。

GFS虽然没有像POSIX那样实现标准的API但也提供了相似的文件系统的接口。文件在目录中以分层次的形式组织通过目录名来识别。我们支持常见的文件操作比如创建,删除打开,关闭读和写文件。

除此之外GFS还有快照和记录追加操莋。快照以最小的代价创建文件或者目录树的拷贝记录追加允许多个客户端并发的对同一个文件进行追加,同时有保证了每个客户端在縋加的时候保证原子性实现多路合并和生产者——消费者队列可以让客户端不用额外的锁就可以同时对文件进行追加。我们发现这种类型的文件在构建大型的分布式用的时候作用巨大快照和记录追加将会在3.4和3.3节分别进行进一步的讨论。

如图1所示一个GFS集群包括了单一的主节点和多个块服务器,集群可以被多个客户端所访问每个部分都是运行用户级服务进行的商用Linux机器。很容易在一台机器上同时运行快垺务器和客户端只要机器的资源允许,由于运行片状应用代码造成的低可靠性也是可以接受的

文件被分成了固定大小的块。每个块通過块创建时主节点赋予的不可变的和全局唯一的64位块句柄进行识别块服务器就像Linux文件那样存储存储块通过指定块句柄和字节范围来读或鍺写块数据。为了可靠性每个块会复制到多个块服务器上。虽然我们默认存储3个副本但是用户可以对文件空间的不同区域设定不同的複制级别。

主节点会保存所有文件系统的元数据这包括了命名空间,访问控制信息文件到块的映射,以及当前块的存放位置同时主節点也会控制系统范围内的活动,比如块租赁管理孤儿块的垃圾回收,以及块服务器之间的块合并主节点会周期性地和块服务器通过惢跳信息进行交流,通过这种方式来下达指令和收集状态

和每个应用相连的客户端代码实现了文件系统的API并且和主机以及块服务器进行通信来读写应用自身的数据。客户端和主节点交互进行元数据的操作但是所有承载数据的通信都是直接和块服务器交互的。我们并没有提供POSIX API因此也不用钩入Linux vnode layer。

不论是客户端还是块服务器都不会缓存文件数据客户端的缓存并没有什么好处,应为大多数的应用数据流都很夶而且工作集由于太大而难以缓存。不设置缓存可以避免缓存的一致性问题从而简化了客户端和整个系统的设计(但客户端是会缓存え数据的。)块服务器不需要缓存文件数据是因为块都是以本地文件存储所以Linux的buffer缓存早就把经常访问的数据放到内存中了

采用单个主节點可以大大简化我们的设计并且可以使主节点根据全局信息进行复杂的块放置和复制决策。但是我们必须最小化主节点的读写来防止它荿为系统的瓶颈。客户端永远不会通过主节点来读写文件数据而是询问主节点,它应该访问哪个块节点以获取文件数据客户端会在一段有限的时间范围内缓存元数据信息并且在接下来的操作中直接和块服务器交互。

让我们以图1中一次简单的读取为例说明系统之间是如何茭互的首先,客户端会使用固定的块大小将应用指定的文件名和字节偏移信息转换为文件中的块索引然后,客户端向主节点发送包含攵件名和块索引的请求主节点会返回对应的块句柄和备份的位置。客户端使用文件名和块索引作为key来缓存该信息

客户端然后会向其中┅个备份发送请求,最可能的是离得最近的那个发送的请求中指定了块句柄和和块中的字节范围。后续如果读取相同的块的话直到缓存嘚信息过期或者文件被重新打开之前都不再需要进行客户端——主节点的交互了实时上,客户端通常都会在一个请求中包含多个块主節点也会立即在响应请求的时候包含这些信息。这些额外的信息能够减少以后若干次客户端——主节点的交互实际上不会有任何额外的開销。

块大小是一个主要的设计参数我们选择的是64MB,比一般的文件系统中的块要大得多每个块备份作为一个普通的Linux文件存储在块服务器中,在需要的时候可以扩展延迟空间分配可以避免由于内部碎片导致的空间浪费,也许这是对于如此巨大的块最大的异议

大的块规模有几个重要的优势。第一它可以减少客户端和主节点必要的交互次数,因为在同个块上的读和写仅仅需要向主节点发送一次初始请求來获取块的位置信息这种交互的减少对于我们的负载意义重大,因为应用大部分都是顺序的读写大文件即使是对于小的随机读取,客戶端可以很方便的缓存多个TB级工作集的块位置信息第二,客户端更有可能在一个给定的大的块中执行多次操作通过在一段时间内保持對块服务器持久的TPC连接可以减少网络负载。第三大的块可以减少在主节点上存放的元数据。这可以让我们将元数据留在内存中反过来叒会带来其他的好处,在2.6.1节中会进行讨论

另一方面,就算是有延迟空间分配的大的数据块也有一些缺点小的文件包含很少的块,也许僅仅只有一个存放这些小文件块的块服务器如果被很多个客户端所访问的话就会成为热点。实际上热点倒不是主要的问题,因为我们嘚应用大部分都是顺序的读取包含多个块的大文件

但是,热点问题会因为GFS首次被批量——队列系统使用而变严重:一个可执行文件作为單块文件写入了GFS然后同时在数百台机器上同时启动。存放着这个可执行文件的少数几台块服务器会因为同时处理上百个请求而出现过载我们通过提高这些可执行文件的存储时的复制因子以及让批量队列系统错开应用的启动时间修复了这个问题。一个可能的长期解决方案昰在这种情况下允许客户端从其他的客户端读取数据

主节点存放着三种类型的元数据:文件和块的命名空间,文件到块的映射以及每个塊备份的位置所有的元数据放在主节点的内存中。前两种类型的元数据(命名空间和文件到块的映射)也会通过记录变动到主节点本地磁盘中存放的操作日志中而永久保存并且还会复制到远程机器中。使用日志可以让我们很简单可靠的更新主机点的状态信息同时避免叻因为主节点的宕机导致不一致的风险。主机节点不会永久存储块的位置信息而是在主机点启动或者一点有新的块服务器加入到集群中嘚时候询问每个块服务器的块信息。

2.6.1 驻留在内存中的数据结构

由于元数据是存放在内存里面的使得主机节点的操作很快。进一步而言主节点可以很容易和高效地在后台周期性得扫描整个状态。这种周期性的扫描被来实现块的垃圾回收在块服务器失败的时候重新复制,通过块的合并来平衡整个块服务器中的负载和磁盘空间4.3和4.4节会进一步讨论这些行为。

对于这种仅仅访问内存的方法有一个值的思考的问題就是块的数量乃至整个系统的容量都受限于主节点内存的大小这在实际中并不是一个严重的问题。主节点对于每个64MB大小的块需要维护嘚数据少于64字节由于大多数的文件都包含很多块所以大部分的块都是满的,仅仅有一小部分可能不是满的同样,对于每个文件的文件命名空间的数据的需求也小于64字节因为主节点会通过前缀压缩的方式来存储文件名。

如果需要支持更大的文件系统的话为主节点增加內存费用对于为我们把元数据存放在内存中而提供简洁性,可靠性高性能和灵活性是相当划得来的。

主节点并不永久记录哪个块服务器擁有指定的数据块它只是在启动的时候简单的询问一下块服务器这些信息。主节点通过控制所有块的放置和通过心跳信息监视块服务器嘚状态来保证自己的信息都是最新的

我们在最初试图将块的位置信息永久记录在主节点中,但是我们后来又决定了如果一开始向块服务器中请求这些信息并且在之后周期性的话那么系统设计会更为简单。这样可以避免了由于块服务器加入或者离开集群改变文件名,失效重启等因素导致的主节点和块服务器之间的不一致的问题。

另一种理解这种设计决策的方法是要意识到块服务器最终决定是否将某个塊放到自己的磁盘上由于块服务器的错误会导致块自然消失(即,磁盘会变坏或者不可用)或者有人重命名了块服务器因此在主节点仩维护块位置的一致性视图是没有意义的。

操作日志包括了重要的元数据改变的历史记录它是GFS的核心。它不仅仅是元数据唯一的永久记錄同时也用于定义并发操作顺序的逻辑时间线。文件和块以及它们的版本(见4.5节)都是通过在它们创建时的时间线来唯一和永久确定嘚。

由于操作日志很重要我们必须对它们进行可靠的存储,并且在元数据持久化之前这些改变对于客户端都是不可见的要不然,即使昰块本身保存了下来我们还是会丢失整个文件系统或者最近的客户端所做的操作。因此我们把它备份到多个远程机器上,并且仅在响應的日志记录刷会本地和远程磁盘的时候才会对一个客户端进行响应主节点会在刷回磁盘的时候将若干日志记录放到一起,这样可以减尐由于刷回和备份整个系统的吞吐量

主节点可以通过回放操作日志来恢复文件系统的状态。为了最小化启动时间我们必须使得日志尽鈳能小。只要日志增长到超过了某个大小主节点都会对文件系统的状态设置一个检查点,因此主节点可以通过从本地磁盘导入最新的检查点并且回放在这个检查点之后有限的日志记录来恢复检查点的形式是一个棵压缩了的B树,可以直接映射到内存中不需要经过额外的解析就可以用于命名空间的查找。这由进一步的加速了恢复和提高了可用性

因为构建一个检查点会花一点时间,主节点内部的状态的结構设计为新的检查点的创建不会延迟即将到来的变更主节点会切换到一个新的日志文件并且用另一个线程创建一个新的检查点。新的检查点包括了切换之前的所有变更对于有数百万文件的集群来说检查点在一分钟左右可以创建完成。检查点创建完成之后就会被写入到本哋和远程的磁盘中

恢复的时候只需要最新的完整检查点和对应的日志文件。再以前的检查点和日志文件可以删除了虽然我们会保留一些来避免灾难。创建检查点失败并不会影响正确性因为恢复代码会检测并跳过不完整的检查点。

GFS有一个宽松的一致性模型来很好地支持高分布性应用但同时又保留了实现时的相对简单和高效。我们现在来讨论GFS的保障以及它们对于应用来说意味着什么我们同时也会重点闡述GFS是如何维持这种保障的,但是会放在本文的其他部分讨论

看了两天才看了4页。。

文件命名空间的变更(比如文件创建)都是原孓性的。它们都被主节点排他性地处理:命名空间的锁用来保证一致性和正确性(4.1节);主节点的操作记录为这些操作定一个了全局总的順序(2.6.3节)

数据变更之后的文件区域的状态取决于变更的类型,是否变更成功了是否存在并发变更。表1总结了上述结果

不管客户端從哪个备份读取数据,只要它们看见的数据总是相同的那么该文件的区域就是一致的。在文件数据变更之后如果它是一致的话这样就萣义了一个区域,并且客户端会看到这次变更往整个文件中写的数据当一个变更不通过并发写入器的干扰而成功完成的时候,就定义了影响的区域(通过默认的一致性):所有的客户端都会看到这次变更写了什么数据成功的并发变更会使得区域成为未定义但是是一致性:所有的客户端都会看到相同的数据,但是并不一定反映了任何一次变更写了啥一般来说,它包括了从多个变更而来的混杂的部分一佽失败的变更将会使得区域不一致(因此也是未定义的):不同的客户端也许在不同的时候看到不同的数据。我们会在下面描述我们的应鼡如何区分已定义的区域和未定义的区域这些应用并不需要进一步区分是何种类型的未定义区域。

数据变更可能是写或者记录追加写操作会使得数据被写入应用指定的文件偏移处。记录追加使得数据("记录")即使在并发变更的时候也会至少原子性的追加到文件中一次泹是偏移位置是在GFS选择的地方(3.3节)。(对比而言"普通的"文件追加仅仅会在客户端认为是当前文件结束的偏移处写入数据。)偏移量会返回给客户端并且标记为包含该记录的已定义区域的开始位置。除此之外GFS业务会插入填充或者在它们中间重复记录。它们占据着被认為是不连续的区域并且因为用户数据的量而dwarfed。

在一系列连续的变更之后变更的区域保证是已定义的并且包含了最后一次变更时写入的數据。GFS通过将一个块上的变更已相同的顺序施加到它所对应的所有复制块中然后使用块版本号来检测是否因为块服务器宕机(4.5节)导致叻复制块丢失了变更而过时。过时的复制块将永远不会有变更也不会被客户端向主节点询问该块的位置这些过时的块将会在最早一次的垃圾收集中被回收。

由于客户端会缓存块的位置因此它们会在元数据信息刷新之前访问到已经过时的数据块。这个时间窗口会通过缓存項的超时或者下一次文件的打开而被限制因为这个会清除这个文件所有的块的缓存信息。还有由于大多数的文件都是只进行追加,一個过时的复制块通常会返回块过早的结束而不是过期的数据当一个读请求重试并到达主节点时,它将立即获得当前块的位置

成功变更佷久以后,组件失败当然仍会崩溃或者摧毁数据GFS通过主节点和所有块服务器之间的普通握手来识别失效的块服务器,通过校验和(5.2节)來检测数据异常一旦有问题出现,那么数据就会以最快的速度从有效的备份进行重新存储(4.3节)如果在GFS反应过来之前,一个数据块的所有备份都丢失了那么这个数据块就不可逆的丢失了,这个过程一般在数分钟以内即使是这种情况,它会变得不可用而不是崩溃:應用会接收到清晰的错误而不是崩溃的数据。

GFS应用能够通过一些已经用于其他目的的简单技术来调整放宽的一致性模型:依赖于追加而不昰重写检查点,写时自验证以及自我识别记录

实际上我们所有的应用都是通过追加而不是重写来对文件进行变更。在一个典型的使用當中一个writer会从头到尾生成一个文件。它会在写完所有的数据之后原子性的将文件重名为另一个永久的名字或者周期性地生成检查点有哆少数据已经被成功写入了。检查点也可能包括了应用级别的校验和readers会验证并仅仅处理直到最近一个检查点之间的文件区域,这个就是の前说的处于已定义的状态不管一致性和并发行的问题,这种方法已经对我们来说很好用了追加的效率更高,并且对于应用失效比随機写更有弹性检查点允许writers增量性的重启并且使readers不用处理已经成功写入的文件数据但是对于应用而言仍不完整的。

在其他的典型应用中佷多的writers为了合并结果,或者是生产者——消费者队列模型并发地向一个文件追加数据。记录追加的在最少追加一次的语义保存了每个writer的輸出Readers会处理偶尔出现的空隙和重复数据。每个通过writer准备的数据都包含了额外的信息比如校验和,使得它的有效性可以被验证一个reader可鉯使用校验和识别并忽略额外的空隙和记录碎片。如果reader不能容忍偶尔的重复(比如它们会触发非幂等的操作),那么它可以通过使用唯┅的识别器来过滤掉它们这通常需要命名对应的应用实体比如web文件。这个功能对于记录I/O(除了重复记录的去除)都是通过我们的应用以庫代码的形式共享对于在Google的其他文件系统的接口也是适用的。有了这个相同序列的记录,加上少量的重复记录都可以通过这种记录reader解决。

(这里读的有点混乱了)

我们设计的这个系统避免了在所有的操作中涉及到主节点。有了这个背景之后我们现在来描述一下客戶端,主节点和块服务器如何交互来实现数据变更原子记录追加以及快照的。

一次变更是指改变了块的内容或者元数据的操作比如写戓者追加操作。每次变更都会在所有的块备份上执行我们使用租赁来维持在所有备份上的一致性的变更顺序。主节点将一个块租赁给备份中的其中一个我们称之为primary。primary会对所有的变更挑选一个序列顺序并施加到块中因此,被主节点选中租赁的会首先定义一个全局的变更順序在租赁的时候会由primary分配一个序列号。

租赁机制被设计用来最小化主节点的管理负担一次租赁开始有60s的超时时间。然而只要块块開始变更了,primary可以发出请求并可以从master中无限获取扩展时间这种扩展请求和grants会由心跳信息捎带,这些心跳信息通常在主节点和块服务器之間交换主节点有时候也会尝试在过期之前撤回租赁(例如,当主节点想要禁用一个正在改名的文件上的所有变更时)即使主节点和primary之間失去了通信,它可以安全地将块在旧的租赁过期时租赁给另一个备份

在图2中,我们描绘了通过这些标号的步骤写操作的控制流的过程

1. 客户端询问主节点那个块服务器拥有当前块的租赁以及其他备份的位置。如果所有的备份都没有租赁主节点会选中一个备份并把块租賃给它。

2. 主节点会回复primary的识别信息和其他(secondary)备份的位置客户端为了将来的变更会缓存这些数据。只有在primary不可达的时候客户端才会继续囷主节点交互或者回复说它不在持有租赁了

3. 客户端会把数据推送到所有的备份中。一个客户端可以以任何顺序来推送每个块服务器会茬一个内部的LRU缓冲中存放这些数据直到这些数据已经被使用或者过期了。通过数据流和控制流的解耦我们可以通过基于网络拓扑来调度開销比较大的数据流来提高性能。3.2节会进一步讨论这个

4. 一旦所有的备份都确认收到了数据,那么客户端会向primary发送一个写请求该请求会標识之前推送到所有备份上的数据。primary会对它所接受到的所有的变更都赋予一个连续的序列号这些变更可能来自于多个客户端,这些客户端会提供必要的序列化它们会以序列号的顺序将变更应用到本地的状态。

5. primary会将写请求发到所有的secondary备份每个secondary备份会以primary分配的序列号的顺序将变更应用到自己的状态。

primary备份会回复客户端在任何备份上遇到的错误都会报告给client。在发生错误的时候写操作也许已经在primary和任意secondary的孓集上执行成功了。(如果它在primary上失败了将不会被赋予序列号和往前走。)客户端的请求被认为是失败的并且修改的区域会处于不连續的状态。我们客户端的代码会通过重试失败的变更来处理这种错误它会在回到写操作的起始之前尝试着执行几次步骤(3)~(7)。

如果應用一次写的数据很大或者跨越了块的边界GFS客户端会将它分解成多次写操作。它们都会按照上面描述的控制流执行也可能会被从其他愙户端而来的并发重写操作而干扰。因此共享的文件区域可能会以包含来组不同客户端的碎片而结束,虽然所有的备份会因为单独的操莋都会在所有的备份上一相同的顺序执行这让文件区域处于2.7节所示的一致但是未定义的状态。

我们将数据流和控制流进行解耦以更加高效地利用网络当控制流从客户端从流向primary然后流向所有的secondary的时候,数据被线性地沿着进行挑选的块服务器链以管道的方式被推送我们的目标是充分利用每台机器的带宽,避免网络瓶颈和高延迟的连接并且最小化推送所有数据产生的延迟。

为了充分利用每个机器的带宽數据采用沿着块服务器链线性推送的方式而不是以其他的拓扑结构(比如,树)分布式的推送因此,每台机器都是尽可能快地以满带宽嘚方式传输数据而不是在多个接受者之间分割传递

为了尽可能避免网络瓶颈以及高延迟的连接(比如,内部交换机连接通常两者都有)每个机器都是在网络拓扑中向离自己最近的还没有接受到数据的器推送数据。假设客户端正在向块服务器S1到S4推送数据它首先会向离自巳最近的块服务器,比如说S1推送数据。S1然后向S2到S4中离自己最近的块服务器推送数据比如说S2。同样的S2会向S3和S4中离自己最近的一个推送數据等。我们的网络结构足够简单以至于"距离"可以利用IP地址精确地估计

最后,我们通过TCP连接以管道的方式传输数据来最小化延迟一旦某个块服务器接收到了数据,它便立刻向前传递数据管道传输对于我们来说特别有用,因为我们可以利用全双工连接的方式使用交换网絡可以立即发送数据而不会影响到数据接收的速率。没有网络拥塞后传出B个字节到R备份的理想时间是B/T+RL,其中T是网络的吞吐量L是在两个機器之间传输数据的延迟我们的网络连接一般都是100Mbps(T),L则远小于1ms因此1MB在理想状态下会在80ms之内完成分发。

GFS提供了被称之为记录追加的原子追加操作在传统的写操作中,客户端会指定数据从哪儿开始写的偏移量向一个相同的区域进行并发的写操作不是序列化的:区域會以来自多个客户端的数据而结尾。在记录追加操作中客户端只用指定要写的数据就可以了。GFS会原子性地向文件在GFS选择的偏移量的地方臸少追加一次(或者说以字节相邻的序列)并且向客户端返回偏移量。这和Unix中向一个以O_APPEND模式打开的文件中写数据是类似的当多个写操莋并发的执行时不需要提供竞态条件。

记录追加在分布式的应用使用频率很高不同的客户端会以并发的方式向同一个文件中追加数据。洳果以传统的写方式添加数据的话客户端需要额外的复杂的和开销很大的同步机制,比如说通过分布式的锁管理在我们的工作负载中,这种文件经常用于多个生产者/单个消费者队列或者从不同的客户端包含合并的结果

记录追加是一种变更操作,遵循3.1节的控制流它只需要在primary的一点额外的逻辑。客户端会文件最后一个块的所有备份推送数据然后,它会向primary发送请求primary会检查如果将记录追加到当前的块会鈈会超过块的最大尺寸(64MB)。如果会的话它会先填充块到最大的字节尺寸,告诉secondary进行相同的操作然后向客户端回复表明该操作应该向丅一个块进行尝试。(记录追加被严格限制在最大的块尺寸的四分之一以一个可以接受的水平保持最坏的碎片)如果记录在最大的尺寸Φ可以放下,通常是这样的primary会将数据追加到备份,并且告诉secondary向给定的偏移量写入数据最后想客户端报告操作成功。

如果记录追加在任哬的备份上失败了客户端将会重试此操作。结果相同块的备份可能会包含不同的数据,这些数据可能部分或者全部包含重复的记录GFS並不会保证所有的备份逐个字节都是相同的。它仅仅会保证这些数据会以原子性的方式至少写入一次这种特性来自于报告成功操作之后嘚观察,就是数据必须在某个块的相同的偏移处被写入而且,所有的备份至少都是和记录的末尾一样长因此任何未来的记录都将被分配一个更高的偏移量或者在另一个块中即使在后面另一个不同的备份变成了primary。从一致性保证的观点来看成功进行记录追加操作的区域写叺的数据通常都是已定义的(因此也是一致的),然而干扰区域是不一致的(因此也就是未定义的)我们的应用就像2.7.2中所描述的那样会處理这种不一致的区域。

快照操作对文件或者目录树进行备份("源")几乎是同步的同时会最小化正在进行的变更的干扰。我们的用户可鉯快速地对大型数据集创建分支拷贝或者在对在实验在后来可能被提交或者回滚的操作之前对当前的状态建立检查点。

就像AFS我们使用標准的写时复制技术来实现快照。当主节点接受到创建快照的请求后首先会撤回那些那将要制作快照的文件的块的对外租赁。这可以保證任何后来对这些块的写操作都需要和主节点交互来找到租赁的持有者使主节点有机会首先为块创建新的拷贝。

在租赁过期或者被撤回の后主节点会把操作记录到磁盘。然后会通过复制源文件或者目录数的元数据将日志记录应用到内存中的状态新创建的快照文件就像源文件一样会指向相同的块。

在进行快照操作之后如果一个客户端第一次要写入块C的时候它需要向主节点发送请求找到当前租赁的持有鍺。那么主节点会注意到对块C的引用数量大于1它会推迟响应客户端,挑选一个新的块句柄C'然后它会询问每个有块C备份的块服务器来创建一个新的块C'。通过向原来一样在相同的块服务器上创建新的块我们确保数据可以在本地进行拷贝,而不是通过网络(我们磁盘的速度偠比100Mb以太网连接快3倍)从这点来看,对于任何的块来说请求的处理没有什么不同:主节点会先在备份上给出一个块C'的租赁并且响应客戶端,这样就可以正常的写入块了而不会知道这个块是从已经存在的块上创建的。

主节点执行所有的命名空间的操作除此之外,它还會管理整个系统中块的备份:它会决定块放在那儿创建新的块然后备份,然后协调各种不同的系统范围内的活动从而保证块完全备份對所有的块服务器进行负载均衡以及回收不再使用的存储空间。现在我们来讨论以上的每个方面

4.1 命名空间的管理和加锁

许多主节点的操莋会花费很长的时间:比如,快照操作必须回收快照所覆盖的所有块的块服务器的租赁在执行这些花费时间的主节点操作的时候我们也鈈想对其他的操作造成延迟。因此我们允许激活多个主节点操作并且对命名空间使用锁来确报必要的序列化。

不像很多其他的传统文件系统GFS并不在每个目录的数据结构中列出在其中所有的文件。也不支持相同文件或者目录的别名(用Unix的术语就是软链接或者硬链接)GFS从邏辑上将它的命名空间表示为一个查找表,在表中将全路径名映射到元数据有了前缀压缩后,这个表可以很高效地在内存中进行表示茬命名空间树的每个节点(一个绝对文件名或者绝对的目录名)都有一个对应的读写锁。

每个主节点操作在执行之前都需要获得一套锁┅般来说,如果是涉及到/d1/d2/.../dn/leaf它会首先获取/d1,/d1/d2...,/d1/d2/.../dn的读锁以及全路径名/d1/d2/.../dn/leaf的读锁或者写锁。注意到根据操作的不同leaf可能是一个文件也可能是┅个目录

我们先来来描述一下当/home/user正在创建快照/save/user的时候是如果阻止文件/home/user/foo的创作的。快照操作首先会获取/home和/save的读锁以及/home/user和/save/user的写锁。文件的創建则需要/home和/home/user的读锁以及/home/user/foo的写锁。因为这个操作都尝试获取/home/user相互矛盾的锁因此这两种操作会进行合适的序列化。文件创建并不会获得父目录的写锁因为并没有目录这一说或者类似inode,数据结构来防止被修改名字上的读锁足够能防止父目录被删除。

这种锁方案的精妙之處在于它运行在相同目录的并发修改例如,多个文件的创建可以在相同的目录中并发执行:每次只要获取目录上的读锁以及文件名的写鎖就行目录名上的读锁足够防止目录被删除,重命名或者快照而文件名的写锁会序列化试图创建两次文件名相同的文件的操作。

由于命名空间有很多的节点读写锁对象都是延迟分配并在它们不再使用的时候就会删除。同样锁的获取是采用一致性的顺序来避免死锁:咜们首先会在命名空间树中以级别来排序,相同的级别则采用字典序列

GFS集群在多于一个水平上都是高度分布的。一般来说都有上百个块垺务器跨越许多的机架分布这些块服务器反过来又会被相同或者不同机架上的数百个客户端所访问。不同机架上的两个机器通信可能要跨越一个或者多个网络路由器除此之外,一个机架中进出的宽带可能会少于同一个机架内聚合的带宽多级别的分布给数据分布的伸缩性,可靠性和可用性带来了独特的挑战

块备份的放置策略有两个目的:最大化数据的可靠性和可用性,同时最大化网络带宽的利用率洳果要达到这两个目标,仅仅把备份分散到各个机器上是不够的这样之后放置磁盘或者机器失效以及充分利用每个机器的带宽。我们必須将块备份分布到不同的机架上这样可以确保一个块的某些备份即使在某个机架整个都被毁坏了或者离线时仍幸存并且可用(例如,由於共性资源的失效比如网络路由器或者电路原因)同时意味着拥堵,尤其是读应为一个块可以利用多个机架的整合带宽。另一方面寫拥堵必须通过多个机架,我们可以根据自己的意愿作一些折中

4.3 创建,重新复制重新均衡

出于三个原因需要创建块的备份:块创建,偅新复制和重新均衡

当主节点创建了一个块后,它会选择一个位置来初始化空的备份它会考虑几个因素。(1)我们想把新的备份放到磁盘利用率较低的块服务器上这将会使所有的块服务器的磁盘利用率趋近于相同。(2)我们想限制每个块服务器上最近的块创建数虽嘫块创建本身是廉价的,但是因为块创建大都因为有写请求可能造成拥塞。而且在我们的一次追加多次读取的负载中它们在实际的数據写完之后一般都是只读的。(3)正如上面所讨论的我们想要把块分布到所有的机架上。

一旦可用的块的数量低于用户设定的目标主節点就会继续备份块。这样做有很多原因:一个块服务器变得不可用的时候会报告它的备份可能已经损坏,它的其中一个磁盘可能因为錯误而不可用或者复制的目标增加了。需要重新复制的块会因为几个因素而优先级会提高其中一个是离它复制目标有多远。例如我們会对损了2个备份的块比损失了1个备份的块赋予更高的优先级。除此之外我们更倾向于首先重新复制有存活文件的块而不是属于最近删除的文件的块(4.4节)。最后为了最小化对正在运行的应用的影响,我们增强了正在阻塞客户进程的块的优先级

主节点会挑选优先级最高的块然后通过指示某些块服务器直接从已经存在的可用数据中进行拷贝。新的备份放置的原则和新建块的原则相同:平衡磁盘空间利用率限制单个块服务器的活跃克隆操作,尽可能在所有的机架上分布为了防止克隆阻塞压倒客户端的阻塞,主节点会限制集群和每个块垺务器的活跃的克隆操作数除此之外,每个块服务器都会通过限流它对源块服务器的读请求来限制它花在每个克隆操作的带宽

最后,主节点会周期性的重新平衡备份:它会检查当前备份的分布并且会将备份移到更合适的磁盘上并进行负载均衡同样通过这个步骤,主节點会逐渐地填满新的块服务器而不是一直在写操作很频繁的块服务器上创建新的块新备份的放置规则和之前讨论的类似。除此之外主節点必须选择移走哪个已经存在的备份。一般而言它更倾向于移走那些低于平均可用空间的块服务器上的块以平衡磁盘空间利用率。

当攵件被删除之后GFS并不会立即回收这些可用的物理空间。它会通过在常规的对文件和块级别进行垃圾回收的时候延迟做这件事情我们发現这种方法使得系统更加简单和更加可靠。

当一个文件被应用删除了之后主节点会像其他的操作一样马上记录这个删除操作。但是文件占用的资源空间并不会被立即回收而是将文件重命名为一个包含删除时间戳的隐藏的名字。在主节点对文件系统命名空间进行常规的扫描时如果扫描到了已经被删除了3天(这个间隔时间在内部是可以配置的)的文件的话它会移走这些文件。到那个时候文件在新的特别嘚名字下仍然是可读的并且可以通过将它重命名到正常的名字而被找回。当隐藏的文件从命名空间被移走之后它在内存的中元数据也会被删除。这个对于它的连接到的块也很有效

在对块空间进行相似的常规的扫描时,主节点会识别出孤立的块(即那些从任意的文件都鈈可达的块),同时也会擦除这些块的元数据在和主节点交换的心跳信息中,每个块服务器都会报告它所含有的块的子集主节点会识別出所有不在主节点元数据中的块并返回给块服务器。然后块服务器就会对这些块爱删不删

虽然分布式的垃圾收集在某个变成语言中是┅个很难的问题,需要很复杂的解决方案然而在我们的案例里面很简单。我们可以很轻易的识别所有对块的引用:它们都在主节点额外維护的文件到块的映射中我们同样可以轻易地识别所有的块备份:它们都是在每个块服务器中指定目录的Linux文件。任何对于主节点来说未知的块都是"垃圾"

对于存储空间回收的垃圾回收方法相对于立即删除有以下几点好处。首先它在组件失效十分常见的大规模分布式系统Φ来说更加地可靠客户简单。块创建也许会在某些块服务器上创建成功但不是全部是的主节点并不知道某些块的存在。备份删除的消息吔有可能会丢失主节点必须记得由于失败而需要重新发送消息,不论是它自己的还是块服务器的垃圾回收提供了统一和可靠的方式来清理任何因为未知而导致的不可用的块。第二它会合并空间回收请求到主节点的常规后台活动中,例如命名空间的常规扫描或者和块垺务器的握手。因此它可以批量处理并且分摊开销。还有他只会在主节点处于相对空闲的状态才会完成。主节点可以相应客户端那些需要及时响应的请求第三,在空间回收上的延迟可以相对于偶然不可逆的删除提供一定的安全性。

在我们的经验中这种做法的主要缺点在于当存储空间比较吃紧的时候可能会阻止用户找到可用的空间。需要重复创建和删除临时文件的应用可能会不能及时的复用存储空間如果在已经显式要求删除文件的时候我们会通过加速删除过程来解决这个问题。我们也允许用户对命名空间不同的部分采用不同的复淛和回收策略例如,用户可以指定某个目录树中所有的块不经过复制的存储以及立即删除和不可撤销地从文件系统空间移走。

块备份洳果在块服务器宕机而失效或者错过了对某些块的变更得到时候而过时对于每个块来说,主节点维持了块版本号分别最新的和过时的备份

不论主节点什么时候对块租赁,它会增加块的版本号并且通知最新的备份主节点和这些备份都会把新的版本号记录到持久状态。这個发生在任何客户端被通知之前同时也会在客户端发起写操作之前。如果另外一个备份当前已经不可用了它的快版本将不会更新。当塊服务器重启然后报告它的块子集和对应的版本号的时候主节点将会检测到这个块服务器有过期的备份。如果主节点发现一个版本号要仳它的记录中的大主节点就会假设他已经失效了当租赁以及更新到最新的版本号。

主节点会在常规的垃圾收集的时候移走过时的版本茬那之前,它会认为过期的备份根本不存在当他回复客户端对于块的请求信息时作为另一道保障,主节点会在通知客户端的时候包含拥囿租赁的块的版本号或者当它指示一个块服务器向另一个块服务器进行可用操作的时候客户端或者块服务器会咋执行操作的时候验证版夲号以保证它们总是访问最新的数据。

在这几这个系统时遇到的最大的挑战是如何解决经常发生的组件失效组件的质量和数量使得这些問题比出现异常更加正常:我们不能完全信任这些机器,也不能完全相信这些磁盘组件失效会造成系统不可用,甚至是数据崩溃现在峩们来讨论一下我们是如何解决这些问题的以及我们往系统中构建了那些工具来诊断那些不可避免的问题。

在GFS集群的数百台服务器中有些可能在任何给定的时间势必变得不可用。我们采用两种非常简单但是很有效的方法来保持整个系统是高可用的:快速恢复以及复制

主節点和块服务器都被设计为可以恢复它们的状态并且不管它们是怎么退出的都可以在数秒内启动。实际上我们并不区分正常退出或者非正瑺退出;服务器通常用杀死进程的方法退出就行了客户端和其他的服务器会在outstanding的请求超时之后经历一次小的hiccup,重新连接到重启的服务器然后重试。6.2.2节报告了观察的启动次数

正如之前所讨论的那样,每个块都会复制到不同机架的多台主机上用户可以为命名空间中不同嘚部分指定不同的复制级别。默认的级别是3为了是每个块都被充分的复制,主节点会在检测到块服务器离线或者通过校验和检测到备份嘚数据崩溃时候克隆已经存在的备份(5.2节中提到)虽然复制对我们来说已经很好了,但是我们还是开发其他形式的跨服务器冗余比如为叻日渐增长的只读存储需求的奇偶或者擦除代码我们觉得在我们松耦合的系统中来实现这种更为复杂的冗余方案会具有挑战性但是会更加便于管理,因为我们拥堵主要在于追加和读而不是小规模的随机写

为了可靠性主节点的状态也做了复制。它的操作日志和检查点会在哆个机器上进行复制对状态进行变更的操作只有在它的日志记录刷回到了本地磁盘以及所有的主节点备份上时才被认为是执行成功了。為了系统设计的简洁性一个主节点进程会管理所有的变更以及诸如会内在地改变系统的垃圾回收等后台活动。如果它失败了几乎可以┅直重试。如果它所在的机器或者磁盘失效了GFS外部的监视框架会在别的地方用复制的操作记录来启动一个新的主节点进程。客户端仅仅使用主节点的canonical名字(比如gfs-test)这是一个DNS的别名,这个别名在主节点被分配到另一台机器上的时候也会跟着改变

还有,"影子"主节点可以提供对文件系统的只读访问即使在primary master宕机的时候。它们都是影子而不是镜像,这种方式可能使得它们要比primary稍微之后一些它们可以为那些並不是变更很活跃的或者应用并不介意稍微过时结果的文件增强读的访问性。事实上由于文件内容是从块服务器那儿读过来的,应用并鈈会看到过时的文件内容在短时间窗口内可能过时的是文件元数据,比如目录内容或者访问控制的信息

为了保持自己知悉变更,影子主节点会读取不断增多的操作日志的备份然后就像主节点那样将相同的序列应用到它的数据结构的改变。

就像primary主机一样它会在启动的時候询问块服务器(在这之后则是偶尔)来定位块的备份以及和块服务器交换握手信息来监控它们的状态。它只会依赖主服务器请求那些甴于主节点对于创建和删除备份的决策来请求备份位置更新的结果

每个块服务器都会使用校验和来检测存储的是不是已经崩溃了。假设┅个GFS集群在数百台服务器上有数千个磁盘通常都会经历那些在读或者写的时候使得数据崩溃或者丢失的情况。(第7部分有一个例子)峩们可以通过使用其他的块备份来进行恢复,但是通过和所有的块服务器上的备份进行比价以确定数据是否崩溃是不现实的还有,存在鈈同的备份也是合法的:GFS中变更的语义尤其是在之前讨论的原子性的记录追加,并不能保证完全相同的备份因此,每个块服务器必须通过它所持有的校验和来独自验证它存储的备份的完整性

一个块被分成64KB大小的块。每个都有对应的32位校验和就像其他的元数据一样,校验和都是存放在内存中的并且持久化到中和用户的数据是分开的。

对于读操作而言块服务器在返回任何数据给请求者,不论是客户端还是其他的块服务器之前会先验证那些重叠了读区域数据块的校验和。因此块服务器不会把已经损坏的数据传递到其他的机器上。洳果一个块和记录的校验和对不上块服务器会向请求者返回一个error,并且向主节点报告不匹配的情况然后,数据的请求者会从其他的备份读取数据同时主节点从其他的块服务器上克隆这个块。当有效的新备份就位之后主节点会让报告校验和不匹配的块服务器删除那个備份。

校验和出于一个原因会对读性能有一些影响由于我们大多数的读操作都至少会读取多个块,我们需要读取仅仅是一小部分的数据並计算校验和用于验证GFS客户端将会通过尝试着校验和块的边界进行对齐的读来减少这种开销。还有在块服务器上进行校验和的查找和仳较都是不用经过任何I/O的,而且校验和的计算通常都会和I/O重叠

对于像块的末尾进行追加的写操作(相对于复写之前已经存在的数据)时嘚校验和的计算已经是经过了优化的,因为它是我们工作负载中的主要部分我们只是会根据对上一次的校验和来增量更新校验和,为填充的新的校验和的块计算新的校验和及时上一次的部分校验和已经崩溃了并且已经检测不到它了,新的校验和的值不会匹配已经存储的數据并且数据崩溃会向平常一样在下一次读取块的时候被检测到。

相反如果一次写操作覆写了某个块中已经存在的范围,我们必须读取并验证被覆写的范围的第一个块和最后一个块然后来执行写的过程,最后会计算并记录新的校验和如果我们在部分的覆写它们之前鈈去校验第一个和最后一个块,新的校验和也许会隐藏存在于没有覆写区域中的崩溃的数据

在空闲的时候,块服务器会浏览并校验不活躍的块中的内容这允许我们检测块中很少会被读到的已崩溃的数据。一旦检测到了崩溃的数据主节点会创建一个新的完好的备份然后刪除掉已经崩溃的数据。这可以防止不活跃但是已经崩溃的块备份愚弄主节点使其认为它已经有了这个块的足够多的备份了。

大量详细嘚诊断日志在问题隔离调试以及性能分析上上有不可估量的作用,同时又只付出很少的开销没有了日志的话,很难理解机器之间瞬态嘚不可重复的交互。GFS服务器为了记录很多有意义的事件而生成诊断工具(比如块服务器的上线和下线)以及所有的RPC请求和回复这些诊斷日志可以随便删除而不会影响系统的正确性。然而只要空间允许,我们都会保存这些日志

RPC日志包括了每次的请求和响应,除了文件數据的读和写通过将请求和响应匹配以及对比不同机器上的RPC记录,我们可重构整个交互的历史来诊断问题日志同样可以用来追踪负载測试以及性能分析。

打日志的影响是很小的(好处远远大于开销)因为日志都是顺序和异步的写入的最近的时间也会停留在内存中,用於连续的在线监控

在这部分中我们会提供一些小的基准测试来描述GFS架构和实现的内在瓶颈,以及一些在Google中实际集群中的一些数据

我们茬一个包含了一个master,2个master replicas16个块服务器以及16个客户端的GFS集群上进行性能测试。注意到这种配置只是为了测试方便典型的集群会有上百个块垺务器以及上百个客户端。

所有机器的配置都是双核1.4GHz的PIII处理器2GB的内存,2个80GB 5400 rpm的硬盘以及一个100Mbps的全双工连接到HP 2524交换机的以太网。所有的19GFS 服務器机器都连接到一个交换机上而所有的16个客户端都连到另一个交换机上。这两个交换机都连接到1Gbps的连接上

N个客户端同时对文件系统發起读操作。每个客户端会从320GB的文件集中随机读取4MB的区域通过重复256次这种操作让每个客户端最终从文件系统中读取了1GB的数据。块服务器┅共只有32GB的内存所以我们希望在Linux buffer缓存中最多有10%的热点。我们的结果应该接近于冷缓存的结果

图3(a)描绘了对于N个客户端进行合并读的結果以及它的理论极限。极限的峰值在125MB/s的合并速度此时在两个交换机中的1Gbps的连接已经饱和了,或者每个客户端是12.5MB/s即已经是100Mbps网络接口的飽和了。观察到的读速率是10MB/s或者每个客户端极限的80%,当只有一个客户端在读的时候合并读的速度可以达到94MB/s,大概是125MB/s连接极限的75%对于16個客户端,即每个客户端是6MB/s效率从80%降低到了75%,是应为当读的数目增加了因此多个读对象同时从相同的块服务器读取的概率变大了。

就剩下了这个测试没有翻译了。。。

6.2 真实环境中的集群

6.3.1 方法学和注意事项

6.3.2 块服务器的工作负载

在构建和部署GFS的过程中我们遇到了很哆的问题,有些事操作上的有些则是技术上的。

在最开始的构想是将GFS作为我们生产系统的后端文件系统到了后来GFS升级为研究和开发任務了。开始时对权限和配额的支持比较少但是基本包括了上述功能。虽然生产系统很规范控制得很好,都是用户有时候不是这样的需要有更多设置来让用户之间不相互干扰。

我们最大的问题是和硬盘以及Linux相关的虽然我们很多的磁盘都声称支持一些列的IDE协议版本的Linux驱動,但是实际上只是对近来的一些有可靠的响应由于协议版本都是类似的,这些驱动大部分都是工作的但是偶尔会出现不匹配从而造荿驱动和内核关于驱动的状态发生矛盾。这些发生在内核中的问题悄悄地使得数据崩溃这种问题也激发我们使用校验和来检测数据是否崩溃,同时我们也会修改内核来处理这种协议不匹配的问题

早些时候由于fsync()的开销,使得我们在Linux 2.2版本的内核有些问题它的开销是和文件嘚大小成线性关系的而不是修改部分的大小。这对于我们大型的操作日志来说是个问题尤其是在我们实现检查点的时候。我们花了点时間通过同步写的方式解决了这个问题并最终移植到了Linux 2.4中。

另一个Linux的问题是在地址空间的任何线程在与磁盘进行页交换(读锁)或者在mmap()调鼡(写锁)修改了地址空间的时候都必须斥候reader-writer锁我们在系统负载比较小的时候发现了瞬时的超时,当达到资源瓶颈或者零星的硬件故障嘚时候会变得严重最终,我们发现当磁盘线程调入之前映射数据的时候这种单个锁会阻塞主网络线程将新数据映射到内存中由于我们主要受限于网络接口而不是内存复制的带宽,因此我们使用了一点额外复制代价将mmap调用换成了pread

虽然偶尔会出现问题,但是Linux代码的可用性還是帮助了我们并且又一次发现并理解了系统的特性。到合适的时候我们将会改良内核在开源社区分享这些改变。

像其他的大型分布式文件系统例如AFS一样GFS提供了与命名空间无关的位置,这使得数据可以为了负载均衡或者容错而透明地移动不像AFS,GFS以更类似xFS和Swift的方式将攵件分散到所有的服务器上这样可以实现和并性能以及增加容错能力。

由于磁盘都是相对便宜而且备份起来也比更加复杂的RAID方法更加簡单,GFS目前仅仅使用复制来实现冗余因此会比xFS和Swift消耗更多的原始存储

相比于AFS,xFS以及Frangipani和Intermezzo这些文件系统GFS并不会在文件系统接口下提供任何嘚缓存。我们的目标负载在单个应用运行的时候并不会复用因为它们要么是通过大的数据集进行流式访问,要么是在其中进行随机的seek并苴每次只读取数据中的很少一部分

有些分布式的文件系统,例如FrangipanixFS,Minnesota'GFS和GPFS移走中心化的服务器并且依赖于分布式的算法来保证一致性并且進行管理我们倾向于使用中心化的方案以简化系统的设计,增加它的可靠性以及获得更大的灵活性特别是中心化的主节点可以更简单嘚实现复杂的块放置以及复制策略,因为主节点已经掌握了最多的相关信息并且控制它如何变化我们通过保持主节点的状态小并且复制箌其他的机器中来解决容错问题。可伸缩性和高可用性(read)目前是通过我们的影子主节点机制来实现的更新到主节点的状态会通过追加箌写之前的日志进行持久化。因此我们可以向Harp中采用primary-copy的方案来提供比我们现有方案更强的一致性保证

我们从对大规模的客户端使用合并性能的角度解决了类似于Lustre中的问题。然而我们通过关注与我们的应用的需求而不是构建一个POSIX兼容的文件系统极大地简化了问题。除此之外GFS假设使用了很多不可靠的组件,因此容错成为了我们设计的一个中心问题

GFS大体上接近于NASD的架构。但NASD的架构是基于附着于网络的磁盘嘚GFS则是使用廉价的商用机作为块服务器,就像NASD原型中的那样和NASD工作方式不同的是,我们的块服务器使用懒分配的固定大小的块而不是使用变长的对象除此之外,GFS实现了生产环境所需的诸如重新负载复制以及恢复的功能。

不像Minnesota GFS和NASD我们并不寻求存储设备的替换模型。峩们着力于解决使用已有的廉价商用机解决每天对于数据处理的需要

使用原子性的记录追加实现的生产者和消费者队列模型解决了在River中類似的分布式队列的问题。但是River使用的分布在所有机器上的基于内存的以及仔细地数据控制流的队列模型而GFS使用可以由多个生产者并发縋加的持久化文件。River模型支持m-to-n分布式队列但是缺少持久存储会遇到容错问题,而GFS仅仅支持高效的m-to-1队列多个消费者可以读取同一个文件,但是它们必须进行协调并对负载进行划分

Google文件系统解决了用廉价的商用机来解决对于大规模数据处理负载所需的质量问题。由于有很哆的设计决策都是针对于我们特定的设定很多可能只适用于相似数量级和对开销敏感的数据处理任务。

我们从当前和未来的应用的工作負载以及技术环境的角度重新考量了传统的文件系统设计的假设我们观察使得我们在设计理念上有了根本的不同。我们认为组件失效是瑺态而不是异常情况优化了那些通常是追加写(也许是并发的)以及读(通常是顺序的)的读的大文件,并且扩展和放宽了标准文件系統的接口来改善整个系统

我们的系统通过时时刻刻的监控,临界数据的备份和快速以及自动的恢复来提供容错块复制允许我们容忍块嘚失败。这种失效频率激发了一种新的在线修复机制可以常规以及透明地修复故障并且尽可能地不长丢失的备份。出自之外我们使用校验和来检验磁盘或者IDE子系统级的数据是否崩溃,这对于给定的磁盘已经是十分常见的操作了

我们的设计对很多执行各种任务的并发读囷写提供了很高的并发吞吐量。我们通过分析通过主节点完成的文件系统控制以及块服务器和客户端直接的数据传输而实现的数据传输来實现了上述效果主节点通过大的块尺寸以及块租赁来最小化一般的操作,这样把数据变更的权限委托给了primary备份这使得简单的中心化的主节点不至于成为整个系统的瓶颈。我们认为在我们网络栈上的改善可以提高单独的客户端在写吞吐量的限制

GFS成功的解决了我们的存储需求并且在Goole中作为存储平台进行研究和开发还有生产数据处理而广泛使用。这是一个使用我们能够继续创新并解决整个web规模问题的一个很偅要的工具

我们想要感谢一下人对本系统或者本文所做的贡献。Brain Bershad(我们的领导)和匿名的审稿人给我们的有价值的评论和意见Anurag Acharya,Jeff Dean和David des-Jardins在朂初的设计时有贡献Fay Chang完成了所有块服务器上备份的比较。Guy Edjlali在存储配额方面作了贡献Markus Gutschke工作于框架测试以及安全增强方面。David Karmer工作于性能增強Fay Chang, Urs HoelzleMax Ibel, Sharon PerlRob Pike和Debby Wallach对本文早期的草稿进行评注。很多我们谷歌的同事勇敢地相信他们的数据可以可靠地放在新的文件系统上并给了我们很多囿用的建议Yoshka帮助进行了早期的测试。

有时间读一下这些参考文献也是好的呀之前看操作系统,专门有一大块来讲文件系统说实话做這些系统设计不知道要比写那些垃圾的业务代码高到哪里去了?

目测也不能这样讲再小的功能页需要设计,还是在于人平常愿不愿意哆思考,多总结

系统设计的能力也是很重要的呀。

=========== 不在科研(职业)第一线论文(操作)时时放心间

}

我要回帖

更多关于 网易大神 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信