作者简介北京科技中心建筑组组长陈东明负责产品线架构设计和基础架构研发。他曾经是百度的架构师,负责百度即时通讯产品的架构设计。具有丰富的大规模系统建设和基础设施研发经验,擅长复杂业务需求下的大并发、分布式系统设计和持续优化。
GFS(Google文件系统)是Google开发的分布式文件系统。
2003年,Google发表了一篇详细描述GFS架构的论文。GFS、MapReduce、Bigt able也被称为谷歌三大,推动了谷歌的快速发展。其他互联网公司和开源领域纷纷模仿并构建自己的系统。可以说,GFS、MapReduce和Bigt能够引领互联网公司分布式技术的发展。但是GFS架构设计并不是一个完美的架构设计,它有很多问题,缺乏一致性就是其中之一。
本文讨论了GFS体系结构设计,分析了它在一致性方面的设计缺陷,并探讨了一致性对分布式系统的重要性。
先说GFS的界面设计。
港市
GFS使用熟悉的接口,但是没有实现POSIX等标准接口。常见操作包括:创建、删除、打开、关闭、读取、写入、记录追加等。创建、删除、打开、关闭和POSIX的接口都差不多,这里就不强调了。下面详细描述了write,record append提供的语义。
write操作可以将任意⻓度len的数据写入到任意指定文件的位置off setrecord append操作可以原子的将len<=16MB的数据写入到指定文件的末尾GFS之所以设计这个接口,是因为记录追加不是一个简单的偏移量等于文件结尾的写操作。记录追加是一个具有原子特性的操作,这将在本文后面详细解释。
写操作和记录附加操作都允许多个客户端同时操作。
结构
GFS架构如下。(节选自英国政府飞行服务队的论文)
主要架构组件有GFS客户端、GFS主服务器和GFS分块服务器。一个GFS集群包括一个主服务器和多个分块服务器,该集群可以被多个GFS客户端访问。
GFS客户端是运行在Application进程中的代码,通常以SDK的形式存在。
GFS中的文件被分成固定大小的块,每个块的大小固定在64MB。GFSchunkerver将这些区块存储在本地Linux文件系统中,即本地磁盘中。一般来说,每个chunck将保存3个副本。一个分块服务器将保存多个分块,每个分块都有一个称为分块句柄的标识符
GFS mast er维护文件系统的met adat a (metadat a),包括名称空(命名空间,即传统文件系统中的文件树)、访问控制信息、哪个组块由每个文件组成以及组块存储在哪个组块服务器上,即位置。
在这种架构下,读写文件的基本过程被简化并抽象为以下过程:
写过程
1.1 .客户端向masterer发送一个creat e e请求,包括文件路径和文件名。Masterer根据文件路径和文件名创建一个对象,用名称空表示这个文件。
2.客户端将待写入文件的数据发送到三分块服务器。每个chunkserver收到数据后,会将数据写入本地文件系统。写入成功后,它向Masterer发送请求,通知Masterer块写入成功,同时通知客户端写入成功。
3.Masterer收到chunkserver的成功写入后,记录下这个块与机器的对应关系,也就是块的位置。
4.在4.client确认所有三个chunkserver都已成功写入后,写入就成功了。
这个写作过程是一个高度简化和抽象的过程。实际的写作过程是一个非常复杂的过程。应考虑写入类型(即随机写入或尾部追加)和并发写入。后面我们会继续详细描述编写过程,解释GFS是如何处理不同的编写类型和并发编写的。
阅读过程
1.应用程序启动读取操作,并指定文件路径和偏移量
2.根据固定的块大小(即64MB),2.client计算数据的块号,即块索引号。
3.3 .客户端向Masterer发送请求,包括文件名和索引号,Masterer返回三个副本所在的三台机器,即副本的位置。
4.4 .客户端向其中一台复制机发送请求,请求替换块句柄和字节的读取范围。
5.chunkserver从本地文件系统读取数据,并根据块句柄和读取范围将其返回给客户端
这个阅读过程没有太多的简化和抽象,但实际阅读过程中会有一些优化,与本文的主题关系不大。
写过程细节
下面详细说说写作过程的几个细节。
1.名称间的名称空间管理和锁定空
写入过程需要向主机发送creat e等请求,以操作保存在主机上的名称空。如果多个客户端同时写入,这些客户端也会同时向主机发送创建请求。主机同时接收多个请求,通过锁定防止多个客户端同时修改同一文件的元数据。
2.租赁
客户端需要将数据写入三个副本。在并发的情况下,多个客户端将同时向三个副本写入数据。GFS需要一个规则来管理这些数据的写入。简而言之,每个块只有一个副本来管理多个客户端的并发写入。也就是说,对于一个数据块,Masterer将授予其中一个副本一个租约,而拥有该租约的副本将管理对该数据块的所有数据写入。带有租约的副本称为主副本。其他副本称为二级副本。
3.更改顺序(突变顺序)
写入一个文件(无论是写在任何位置还是追加在末尾)都叫Mut at ion。Primary管理所有客户端的并发请求,以便所有请求都按照一定的顺序应用到区块。
4.基本变更流程
1).①。客户端询问master哪个chunkserver持有该块的租约以及其他副本的位置。如果没有副本持有租约,Masterer选择一个副本并通知该副本它持有租约。
2).Masterer回复客户端,告知主副本的位置和客户端的次副本的位置。客户端会联系主副本,如果主副本没有响应或回复客户端它不是主副本,客户端会再次联系主副本。
3)客户端将数据推送到所有副本。客户可以按任何顺序推送。每个分块服务器都将数据缓存在缓冲区中。
4)当所有副本回复它们已收到数据时,客户端向主副本发送一个写请求,该请求标识先前写入的数据。收到请求后,主拷贝为写入分配一个连续的数字,主拷贝按数字顺序将数据写入本地磁盘。
5).主拷贝将编号的写请求转发给其他次拷贝,次拷贝也按照编号的顺序在本地写入数据,并回复主拷贝成功写入数据。
6).当主拷贝收到所有辅助拷贝的回复时,表示写操作成功。
7).主副本回复客户端写入成功。在任何副本上遇到的任何错误都将作为失败报告给客户端。
上面提到的writee接口遵循这个基本过程。下图描述了这个基本过程。(节选自英国政府飞行服务队的论文)
5.跨境变化
如果一次写入的数据量超过一个数据块的边界,客户端会将写入分解为对多个数据块的多次写入。
6.原子记录追加
本文称记录追加为原子记录追加。这个接口也遵循基本的变更流程,并且有一些附加的逻辑:客户端将数据推送到所有副本后,客户端向主副本发送请求,主副本收到写请求后,会检查记录是否附在尾部,不会超出块的边界。如果超出边界,它会填充块中剩余的空空间(这里填充什么并不重要,我们稍后会解释这个块),并让其他辅助副本做同样的事情,然后告诉客户端应该在下一个块中重试此写入。如果该记录适合该块的剩余空,则主副本将该记录附加到尾部,并告诉其他次副本写入的数据在相同的位置,最后通知客户端操作成功。
原子数
在谈完架构和读写流程之后,我们开始分析GFS的一致性,从原子性开始。
前面已经提到了writ e和Atomic recordappend之间的区别。如果写入次数超过一个块的边界,它将被分解为多个操作。当处理跨越边界的数据时,写E和记录追加的行为是不同的。下面举两个例子来说明。
示例1,文件当前有两个块,即chunk1和chunk2。
客户端1将以54MB的速度写入20MB的数据。这种写法越界,分为两种操作。第一个操作写入chunk1的最后10MB,第二个操作写入chunk2的前10 MB。客户端2还应该以54MB的速度写入20MB的数据。这个写操作也跨越了边界,也分为两个操作,chunk1的最后10MB作为第三个操作,chunk2的前10 MB作为第四个操作。
如果两个客户端同时写入数据,则第一个和第三个操作在chunk1上同时执行,第二个和第四个操作在chunk2上同时执行。如果chunk1先执行第一个操作,然后执行第三个操作,chunk2先执行第四个操作,然后执行第二个操作,最后,客户端1和客户端2写入的数据将保留在chunk1和chunk2上。虽然客户端1和客户端2都写成功了,但这既不是客户端1想要的结果,也不是客户端2想要的结果。最终结果是客户端1和客户端2的混合写入。对于客户端1和客户端2,它们的操作不是原子的。
示例2,文件当前有两个块,即chunk1和chunk2。
客户端将以54MB的速度写入20MB的数据。这种写法越界,分为两种操作。第一个操作写入chunk1的最后10MB,第二个操作写入chunk2的前10 MB。Chunk1执行第一个操作成功,chunk2执行第二个操作失败,也就是写入的数据部分成功,部分失败,不是原子操作。
让我们看看记录附加。记录追加最多可以写入16MB的数据,当写入的数据量超过块的剩余空时,剩余的空将被填充,因此将在下一个块中重试该写入操作。这两点确保记录附加操作只在一个块上生效。这样就避免了跨边界分解成多个操作,从而避免了两个非原子行为:一部分写入数据成功,另一部分失败,避免了另外两个非原子行为。
GFS原子性的意义
因此,GFS的原子性并不意味着多个副本之间的原子性,而是指在多个区块上发生的多个操作的原子性。可以得出结论,如果写操作不越界,那么不越界的写操作也满足GFS调用的原子性。
GFS多重副本不是原子的
在块的副本之间,GFS不是原子的,它也没有原子副本。它的行为是:
如果写操作成功,它将在所有副本上成功。如果失败,有可能一些拷贝会成功,另一些会失败。
在下面这样的行为中,失败是这样处理的:
如果是write失败,那么客户端可以重试,直到write成功,达到一致的状态。但是如果在重试达到成功以前出现宕机,那么就变成了永久的不一致了。Record Append在写入失败后,也会重试,但是与writ e的重试不同,不是在原有的off set上重试,而是接在失败的记录后面重试,这样Record Append留下的不一致是永久的不一致,并且还会有重复问题,如果一条记录在一部分副本上成功,在另外一部分副本上失败,那么这次Record Append就会报告给客户端失败,并且让客户端重试,如果重试后成功,那么在某些副本上,这条记录就会成功的写入2次。我们可以得出结论,记录追加保证至少是一次原子操作。
一致
GFS称自己的一致性为宽松的一致性模型。该模型分析元数据和文件数据的一致性。松弛主要是指文件数据具有松弛的一致性。
元数据的一致性
元数据操作都是由一个单独的Masterer来处理的,操作都是有锁保护的,所以是原子的,是正确的。
文件数据的一致性
在解释松弛一致性模型之前,让我们来看看这个模型中的两个概念。对于文件中的区域:
如果无论从哪个副本读取,所有的客户端都能总是看到相同的数据,那么就叫一致的(consist ent );在一次数据变更后,这个文件的区域是一致的,并且客户端可以看到这次数据变更写入的所有数据,那么就叫界定的(defined)。在GFS文件中,松弛一致性总结如下表所示:
写
记录追加
连续成功
界定
定义散布在一致的
并行成功
一致但未定义
失败
前后不一的
分别解释表格中的几种情况:
1.在没有并发的情况下,写是不会互相干扰的,成功的写是有定义的,所以一定要一致
2.在并发的情况下,成功的编写是一致的,但是没有定义,也就是我们前面引用的例子2。
3.如果写入失败,复制副本之间将存在不一致。
4.记录追加可以保证定义,但是定义的区域之间存在一些不一致的区域。“记录追加”将填充数据,无论每个副本是否填充相同的数据,该区域的这一部分都将被视为不一致。
如何适应宽松的一致性模型
这种松弛一致性模型实际上是一种不能保证一致性的模型,或者更准确地说,是不一致的数据与一致的数据混合在一起的模型。
这些不一致的数据混合在一起对于相应的应用是不可接受的。在这种一致性下,GFS应该如何使用?文中给出了几点建议:依靠追加代替覆盖,建立检查点,编写自验证和自识别记录。
用法1:当只有一次写入时,文件从头到尾生成。
方法1.1:先临时写入一个文件,再全部数据写入成功后,将文件改名成一个永久的名字,文件的读取方只能通过永久的文件名访问到这个文件。方法1.2:写入方按一定的周期,写入数据,写入成功后,记录一个写入进度检查点(checkpoint ),这个检查点包括应用级的校验数(checksum)。读取方只去校验、处理检查点之前的数据。即便写入方出现宕机的情况,重启后的写入方或者新的写入方,会从检查点开始,继续重新写入数据,这样就修复了不一致的数据。用法2:一个文件的末尾附加多个写操作。这种用法类似于生产-消费消息队列。多个生产者将消息附加到文件的末尾,消费者从文件中读取消息
方法2.1:使用记录追加接口,确保数据至少成功写入一次。然而,应用程序需要处理不一致的数据和重复的数据。
为了校验不一致数据,给每个记录添加校验数(checksum),读取方通过校验数识别出不一致的数据,并且丢弃不一致的数据。如果应用读取数据没有幂等处理,那么应用就需要过滤掉重复数据。写入方写入记录时额外写入一个唯一的标识(ident ifier),读取方读取数据后通过标识辨别之前是否已经处理过这个标识的数据。GFS的设计理念
可以看出,基于GFS的应用需要一些特殊的措施来处理GFS松弛一致性模型带来的各种问题。GFS的一致性保证对用户非常不友好,很多人第一次看到这样的一致性保证都很惊讶。
那么GFS为什么要设计这样的一致性模型呢?GFS在建筑中选择这样的设计有自己的设计理念。GFS奉行简单、充分的原则。GFS要解决的主要问题是如何使用廉价的服务器来存储海量数据,实现非常高的吞吐量(GFS已经很好的实现了这两点,但不是本文的主题,这里就不展开了),文件系统本身的实现应该比较简单,可以很快实现(GFS开发者开发GFS后很快就会开发出BigT able)。GFS很好的完成了这个目标。但是留下了一致性问题,给用户带来了负担。但是在GFS应用的前期,一致性不是问题。GFS的主要用户是GFS开发者,他们知道如何使用GFS。这种不一致性在BigT able中得到了很好的屏蔽(使用上述方法),BigT able提供了很好的一致性保证。
但是随着GFS的发展,简单而充分的GFS架构带来了很多问题,一致性只是其中之一。长期主导GFS的领袖肖恩·昆兰在采访中详细解释了GFS通过应用初期后这种简单架构带来的各种问题。
开源分布式文件系统HDFS清楚地看到了GFS一致性模型给用户带来的不便,并坚定地抛弃了GFS一致性模型,提供了良好的一致性保证。HDFS的一致性在这里就不展开了,会详细讨论。
总之,对于分布式文件系统乃至所有分布式系统来说,保证连续性是非常重要的。
1.《快进的进化》,肖恩·昆兰,2009,
https://queue.acm.org/detail.cfm? id = 1594206
1.《GFS 从 GFS 失败的架构设计来看一致性的重要性》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《GFS 从 GFS 失败的架构设计来看一致性的重要性》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/yule/1578222.html