Google三大论文(二)GFS:大规模分布式文件系统

一.什么是GFS

GFS(Google File System )是一个大规模分布式文件系统。GFS与传统的分布式文件系统有着很多相同的设计目标,比如,性能、可伸缩性、可靠性以及可用性。而不同之处主要在于一下几点:

  • 因组件数目庞大,用户访问量大,组件失效被认为是常态事件。所以,持续的监控、错误侦测、灾难冗余以及自动恢复的机制必须集成在GFS中。
  • 以通常的标准衡量,我们的文件非常巨大。数GB的文件非常普遍。每个文件通常都包含许多应用程 序对象,比如web文档。当我们经常需要处理快速增长的、并且由数亿个对象构成的、数以TB的数据集 时,采用管理数亿个KB大小的小文件的方式是非常不明智的,尽管有些文件系统支持这样的管理方式。因 此,设计的假设条件和参数,比如I/O操作和Block的尺寸都需要重新考虑。
  • 绝大部分文件的修改是采用在文件尾部追加数据,而不是覆盖原有数据的方式。对文件的随机写入 操作在实际中几乎不存在。一旦写完之后,对文件的操作就只有读,而且通常是按顺序读。对于这种针对海量文件的访问模式,客户端对数据块缓存是没有意义的,数据的追加 操作是性能优化和原子性保证的主要考量因素。
     
  • 应用程序和文件系统API的协同设计提高了整个系统的灵活性

二.GFS的预期设计

  • 系统由许多廉价的普通组件组成,组件失效是一种常态。系统必须持续监控自身的状态,它必须将组 件失效作为一种常态,能够迅速地侦测、冗余并恢复失效的组件。
  • 系统存储一定数量的大文件。我们预期会有几百万文件,文件的大小通常在100MB或者以上。数个 GB大小的文件也是普遍存在,并且要能够被有效的管理。系统也必须支持小文件,但是不需要针对 小文件做专门的优化。
  • 系统的工作负载主要由两种读操作组成:大规模的流式读取和小规模的随机读取。大规模的流式读取 通常一次读取数百KB的数据,更常见的是一次读取1MB甚至更多的数据。来自同一个客户机的连续 操作通常是读取同一个文件中连续的一个区域。小规模的随机读取通常是在文件某个随机的位置读取 几个KB数据。如果应用程序对性能非常关注,通常的做法是把小规模的随机读取操作合并并排序, 之后按顺序批量读取,这样就避免了在文件中前后来回的移动读取位置。
  • 系统的工作负载还包括许多大规模的、顺序的、数据追加方式的写操作。一般情况下,每次写入的数 据的大小和大规模读类似。数据一旦被写入后,文件就很少会被修改了。系统支持小规模的随机位置 写入操作,但是可能效率不彰。
  • 系统必须高效的、行为定义明确的实现多客户端并行追加数据到同一个文件里的语意。我们的文件通常被用于”生产者-消费者“队列,或者其它多路文件合并操作。通常会有 数百个生产者,每个生产者进程运行在一台机器上,同时对一个文件进行追加操作。使用最小的同步 开销来实现的原子的多路追加数据操作是必不可少的。文件可以在稍后读取,或者是消费者在追加的 操作的同时读取文件。
  • 高性能的稳定网络带宽远比低延迟重要。我们的目标程序绝大部分要求能够高速率的、大批量的处理 数据,极少有程序对单一的读写操作有严格的响应时间要求。 

 三.GFS架构

一个GFS集群包含三个角色:一个单独的GFS Master总控制服务器,多台GFS Chunkserver(数据块服务器,简称CS)和多个GFS Client客户端.GFS存储的文件都被分割成固定大小的Chunk。 Chunk服务器把Chunk以linux文件的形式保存在本地硬盘上,并且根据指定的Chunk标识和字节范围来读写块数据。为了保证可靠性,Chunk在不同的机器中复制多份,缺省情况下,使用3个存储复制节点,不过用户可以为不同的文件命名空间设定不同的复制级别。

Master中维护了系统的元数据(SQL中的说法是:数据的数据),这些元数据包括Chunk名字空间、访问控制信息、文件和Chunk的映射信息、以及当前Chunk的位置信息.。Master还管理着系统范围内的活动,比如, Chunk租用管理无用Chunk的回收、以及Chunk在Chunk服务器之间的迁移。 Master节点使用心跳信息周期地和每个Chunk服务器通讯,发送指令到各个Chunk服务器并接收Chunk服务器的状态信息。

Client代码实现了GFS文件系统的API接口函数以及应用程序的访问接口。应用程序从Master获取元数据,根据元数据提供的信息与Chunk服务器直接进行交互。从架构图可以看出,Client和Master之间的交互只有控制流(指令信息),没有数据流,因此降低了Master的负载 (因为控制流只需传送指令和状态,数据量小)。

四.一致性模型

1.什么是一致性

理解起来很好理解,在分布式系统中,数据会往往会复制成多个副本,当对其中一个副本的数据进行修改时,其他多个副本也应进行相应的改动以保证分布式系统中数据的一致。

2.影响一致性的操作:

  • 修改元数据
  • 写数据:向一个文件块中写数据,客户端指定要写的数据和要写的数据在一个文件中的偏移量。
  • 追加数据:客户端指定要将哪些数据追加到哪个文件后,系统返回追加成功的数据的起始位置。

3.GFS中的一致性

元数据的一致性

元数据只有一份(不考虑hidden master和日志),不存在副本一致性问题,只考虑传统数据库中的内部一致性问题,即符合数据库的内部约束,GFS对元数据的修改都要加锁,隔离各个操作。

修改元数据+无并发

操作只有成功和不成功两情况,元数据永远是一致的。

修改元数据+并发

GFS存在锁机制,会将各个操作依次执行,与无并发一样。只不过并发的修改后值是多少不确定(是我不确定,不是GFS不确定,也许有一定的机制来对操作排序,也许是自由竞争)。元数据还是一致的。

可以看到,如果数据只有一份,总是一致的。只有并发会产生语义的问题,需要根据应用逻辑进行并发处理。

一个文件块(chunk)的一致性

这里只讨论 一个chunk ,也就是 一个文件块 的写操作,不涉及整个文件的写流程中数据和元数据的流程,原论文里好像也没介绍文件的写流程。

每个chunk默认有3个副本,不同副本会存在不同节点上,master会设置1个主副本(primary),2个二级(secondary)副本。

当写操作和追加操作失败,数据会出现部分被修改的情况,于是肯定会出现副本不一致的情况,这时就依赖master的重备份来将好的副本备份成N份。以下只考虑操作成功的情况。

写一个chunk+无并发

写一个chunk时,客户端向primary发送写请求(一个chunk对应几个写请求不确定,这里不影响理解,当做一个看就可以了)。primary确定写操作的顺序,由于没有并发,只有一个写请求,直接执行这个写请求,然后再命令secondary副本执行这个写请求。其他secondary都按照这个顺序执行写操作,保证了全局有序,并且只有当所有副本都写成功,才返回成功,用系统延迟保证了数据强一致,即 consistent(所有副本的值都一样) 。

这个强一致指每个写成功后,所有客户端都能看到这个修改。即论文中说的 defined 。defined 的意思是知道这个文件是谁写的(那么谁知道呢?肯定是自己知道,其他客户端看不到文件的创建者)。也就是当前客户端在写完之后,再读数据,肯定能读到刚才自己写的。

写一个chunk+并发

这时primary可能同时接受到多个客户端对自己的写操作。举个例子,两个客户端同时写一个chunk。w1或w2代表(写操作+数据)。下边表示client1想将这个chunk写成w1,client2想将这个chunk写成w2。

  • client1:w1

  • client2:w2

于是primary要将这些写操作按某个机制排个顺序:

  • primary:w2,w1

然后在primary本地执行,于是这个chunk首先被写成w2,之后被覆盖成w1。

之后所有secondary副本都会按照这个顺序来执行操作,于是所有副本都是w1,这时数据是 consistent 的,也就是副本一致的。因为所有操作都正确执行了,所以两个client都收到写成功了。但是谁也不能保证数据一定是自己刚才写的,也就是 undefined 。这与最终一致性有点像(系统保证所有副本最终都一样,但是不保证是什么值)。

追加数据+无并发

追加数据时,会追加到最后一个chunk,其实和写一个chunk+无并发基本一样。 
但由于追加操作和写文件不一样,追加操作不是幂等的,当一次追加操作没有成功,客户端重试时,不同副本可能被追加了不同次数。

假设追加了一个数据a

  • client:追加a。

第一次追加请求执行了一半失败了,这个chunk的所有副本现在是这样:

  • primary:原始数据,offset1:a
  • second1:原始数据,offset1:a
  • second2:原始数据

于是客户端重新发送追加请求,因为primary会先执行操作再将请求发给secondary,所以primary当前文件是最长的(先不考虑primary改变的情况)。primary继续往offset2(当前文件末尾)追加,并通知所有secondary往offset2追加,但是secondary2的offset2不是末尾,所以会先补空。如果这次追加操作成功,数据最终会是这样:

  • primary:原始数据,offset1:a,offset2:a
  • second1:原始数据,offset1:a,offset2:a
  • second2:原始数据,offset1:*,offset2:a

并且给客户端返回 offset2 。

于是数据中间一部分是 inconsistent。但是对于追加的数据是 defined 。客户端再读offset2,可以确定读到a。 
这就是追加操作的defined interspersed with inconsistent。

追加数据+并发

两个客户端分别向同一个文件追加数据a和b

  • client1:追加a
  • client2:追加b

最后一个文件块的primary接收到追加操作后进行序列化

  • primary:b,a

然后执行,b失败了一次,于是client2再发送一次追b。primary再追加一次。

  • primary:原始数据,off1:b,off2:a,off3:b
  • second1:原始数据,off1:b,off2:a,off3:b
  • second2:原始数据,off1: ,off2:a,off3:b

client1收到GFS返回的off2(表示a追加到了文件的off2位置),client2收到off3

也满足off2和off3是 defined ,off1是 inconsistent ,所以总体来说是 defined interspersed with inconsistent

可以看到,不管有没有并发,追加数据都不能保证数据全部 defined,只能保证有 defined ,但是可能会与 inconsistent相互交叉。

(以上关于一致性的描述摘抄自https://blog.csdn.net/qiaojialin/article/details/71574203)

五.交互系统

1.租约机制

目的:最小化Master节点的管理负担。

简介:Master节点为 Chunk的一个副本建立一个租约,我们把这个副本叫做主Chunk。主Chunk对Chunk的所有更改操作进 行序列化。所有的副本都遵从这个序列进行修改操作。因此,修改操作全局的顺序首先由Master节点选择 的租约的顺序决定,然后由租约中主Chunk分配的序列号决定。 

流程:

  1. 客户机向Master节点询问哪一个Chunk服务器持有当前的租约,以及其它副本的位置。如果没有一 个Chunk持有租约,Master节点就选择其中一个副本建立一个租约。
  2. Master节点将主Chunk的标识符以及其它副本(又称为secondary副本、二级副本)的位置返回给 客户机。客户机缓存这些数据以便后续的操作。只有在主Chunk不可用,或者主Chunk回复信息表 明它已不再持有租约的时候,客户机才需要重新跟Master节点联系。
  3. 客户机把数据推送到所有的副本上。客户机可以以任意的顺序推送数据。Chunk服务器接收到数据 并保存在它的内部LRU缓存中,一直到数据被使用或者过期交换出去。由于数据流的网络传输负载非 常高,通过分离数据流和控制流,我们可以基于网络拓扑情况对数据流进行规划,提高系统性能,而 不用去理会哪个Chunk服务器保存了主Chunk。
  4. 当所有的副本都确认接收到了数据,客户机发送写请求到主Chunk服务器。这个请求标识了早前推 送到所有副本的数据。主Chunk为接收到的所有操作分配连续的序列号,这些操作可能来自不同的 客户机,序列号保证了操作顺序执行。它以序列号的顺序把操作应用到它自己的本地状态中 
  5. 主Chunk把写请求传递到所有的二级副本。每个二级副本依照主Chunk分配的序列号以相同的顺序执行这些操作。
  6. 所有的二级副本回复主Chunk,它们已经完成了操作。
  7. 主Chunk服务器回复客户机。任何副本产生的任何错 误都会返回给客户机。在出现错误的情况下,写入操作可能在主Chunk和一些二级副本执行成 功。(如果操作在主Chunk上失败了,操作就不会被分配序列号,也不会被传递。)客户端的请求 被确认为失败,被修改的region处于不一致的状态。我们的客户机代码通过重复执行失败的操作来 处理这样的错误。在从头开始重复执行之前,客户机会先从步骤(3)到步骤(7)做几次尝试

2. 数据流 

目的:提高网络传输的效率。充分利用每台机器的带宽,避免网络瓶颈和高延时的连接,最小化推送所有数据的延时。
简介:把数据流和控制流分开的措施。在控制流从客户机到主Chunk、然后再到 所有二级副本的同时,数据以管道的方式,顺序的沿着一个精心选择的Chunk服务器链推送。

3.原子的记录追加 

在上面解释一致性模型的时候其实已经对此略有提及,GFS提供了一种原子的数据追加操作–记录追加。传统方式的写入操作,客户程序会指定数据写入的偏移 量。对同一个region的并行写入操作不是串行的:region尾部可能会包含多个不同客户机写入的数据片段。使用记录追加,客户机只需要指定要写入的数据。GFS保证至少有一次原子的写入操作成功执行(即 写入一个顺序的byte流),写入的数据追加到GFS指定的偏移位置上,之后GFS返回这个偏移量给客户机。这类似于在Unix操作系统编程环境中,对以O_APPEND模式打开的文件,多个并发写操作在没有竞态条件时的行为。

如果记录追加操作在任何一个副本上失败了,客户端就需要重新进行操作。重新进行记录追加的结果是, 同一个Chunk的不同副本可能包含不同的数据–重复包含一个记录全部或者部分的数据。GFS并不保证 Chunk的所有副本在字节级别是完全一致的。它只保证数据作为一个整体原子的被至少写入一次。

六.Master节点的操作

  • 管理命名空间和锁
  • 选择Chunk副本的位置
  • 对Chunk副本进行创建,重新复制,重新负载均衡。
  • 进行垃圾回收
  • 进行过期失效的副本检测

七.容错机制

容错机制是GFS中比较重要的机制,因为在GFS中,组件失效是一种常态,还可能造成系统的不可用。我们讨论的是如何面对这些挑战,以及当组件失效不可避免的发生时,用GFS自带工具诊断系统故障。

 1.高可用性

在GFS集群的数百个服务器之中,在任何给定的时间必定会有些服务器是不可用的。我们使用两条简单但 是有效的策略保证整个系统的高可用性:快速恢复和复制。

  • 快速恢复:Master服务器和Chunk服务器可以在任何情况下快速恢复状态并重新启动
  • chunk复制:每个Chunk都被复制到不同机架上的不同的Chunk服务器上。用户可以为文件命名空间的不同部分设定不同的复制级别,缺省是3。
  • Master服务器的复制 :为了保证Master服务器的可靠性, Master服务器的状态也要复制。 Master服务器所有的操作日志和checkpoint文件都被复制到多台机器上。

2.数据完整性 

每个Chunk服务器都使用Checksum来检查保存的数据是否损坏。每个Chunk服务器必须独立维护Checksum来校验自己的副本的完整性

3.诊断工具

详尽的、深入细节的诊断日志,在问题隔离、调试、以及性能分析等方面给我们带来无法估量的帮助,同 时也只需要很小的开销。

八.总结

GFS是谷歌三大论文中个人认为较为理解的一篇,主要为我们介绍了由一个Mster服务器,多个Chunk服务器,多个客户端通过特殊的交互方式组成的大规模分布式文件系统。主要搞清楚GFS中每个板块的作用,交互模式,及其的容灾方式就可以了。

在这里附上谷歌三大论文中文翻译版的链接:https://pan.baidu.com/s/1SNP3S3USTwMhh83f-m_o-g 密码:skzv

感谢浏览

你可能感兴趣的:(Google三大论文(二)GFS:大规模分布式文件系统)