论文部分内容阅读
摘要:本文主要阐述关于分布式文件系统GFS,它是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。通过详细介绍其一致性模块以及读写流程,针对GFS的大块的逻辑和设计理念及相关要点都进行了详细的分析。
关键词:云储存系统;GFS系统架构;设计策略;
一、GFS设计思路
1.组件/机器失效
GFS包括几百甚至几千台普通的廉价设备组装的存储机器,同时被相当数量的客户机访问。GFS组件的数量和质量导致在事实上,任何给定时间内都有可能发生某些组件无法工作,某些组件无法从它们目前的失效状态中恢复。例如谷歌遇到过各种各样的问题,比如应用程序bug、操作系统的bug、人为失误,甚至还有硬盘、内存、连接器、网络以及电源失效等造成的问题。所以,持续的监控、错误侦测、灾难冗余以及自动恢复的机制必须集成在GFS中。
2.谷歌处理的文件都非常巨大。(大数据):
这点跟NEFS的场景既有相似性又不完全一致,NEFS上层对接的是NOS对象存储,基本都是大量的小文件(100MB以下),总体量比较大,对象个数比較多,因此也需要考虑元数据管理的成本,因此NEFS采用了小文件合并的设计思路(不详细展开)。
谷歌系统中数GB的文件非常普遍。每个文件通常都包含许多应用程序对象,比如web文档。当我们经常需要处理快速增长的、并且由数亿个对象构成的、数以TB的数据集时,采用管理数亿个KB大小的小文件的方式是非常不明智的,尽管有些文件系统支持这样的管理方式。因此,设计的假设条件和参数,比如I/O操作和Block的尺寸都需要重新考虑。
3.绝大部分文件的修改是采用在文件尾部追加数据,而不是覆盖原有数据的方式。(读写模型:顺序写,大部分顺序读,小部分随机读):
对文件的随机写入操作在实际中几乎不存在。一旦写完之后,对文件的操作就只有读,而且通常是按顺序读。大量的数据符合这些特性,比如:数据分析程序扫描的超大的数据集;正在运行的应用程序生成的连续的数据流;存档的数据;由一台机器生成、另外一台机器处理的中间数据,这些中间数据的处理可能是同时进行的、也可能是后续才处理的。对于这种针对海量文件的访问模式,客户端对数据块缓存是没有意义的,数据的追加操作是性能优化和原子性保证的主要考量因素。
4.应用程序和文件系统API协同设计,简化对GFS的要求(灵活性):
例如一致性模型要求放松了,这样就减轻了文件系统对应用程序的苛刻要求,大大简化了GFS的设计。并且引入了原子性的记录追加操作,从而保证多个客户端能够同时进行追加操作,不需要额外的同步操作来保证数据的一致性。
二、GFS接口:
GFS提供了一套类似传统文件系统的API接口函数,文件以分层目录的形式组织,用路径名来标识。GFS支持常用的操作,如创建新文件、删除文件、打开文件、关闭文件、读和写文件。但是要理解一点:文件块被存储在linux硬盘上,GFS只是一个管理器而已,这些文件操作API也只是个对这些抽象文件的管理而已。也就是说GFS层级比底层文件系统以及虚拟文件系统层次要高。注:这点跟NEFS也比较像,NEFS也是对文件粒度进行管理的,而不是针对块设备,因此也是在底层文件系统及虚拟文件系统之上。
四、GFS设计架构:
使用论文中的原图如下:如图所示,GFS主要由以下三个系统模块组成:Master:管理元数据、整体协调系统活动。·ChunkServer:存储维护数据块(Chunk),读写文件数据。·Client:向Master请求元数据,并根据元数据访问对应ChunkServer的Chunk。
三、GFS设计要点:
(1)chunk机制
chunk是GFS中管理数据的最小单元(数据块),每一个chunk被一个64位的handle唯一标识,chunk被当做普通的文件存储在linux系统中。每个chunk至少会在另一个chunkserver上有备份,而默认会为每个chunk做三个备份。chunk大小默认为64MB,比一般的文件系统的4kb的块要大的多得多。Chunkserver一般不会缓存数据,因为chunk都是存储在本地,故而linux已经将经常被访问的数据缓存在内存中了。
chunk块设置比较大(一般文件系统的块为4kb)的优缺点如下:
优点:
1.减少元数据量,方便客户端预读缓存(filename+chunkindex->chunkhandle+chunkserverlocation),减少客户端访问的次数,减少master负载。
2.减少元数据量,master可以将元数据放在内存中。
3.客户端取一次元数据就能读到更多数据,减少客户端访问不同chunkserver建立tcp连接的次数,从而减少网络负载。
缺点:
1.对于小文件的场景,容易产生数据碎片。
2.小文件占用chunk少,对小文件频繁访问会集中在少数chunkserver上,从而产生小文件访问热点(这个问题在后续的可靠性篇章中有相关的解决方案)。
(2)元数据管理
系统中三种元数据类型:·文件和chunk的名称(命名空间)。文件和chunk之间的映射关系,比如说每个文件是由哪些chunk共同组成。每个chunk备份的位置。命名空间、文件和Chunk的对应关系的存储方式:内存:真实数据;磁盘:定期Checkpoint(压缩B树)和上次CheckPoint后的操作日志;多机备份:Checkpoint文件和操作日志。Chunk位置信息的存储方式:内存:真实数据磁盘:不持久存储系统启动和新Chunk服务器加入时从Chunk服务器获取。避免了Master与ChunkServer之间的数据同步,只有Chunk服务器才能最终确定Chunk是否在它的硬盘上。 (3)Master单一性
单一的Master节点的策略大大简化了FGS的设计。单一的Master节点可以通过全局的信息精确定位Chunk的位置以及进行复制决策。针对master宕机的处理和恢复在后续的高可用篇中详细介绍。
(4)操作日志
操作日志包含了关键的元数据变更历史记录。这对GFS非常重要。这不仅仅是因为操作日志是元数据唯一的持久化存储记录,它也作为判断同步操作顺序的逻辑时间基线。文件和Chunk,连同它们的版本(可以看做是更新次数,以判断chunk是否过期,低版本的认为过期),都由它们创建的逻辑时间唯一的、永久的标识。操作日志和chunk数据一样,都需要复制到多台机器,使用多副本保存。
租约(lease)机制主要是为了保持多个副本间变更顺序一致性的。Master节点为Chunk的一个副本建立一个租约,我们把这个副本叫做主Chunk。主Chunk对Chunk的所有更改操作进行序列化。所有的副本都遵从这个序列进行修改操作。因此,修改操作全局的顺序首先由Master节点选择的租约的顺序决定,然后由租约中主Chunk分配的序列号决定。设计租约机制的目的是为了最小化Master节点的管理负担。租约的初始超时设置为60秒。不过,只Chunk被修改了,主Chunk就可以申请更长的租期,通常会得到Master节点的确认并收到租约延长的时间。这些租约延长请求和批准的信息通常都是附加在Master节点和Chunk服务器之间的心跳消息中来传递。有时Master节点会试图提前取消租约(例如,Master节点想取消在一个已经被改名的文件上的修改操作)。即使Master節点和主Chunk失去联系,它仍然可以安全地在旧的租约到期后和另外一个Chunk副本签订新的租约。写入流程如下,例如有1个主副本,2个从副本的情况:
客户机向Master节点询问哪一个Chunk服务器持有当前的租约,以及其它副本的位置。如果没有一个Chunk持有租约,Master节点就选择其中一个副本建立一个租约(这个步骤在图上没有显示)。Master节点将主Chunk的标识符以及其它副本(又称为secondary副本、二级副本)的位置返回给客户机。客户机缓存这些数据以便后续的操作。只有在主Chunk不可用,或者主Chunk回复信息表明它已不再持有租约的时候,客户机才需要重新跟Master节点联系。
客户机把数据推送到所有的副本上。客户机可以以任意的顺序推送数据。Chunk服务器接收到数据并保存在它的内部LRU缓存中,一直到数据被使用或者过期交换出去。由于数据流的网络传输负载非常高,通过分离数据流和控制流,我们可以基于网络拓扑情况对数据流进行规划,提高系统性能,而不用去理会哪个Chunk服务器保存了主Chunk。
(1)当所有的副本都确认接收到了数据,客户机发送写请求到主Chunk服务器。这个请求标识了早前推送到所有副本的数据。主Chunk为接收到的所有操作分配连续的序列号,这些操作可能来自不同的客户机,序列号保证了操作顺序执行。它以序列号的顺序把操作应用到它自己的本地状态中(也就是在本地执行这些操作,这句话按字面翻译有点费解,也许应该翻译为“它顺序执行这些操作,并更新自己的状态”)。
(2)主Chunk把写请求传递到所有的二级副本。每个二级副本依照主Chunk分配的序列号以相同的顺序执行这些操作。
(3)所有的二级副本回复主Chunk,它们已经完成了操作。
(4)主Chunk服务器(即主Chunk所在的Chunk服务器)回复客户机。
任何副本产生的任何错误都会返回给客户机。在出现错误的情况下,写入操作可能在主Chunk和一些二级副本执行成功。(如果操作在主Chunk上失败了,操作就不会被分配序列号,也不会被传递。)客户端的请求被确认为失败,被修改的region处于不一致的状态。我们的客户机代码通过重复执行失败的操作来处理这样的错误。
在从头开始重复执行之前,客户机会先从步骤(3)到步骤(7)做几次尝试。如果应用程序一次写入的数据量很大,或者数据跨越了多个Chunk,GFS客户端代码会把它们分成多个写操作。这些操作都遵循前面描述的控制流程,但是可能会被其它客户机上同时进行的操作打断或者覆盖。因此,共享的文件region的尾部可能包含来自不同客户机的数据片段,尽管如此,由于这些分解后的写入操作在所有的副本上都以相同的顺序执行完成,Chunk的所有副本都是一致的。这使文件region处于上一节描述的一致的、但是未定义的状态。读取流程可以再看一下设计架构图中的conrol和data流。
(1)GFSclient将服务所要读取的文件名与byteoffset,根据系统chunk大小,换算成文件的chunkindex,即文件数据所处的第几个chunk。
(2)将filename与chunkindex传给master。
(3)Master返回给client元数据信息(包含chunkhandle与实际存储的chunkserverlocation)。然后client获取到该信息,作为key值与filename+chunkindex缓存起来。
(4)Client根据这些元数据信息,直接对chunkserver发出读请求。对于三副本而言(一份chunk存储在三台不同的chunkserver),client选择离自己最近的chunkserver(网络?),通过之前获取的元数据信息找到需要读的chunk位置以及下一个chunk位置。如果缓存的元数据信息已过期,则需要重新向master去获取一遍。
(5)Chunkserver返回给client要读的数据信息。
结语
GFS的设计思路决定了它的主体框架及设计要点。针对于GFS大部分都是顺序读取的场景来说,不需要做进一步的优化。针对数据追加的优化才是针对顺序写的目标场景进行的好的优化设计点。NEFS的目标场景跟GFS有一定的差别,NEFS上层对接的是NOS对象存储,基本都是大量的小文件(100MB以下),总体量比较大,对象个数比较多。而且大部分是顺序写,随机读场景,因此目标场景不一致导致结构和设计会有一定的差异。
参考文献
[1] Thomas Anderson, Michael Dahlin, Jeanna Neefe, David Patterson, Drew Roselli, and Randolph Wang. Serverless networkfil e systems. In Proceedings of the 15th ACM Symposium on Operating System Principles, pages 109–126, Copper Mountain Resort, Colorado, December 1995.
[2] Remzi H. Arpaci-Dusseau, Eric Anderson, Noah Treuhaft, David E. Culler, Joseph M. Hellerstein, David Patterson, and Kathy Yelick. Cluster I/O with River: Making the fast case common. In Proceedings of the Sixth Workshop on Input/Output in Parallel and Distributed Systems (IOPADS ’99), pages 10–22, Atlanta, Georgia, May 1999.
[3] Luis-Felipe Cabrera and Darrell D. E. Long. Swift: Using distributed disks triping to provide high I/O data rates. Computer Systems, 4(4):405–436, 1991.
作者简介
孙伟(1981-),男,汉族,内蒙古察呼伦贝尔人,集宁师范学院计算机学院讲师,工程硕士,研究方向:软件工程。
关键词:云储存系统;GFS系统架构;设计策略;
一、GFS设计思路
1.组件/机器失效
GFS包括几百甚至几千台普通的廉价设备组装的存储机器,同时被相当数量的客户机访问。GFS组件的数量和质量导致在事实上,任何给定时间内都有可能发生某些组件无法工作,某些组件无法从它们目前的失效状态中恢复。例如谷歌遇到过各种各样的问题,比如应用程序bug、操作系统的bug、人为失误,甚至还有硬盘、内存、连接器、网络以及电源失效等造成的问题。所以,持续的监控、错误侦测、灾难冗余以及自动恢复的机制必须集成在GFS中。
2.谷歌处理的文件都非常巨大。(大数据):
这点跟NEFS的场景既有相似性又不完全一致,NEFS上层对接的是NOS对象存储,基本都是大量的小文件(100MB以下),总体量比较大,对象个数比較多,因此也需要考虑元数据管理的成本,因此NEFS采用了小文件合并的设计思路(不详细展开)。
谷歌系统中数GB的文件非常普遍。每个文件通常都包含许多应用程序对象,比如web文档。当我们经常需要处理快速增长的、并且由数亿个对象构成的、数以TB的数据集时,采用管理数亿个KB大小的小文件的方式是非常不明智的,尽管有些文件系统支持这样的管理方式。因此,设计的假设条件和参数,比如I/O操作和Block的尺寸都需要重新考虑。
3.绝大部分文件的修改是采用在文件尾部追加数据,而不是覆盖原有数据的方式。(读写模型:顺序写,大部分顺序读,小部分随机读):
对文件的随机写入操作在实际中几乎不存在。一旦写完之后,对文件的操作就只有读,而且通常是按顺序读。大量的数据符合这些特性,比如:数据分析程序扫描的超大的数据集;正在运行的应用程序生成的连续的数据流;存档的数据;由一台机器生成、另外一台机器处理的中间数据,这些中间数据的处理可能是同时进行的、也可能是后续才处理的。对于这种针对海量文件的访问模式,客户端对数据块缓存是没有意义的,数据的追加操作是性能优化和原子性保证的主要考量因素。
4.应用程序和文件系统API协同设计,简化对GFS的要求(灵活性):
例如一致性模型要求放松了,这样就减轻了文件系统对应用程序的苛刻要求,大大简化了GFS的设计。并且引入了原子性的记录追加操作,从而保证多个客户端能够同时进行追加操作,不需要额外的同步操作来保证数据的一致性。
二、GFS接口:
GFS提供了一套类似传统文件系统的API接口函数,文件以分层目录的形式组织,用路径名来标识。GFS支持常用的操作,如创建新文件、删除文件、打开文件、关闭文件、读和写文件。但是要理解一点:文件块被存储在linux硬盘上,GFS只是一个管理器而已,这些文件操作API也只是个对这些抽象文件的管理而已。也就是说GFS层级比底层文件系统以及虚拟文件系统层次要高。注:这点跟NEFS也比较像,NEFS也是对文件粒度进行管理的,而不是针对块设备,因此也是在底层文件系统及虚拟文件系统之上。
四、GFS设计架构:
使用论文中的原图如下:如图所示,GFS主要由以下三个系统模块组成:Master:管理元数据、整体协调系统活动。·ChunkServer:存储维护数据块(Chunk),读写文件数据。·Client:向Master请求元数据,并根据元数据访问对应ChunkServer的Chunk。
三、GFS设计要点:
(1)chunk机制
chunk是GFS中管理数据的最小单元(数据块),每一个chunk被一个64位的handle唯一标识,chunk被当做普通的文件存储在linux系统中。每个chunk至少会在另一个chunkserver上有备份,而默认会为每个chunk做三个备份。chunk大小默认为64MB,比一般的文件系统的4kb的块要大的多得多。Chunkserver一般不会缓存数据,因为chunk都是存储在本地,故而linux已经将经常被访问的数据缓存在内存中了。
chunk块设置比较大(一般文件系统的块为4kb)的优缺点如下:
优点:
1.减少元数据量,方便客户端预读缓存(filename+chunkindex->chunkhandle+chunkserverlocation),减少客户端访问的次数,减少master负载。
2.减少元数据量,master可以将元数据放在内存中。
3.客户端取一次元数据就能读到更多数据,减少客户端访问不同chunkserver建立tcp连接的次数,从而减少网络负载。
缺点:
1.对于小文件的场景,容易产生数据碎片。
2.小文件占用chunk少,对小文件频繁访问会集中在少数chunkserver上,从而产生小文件访问热点(这个问题在后续的可靠性篇章中有相关的解决方案)。
(2)元数据管理
系统中三种元数据类型:·文件和chunk的名称(命名空间)。文件和chunk之间的映射关系,比如说每个文件是由哪些chunk共同组成。每个chunk备份的位置。命名空间、文件和Chunk的对应关系的存储方式:内存:真实数据;磁盘:定期Checkpoint(压缩B树)和上次CheckPoint后的操作日志;多机备份:Checkpoint文件和操作日志。Chunk位置信息的存储方式:内存:真实数据磁盘:不持久存储系统启动和新Chunk服务器加入时从Chunk服务器获取。避免了Master与ChunkServer之间的数据同步,只有Chunk服务器才能最终确定Chunk是否在它的硬盘上。 (3)Master单一性
单一的Master节点的策略大大简化了FGS的设计。单一的Master节点可以通过全局的信息精确定位Chunk的位置以及进行复制决策。针对master宕机的处理和恢复在后续的高可用篇中详细介绍。
(4)操作日志
操作日志包含了关键的元数据变更历史记录。这对GFS非常重要。这不仅仅是因为操作日志是元数据唯一的持久化存储记录,它也作为判断同步操作顺序的逻辑时间基线。文件和Chunk,连同它们的版本(可以看做是更新次数,以判断chunk是否过期,低版本的认为过期),都由它们创建的逻辑时间唯一的、永久的标识。操作日志和chunk数据一样,都需要复制到多台机器,使用多副本保存。
租约(lease)机制主要是为了保持多个副本间变更顺序一致性的。Master节点为Chunk的一个副本建立一个租约,我们把这个副本叫做主Chunk。主Chunk对Chunk的所有更改操作进行序列化。所有的副本都遵从这个序列进行修改操作。因此,修改操作全局的顺序首先由Master节点选择的租约的顺序决定,然后由租约中主Chunk分配的序列号决定。设计租约机制的目的是为了最小化Master节点的管理负担。租约的初始超时设置为60秒。不过,只Chunk被修改了,主Chunk就可以申请更长的租期,通常会得到Master节点的确认并收到租约延长的时间。这些租约延长请求和批准的信息通常都是附加在Master节点和Chunk服务器之间的心跳消息中来传递。有时Master节点会试图提前取消租约(例如,Master节点想取消在一个已经被改名的文件上的修改操作)。即使Master節点和主Chunk失去联系,它仍然可以安全地在旧的租约到期后和另外一个Chunk副本签订新的租约。写入流程如下,例如有1个主副本,2个从副本的情况:
客户机向Master节点询问哪一个Chunk服务器持有当前的租约,以及其它副本的位置。如果没有一个Chunk持有租约,Master节点就选择其中一个副本建立一个租约(这个步骤在图上没有显示)。Master节点将主Chunk的标识符以及其它副本(又称为secondary副本、二级副本)的位置返回给客户机。客户机缓存这些数据以便后续的操作。只有在主Chunk不可用,或者主Chunk回复信息表明它已不再持有租约的时候,客户机才需要重新跟Master节点联系。
客户机把数据推送到所有的副本上。客户机可以以任意的顺序推送数据。Chunk服务器接收到数据并保存在它的内部LRU缓存中,一直到数据被使用或者过期交换出去。由于数据流的网络传输负载非常高,通过分离数据流和控制流,我们可以基于网络拓扑情况对数据流进行规划,提高系统性能,而不用去理会哪个Chunk服务器保存了主Chunk。
(1)当所有的副本都确认接收到了数据,客户机发送写请求到主Chunk服务器。这个请求标识了早前推送到所有副本的数据。主Chunk为接收到的所有操作分配连续的序列号,这些操作可能来自不同的客户机,序列号保证了操作顺序执行。它以序列号的顺序把操作应用到它自己的本地状态中(也就是在本地执行这些操作,这句话按字面翻译有点费解,也许应该翻译为“它顺序执行这些操作,并更新自己的状态”)。
(2)主Chunk把写请求传递到所有的二级副本。每个二级副本依照主Chunk分配的序列号以相同的顺序执行这些操作。
(3)所有的二级副本回复主Chunk,它们已经完成了操作。
(4)主Chunk服务器(即主Chunk所在的Chunk服务器)回复客户机。
任何副本产生的任何错误都会返回给客户机。在出现错误的情况下,写入操作可能在主Chunk和一些二级副本执行成功。(如果操作在主Chunk上失败了,操作就不会被分配序列号,也不会被传递。)客户端的请求被确认为失败,被修改的region处于不一致的状态。我们的客户机代码通过重复执行失败的操作来处理这样的错误。
在从头开始重复执行之前,客户机会先从步骤(3)到步骤(7)做几次尝试。如果应用程序一次写入的数据量很大,或者数据跨越了多个Chunk,GFS客户端代码会把它们分成多个写操作。这些操作都遵循前面描述的控制流程,但是可能会被其它客户机上同时进行的操作打断或者覆盖。因此,共享的文件region的尾部可能包含来自不同客户机的数据片段,尽管如此,由于这些分解后的写入操作在所有的副本上都以相同的顺序执行完成,Chunk的所有副本都是一致的。这使文件region处于上一节描述的一致的、但是未定义的状态。读取流程可以再看一下设计架构图中的conrol和data流。
(1)GFSclient将服务所要读取的文件名与byteoffset,根据系统chunk大小,换算成文件的chunkindex,即文件数据所处的第几个chunk。
(2)将filename与chunkindex传给master。
(3)Master返回给client元数据信息(包含chunkhandle与实际存储的chunkserverlocation)。然后client获取到该信息,作为key值与filename+chunkindex缓存起来。
(4)Client根据这些元数据信息,直接对chunkserver发出读请求。对于三副本而言(一份chunk存储在三台不同的chunkserver),client选择离自己最近的chunkserver(网络?),通过之前获取的元数据信息找到需要读的chunk位置以及下一个chunk位置。如果缓存的元数据信息已过期,则需要重新向master去获取一遍。
(5)Chunkserver返回给client要读的数据信息。
结语
GFS的设计思路决定了它的主体框架及设计要点。针对于GFS大部分都是顺序读取的场景来说,不需要做进一步的优化。针对数据追加的优化才是针对顺序写的目标场景进行的好的优化设计点。NEFS的目标场景跟GFS有一定的差别,NEFS上层对接的是NOS对象存储,基本都是大量的小文件(100MB以下),总体量比较大,对象个数比较多。而且大部分是顺序写,随机读场景,因此目标场景不一致导致结构和设计会有一定的差异。
参考文献
[1] Thomas Anderson, Michael Dahlin, Jeanna Neefe, David Patterson, Drew Roselli, and Randolph Wang. Serverless networkfil e systems. In Proceedings of the 15th ACM Symposium on Operating System Principles, pages 109–126, Copper Mountain Resort, Colorado, December 1995.
[2] Remzi H. Arpaci-Dusseau, Eric Anderson, Noah Treuhaft, David E. Culler, Joseph M. Hellerstein, David Patterson, and Kathy Yelick. Cluster I/O with River: Making the fast case common. In Proceedings of the Sixth Workshop on Input/Output in Parallel and Distributed Systems (IOPADS ’99), pages 10–22, Atlanta, Georgia, May 1999.
[3] Luis-Felipe Cabrera and Darrell D. E. Long. Swift: Using distributed disks triping to provide high I/O data rates. Computer Systems, 4(4):405–436, 1991.
作者简介
孙伟(1981-),男,汉族,内蒙古察呼伦贝尔人,集宁师范学院计算机学院讲师,工程硕士,研究方向:软件工程。