为您提供在线论文写作帮助

中国免费论文网-免费论文|毕业论文|职称论文|论文资料|专业论文

其次,用传统视角来看,我们要处理的文件很多都是巨型的,好几GB的文件也很常见。通常情况下每个文件中包含了多个应用对象,比如web文档。面对快速增长、TB级别、包含数十亿对象的数据集合,如果按数十亿个KB级别的小文件来管理,即使文件系统能支持,也是非常不明智的。因此,一些设计上的假设和参数,比如I/O操作和块大小,需要被重新审视。

毕业论文范文 - 应届毕业生网

为了最大化利用每台机器的网络带宽,我们让数据沿着一个线性链路推送(chunkserver就是链路中的一个个节点),而不是零乱的分布于其他拓扑结构中(比如树状)。我们希望每台机器都会使用全量带宽尽快传输一整批数据,而不是频繁收发零乱的小批数据。

经济管理文库-论文资料网

checksum的计算是为append操作高度优化的,因为append是我们的主要应用场景。append时可能会修改最后的块、也可能新增块。对于修改的块只需增量更新其checksum,对于新增块不管它有没有被填满都可以计算其当前的checksum。对于最后修改的块,即使它已经腐化了而且append时没有检测到,还对其checksum执行了增量更新,此块的checksum匹配依然会失败,在下次被读取时即能侦测到。

工程造价-土木工程网

GFS集群是高度分布式的,而且有多个层级(层级是指:机房/机架/服务器这样的层级结构)。通常会在多个机架上部署几百个chunkserver。这些chunkserver可能被各机架的几百个客户端访问。不同机架之间的机器通讯可能跨一个或多个网络交换机。进出一个机架的带宽可能会低于机架内所有机器的总带宽。多级分布式要求我们更加合理的分布数据,以提高可扩展性、可靠性和可用性。

不过GFS依然支持直接的修改功能——随机偏移写。这并不是因为GFS的架构设计更强大,而是因为它不需要承担某些责任。比如在Haystack和TFS中,它们需要承担图片在真实文件中的存储格式、检索等责任,当将一个图片从655KB修改为656KB时,对存储格式的破坏是难以承受的,所以它们不支持这种直接的修改,只能采用删除+新增来模拟修改。而GFS并没有维护一个文件内部格式的责任,还是那句话,你交给它什么它就存什么。所以用户会保护自己的文件内部格式,他说可以写那就可以写,GFS并没有什么难题,只需解决好一致性、defined、统一变异顺序等问题即可。

读者可以想象这样一个场景,某人和他老婆共用同一个Facebook账号,同时登陆,同时看到某张照片,他希望将其顺时针旋转95度,他老婆希望将其逆时针旋转95度。两人同时点了修改按钮,Facebook应该听谁的?俗话说意见相同听老公的,意见不同听老婆的。但是Facebook不懂这个算法,当他们重新打开页面时可能会:6 都看到图片顺时针旋转了95度;7 都看到图片逆时针旋转了95度;8 其他情况。对于6、7两种情况,都是可以接受的,小夫妻若来投诉那只能如实相告让他们自己回去猜拳,不关Facebook的事儿。因为6、7既满足一致性(两人在并发修改发生后都一直看到一致相同的内容),又满足defined(内容是其中一人写入的完整数据)。对于8会有哪些其他情况呢?如果这事儿发生在单台电脑的本地硬盘(相当于两人同时打开一个图片软件、编辑同一个图片、然后并发提交保存),若不加锁让其串行,则可能导致数据碎片,以简单的代码为例:

尽管分布式垃圾回收是一个困难的问题,它需要复杂的解决方案,但是我们的做法却很简单。master的“文件到chunk映射”中记录了对各chunk引用信息。我们也能轻易的识别所有chunk副本:他们是在某台chunkserver上、某个指定的目录下的一个Linux文件。任何master没有登记在册的副本都可以认为是垃圾。

通过命名空间锁可以允许在相同目录发生并发的变化。比如多个文件在同一个目录被并发创建:每个创建会申请此目录的读锁和各自文件的写锁,不会导致冲突。目录的读锁可以保护在创建时此目录不会被删除、重命名或者执行快照。对相同文件的创建请求,由于写锁的保护,也只会导致此文件被串行的创建两次。

record append也是一种变异,遵从控制流(章节),但是会需要首要副本执行一点点额外的逻辑。客户端将数据推送到文件末尾对应的chunk的所有副本上。然后发送写请求到首要副本。首要副本需要检查append到此chunk是否会导致chunk超过最大的size(69MB)。如果超过,它将此chunk填补到最大size,并告诉次级副本也这么做,随后回复客户端这个操作需要重试,并使用下一个chunk(上一个chunk刚刚已经被填满,文件末尾会对应到一个新chunk)。record append的数据大小被限制为小于等于chunk maxsize的四分之一,这样可以避免填补导致的过多碎片。如果不需要填补(通常都不需要),首要副本append数据到它的副本,得出其偏移量,并告诉次级副本将数据准确的写入此偏移,最终回复客户端操作已成功。

操作日志是对重要元数据变更的历史记录。它是GFS的核心之一。不仅因为它是元数据唯一的持久化记录,而且它还要承担一个逻辑上的时间标准,为并发的操作定义顺序。各文件、chunk、以及它们的版本(见章节),都会根据它们创建时的逻辑时间被唯一的、永恒的标识。

第三,大部分文件发生变化是通过append新数据,而不是覆盖、重写已有的数据,随机写几乎不存在。被写入时,文件变成只读,而且通常只能是顺序读。很多数据场景都符合这些特征。比如文件组成大型的库,使用数据分析程序对其扫描。比如由运行中的程序持续生成的数据流。比如归档数据。还可能是分布式计算的中间结果,在一台机器上产生、然后在另一台处理。这些数据场景都是由制造者持续增量的产生新数据,再由消费者读取处理。在这种模式下append是性能优化和保证原子性的焦点。然而在客户端缓存数据块没有太大意义。

然而,热点确实曾经导致过问题,当GFS最初被用在批量队列系统时:用户将一个可执行程序写入GFS,它只占一个chunk,然后几百台机器同时启动,请求此可执行程序。存储此可执行文件的chunkserver在过多的并发请求下负载较重。我们通过提高它的复制级别解决了这个问题(更多冗余,分担压力),并且建议该系统交错安排启动时间。一个潜在的长期解决方案是允许客户端从其他客户端读取数据(P7P模式~)。

每个应用程序会引用GFS的客户端API,此API与正规文件系统API相似,并且负责与master和chunkserver通讯,基于应用的行为来读写数据。客户端只在获取元数据时与master交互,真实的数据操作会直接发至chunkserver。我们不需提供严格完整的POSIX API,因此不需要hook到Linux的vnode层面。

既然操作日志这么重要,我们必须可靠的存储它,而且直至元数据更新被持久化完成(记录操作日志)之后,才能让变化对客户端可见。否则,我们有可能失去整个文件系统或者最近的客户端操作,即使chunkserver没有任何问题(元数据丢了或错了,chunkserver没问题也变得有问题了)。因此,我们将它复制到多个远程机器,直到日志记录被flush到本地磁盘以及远程机器之后才会回复客户端。master会捆绑多个日志记录,一起flush,以减少flush和复制对整个系统吞吐量的冲击。

在撤回租赁完成后,master将此快照操作日志记录到磁盘。实施快照操作时,它会在内存状态中快速复制一份源文件、源目录树的元数据,复制出来的元数据映射到相同的chunk(和JVM中对象的引用计数相似,此chunk的引用计数为7,源元数据和快照元数据两份引用)。

GFS、Haystack、TFS的控制流大致相同,其思路不外乎:6 客户端需要发起一次新请求;7 客户端询问协调组件,自己应该访问哪个存储组件;8 协调组件分析元数据,给出答复;9 客户端拿到答复直接访问指定的存储组件,提交请求;5 各存储组件执行请求,返回结果。

我们将数据流和控制流解耦来更高效的利用网络。从上述控制流的分析中可以看出,从客户端到首要副本然后到所有次级副本,请求是沿着一个小心谨慎的链路、像管道一样,在各个chunkserver之间推送。我们不能容忍真实数据的流程被此严谨的控制流绑架,我们的目标是最大化利用每个机器的网络带宽,避免网络瓶颈和高延迟连接,最小化推送延迟。

同样,GFS的删除与Haystack、TFS的删除,其意义也不同。GFS的删除指的是整个大文件的删除。Haystack和TFS删除的是用户视角下的一条数据,比如一张图片,它是逻辑存储单元中的一个entry而已。而GFS的删除则是用户视角下的一整个文件,一个文件就对应了多个逻辑存储单元(chunk),里面也包含了海量的应用实体(比如图片)。在这种差异的背景下,他们面临的难题也完全不同。Haystack和TFS面临的就是刚才提到的“删除会破坏存储文件已有数据的格式、造成狭缝碎片”问题,它们的对策就是懒惰软删除、闲了再整理。而GFS则不会遭遇此难题,用户在文件内部删除几条数据对于GFS来说和随机偏移写没啥区别,狭缝碎片也是用户自己解决。GFS需要解决的是整个文件被删除,遗留下了大量的chunk,如何回收的问题。相比Haystack和TFS,GFS的垃圾回收其实更加轻松,因为它要回收的是一个个逻辑存储单元(chunk),一个chunk(副本)其实就是一个真实的Linux文件,调用()删了就等于回收了,而不需要担心文件内部那些精细的组织格式、空间碎片。

比如用户在使用Haystack存取图片时,可想而知,编程界面中肯定会有类似create(photo)、read(photo_id)这样的接口供用户使用。而GFS给用户提供的接口则更类似File、FileInputStream、FileOutputStream(以Java语言举例)这样的标准文件系统接口。对比可见,Haystack的接口更加高层、更加抽象,更加贴近于应用,但可能只适合某些定制化的应用场景;GFS的接口则更加底层、更加通用,标准文件系统能支持的它都能支持。举个例子,有一万张图片,每张655KB左右,用Haystack、GFS存储都可以,用Haystack更方便,直接有create(photo)这样的接口可以用,调用即可;用GFS就比较麻烦,你需要自己考虑是存成一万张小文件还是组装为一些大文件、按什么格式组装、要不要压缩……GFS不去管这些,你给它什么它就存什么。假如把一万张图片换成一部6GB的高清视频AVI文件,总大小差不多,一样可以放心使用GFS来存储,但是Haystack可能就望而却步了(难道把一部电影拆散放入它的一个个needle?)。

我们最大挑战之一是频繁的组件故障。GFS集群中组件的质量(机器质量较低)和数量(机器数量很多)使得这些问题更加普遍:我们不能完全的信赖机器,也不能完全信赖磁盘。组件故障能导致系统不可用甚至是腐化的数据。下面讨论我们如何应对这些挑战,以及我们构建的帮助诊断问题的工具。