Google在03至06年发表了著名的三大论文——GFS、BigTable、MapReduce,用来实现一个大规模的管理计算系统。
今天先来谈谈GFS。因为论文里大段大段的文字加上专业术语读起来对我来说还是有一定困难的,这几篇论文我粗略地看了一遍,然后查询了一些资料,把我的理解以及把论文里一些原文提取出来整合了一下。把每个知识点单独列出来再分为更小的知识点,这样觉得比较容易理解。如果什么地方有理解错误的话,也请大家见谅。
谷歌之所以现在能称霸世界搜索引擎市场,最重要的技术就是GFS,GFS是google分布式存储的基石,其他存储系统,比如Google的bigtable、megastore、percolator均直接或者间接的构建在GFS上。Google File System那么被人称道的原因就是它是Google集群技术的核心。
一、GFS的设计目标
Google File System是一个面向密集应用的,可伸缩的大规模分布式文件系统,以满足其庞大的储存需求。GFS与之前的分布式文件系统具有以下不同:
1、组件失效被认为是常态事件,而不是意外事件
Google的一个应用至少有成百上千台服务器正在运转,同时也有一定数量级的用户进行访问,而系统不能保证时时刻刻每台服务器都运转正常,任意时刻下某个服务器都有可能因为人为操作的失误、程序的bug、操作系统的bug、硬件或者网络的问题,导致组件失效。即使昂贵的硬件设备也不能完全阻止这种情况发生,所以GFS使用多个廉价的磁盘驱动器来组成存储设备。为了对抗组件的失效,GFS中包含了监视、错误侦测、容错以及自动修复的机制。
2、系统存储一定量的大文件,数GB的文件非常普遍
因为GFS处理的都是大规模的数据集,以后面对的基本是数百GB级别的数据,所以设计的假设条件和参数,比如I/O操作和Block的尺寸都需要重新考虑。虽然GFS支持对以KB计算的小文件进行处理,但是这样做效率并不高。
3、系统的工作负载的读操作主要有两种:大规模的流式读取和小规模的随机读取。
大规模的流式读取通常一次读取1MB甚至更多的数据。来自同一个客户机的连续操作通常是读取同一个文件中的连续的一个区域。小规模的随机读取通常是在文件某个随即位置读取几个KB的数据。
所以,如果应用程序对性能非常关注,通常的做法是把小规模的随机读取操作合并并且排序,之后按顺序批量读取,这样就避免了在文件中前后来回移动读取位置。
4、绝大部分文件的修改是采用在文件尾部追加数据,而不是覆盖原有数据的方式。
Google应用中对大部分文件的修改,不是覆盖原有数据,而是在文件尾追加新数据。对文件的随机写是几乎不存在的,一个文件一旦被写好,只是被连续地读取。对于海量文件的访问模式,客户端对数据块缓存已经没有意义。
5、应用程序和文件系统的API的协同设计提高了系统的灵活性
Google希望通过简化GFS的一致性模型来简化文件系统,而不是给应用程序太多压力。由于同时读写数据的客户端很多,因此使用最小的同步开销来实现的原子的多路追加数据操作是必不可少的,所以数据的追加操作是性能优化和原子性保证的主要考量因素。
6、高性能的稳定网络带宽远比低延迟重要。
大多数的应用程序都是高速地处理大量数据,而对每一次的读写操作没有严格的时间要求。 目标程序(客户端)绝大部分要求能够高速率的、大批量的处理数据,高性能稳定的网络带宽比延迟更重要。
7、GFS提供了快照操作
GFS中的快照功能是非常强大的,可以非常快的对文件或者目录进行拷贝,并且不影响当前的操作(读、写、复制)。快照也能以很低的成本创建一个文件或者目录树的拷贝。
二、GFS架构
一个GFS集群包含了一个单独的Master节点(Master)、多台Chunk服务器(Chunkserver),并且同时被多个客户端(Client)访问。
Master:管理元数据、整体协调系统活动
ChunkServer:存储维护数据块,读写文件数据
Client:向Master请求元数据,并根据元数据访问对应ChunkServer的Chunk
1、GFS文件的存储方法
GFS存储的文件都被分割成固定大小的块(Chunk),之后Master服务器会给每个Chunk分配一个不变的、全球唯一的64位Chunk标识。Chunk服务器把Chunk以linux文件的形式保存在本地硬盘上,并根据指定的Chunk标识来读写Chunk数据。
出于可靠性考虑,每个Chunk都会复制到多个Chunk服务器上(缺省时为3个)。
2、Master节点职责
(1)管理所有文件系统的元数据,如名字空间(namespace)、访问控制信息、文件和Chunk的映射信息、以及当前Chunk的位置信息。
(2)管理系统范围内的活动,如Chunk租用(lease)管理、孤立Chunk的回收、以及Chunk在Chunk服务器之间的迁移。
(3)使用心跳信息周期地和每个Chunk’服务器通讯,发送指令到各个Chunk服务器并接受Chunk服务器的状态信息。
3、客户端
(1)GFS客户端和Master节点的通信只获取元数据,所有的数据操作都是由客户端直接和Chunk服务器进行交互的。
(2)GFS客户端代码以库的形式被链接到客户端程序里,客户端代码实现了GFS文件系统的API接口函数、应用程序与Master节点和Chunk服务器的通讯、以及对数据进行读写操作。
(3)客户端缓存数据几乎没有什么用处,因为大部分程序要么以流的方式读取一个巨大文件,要么工作集太大根本无法被缓存。但是客户端会缓存元数据。
三、GFS架构各模块
单一Master节点:
单一的Master节点可以通过全局的信息精确定位Chunk的位置以及进行复制决策。
另外,我们必须减少对Master节点的读写,避免Master节点成为系统的瓶颈。
客户端并不通过Master节点读写文件数据。反之,客户端向Master节点读写文件数据。反之,客户端向Master节点询问它应该联系的Chunk服务器。客户端将这些元数据信息缓存一段时间,后续的操作将直接和Chunk服务器进行数据读写操作。
客户端读取数据流程:
首先,客户端把文件名和程序指定的字节偏移,根据固定的Chunk大小,转换成文件的Chunk索引。
其次,它把文件名和Chunk索引发送给Master节点。
然后,Master节点将相应的Chunk标识和副本的位置信息发还给客户端。客户端用文件名和Chunk索引作为key缓存这些信息。
最后,客户端发送请求到其中一个副本处,一般会选择最近的。
Chunk尺寸:
由于GFS主要面向大文件存储和大规模读写操作,所以其选择了远大于一般文件系统的64MB的Chunk尺寸。
好处:
(1)减少了客户端与Master节点通讯的需求。
(2)客户端可以与一个Chunk进行多次操作,这样就可以通过Chunk服务器保持较长的TCP连接,减少网络负载。
(3)可以减少Master节点需要保存的元数据的数量。
坏处:
(1)易产生数据碎片。
(2)小文件占用Chunk少,对小文件的频繁访问会几种在少数ChunkServer上,从而产生小文件访问热点。
可能解决的方案:
(1)增大小文件复制参数。
(2)客户端间互传数据。
元数据存储方式:
命名空间、文件和Chunk的对应关系的存储方式:
内存:真实数据;
磁盘:定期Checkpoint(压缩B树)和上次CheckPoint后的操作日志;
多机备份:Checkpoint文件和操作日志。
Chunk位置信息的存储方式:
内存:真实数据
磁盘:不持久存储
Master不会持久保存Chunk位置信息,系统启动和新Chunk服务器加入时向各个Chunk服务器轮训它们所存储的Chunk信息。而且通过周期性的心跳信息监控Chunk服务器状态(数据同步问题一并解决)。
内存中的数据结构:
所有元数据保存在内存中,所以Master服务器的操作速度非常快。并且,Master服务器可以在后台简单而高效地周期性扫描自己保存的全部信息。
将元数据全部保存在内存中的方法有潜在问题:Chunk的数量以及整个系统的承载能力都受限于Master服务器所拥有的内存大小。但是在实际应用中,这并不是一个严重的问题。Master服务器只需要不到64个字节的元数据就能够管理一个64MB的Chunk。
四、一致性模型
我的理解是Master对于namespace的修改具有原子性,namespace在逻辑上可以理解为一张查找表,可以将路径名与元数据相映射。
比如说,当两个用户在应用程序中同时同一块文件区域写入了数据,或者一个用户和另外一名用户在同一时刻同一块文件区域分别进行了读写操作,为了确保数据的读写正确,必须要保证数据的一致性。
文件状态:
一致的(串行/并行追加写):如果所有客户端,无论从哪个副本读取,独到的数据都一样。
已定义的(串行随机写):如果对文件的数据修改之后,region是一致的,并且客户端能够看到写入操作全部的内容
一致但未定义(并行随机写):并行修改操作成功完成之后,所有的客户端看到同样的数据,但是无法读到任何一次写入操作写入的数据。
不一致(修改失败):file region内包含了来自多个修改操作的、混杂的数据片段或者不同的客户在不同的时间会看到不同的数据。
数据修改操作分为写入或者记录追加两种。写入操作把数据卸载应用程序指定的文件偏移位置上。即使有多个修改操作并行执行时,记录追加操作至少可以把数据原子性的追加到文件中一次,但是偏移位置是由GFS选择的。
GFS返回给客户端一个偏移量,表示了包含写入记录的、已定义的region的起点。
经过了一系列的成功的修改操作之后,GFS确保被修改的file region是已定义的,并且饱含最后一次修改操作写入的数据。GFS通过以下措施确保上述行为:
(1)对Chunk的所有副本的修改操作顺序一致
(2)使用Chunk的版本号来检测副本是否因为它所在的Chunk服务器宕机而错过了修改操作而导致其失效。
对失效副本的处理:
失效的副本不会再进行任何修改操作,Master服务器也不再返回这个Chunk副本的位置信息给客户端。它们会被垃圾收集系统尽快回收。
即使在修改操作成功执行很长时间之后,组件的失效也可能损坏或者删除数据。GFS通过Master服务器和所有Chunk服务器的定期“握手”来找 到失效的Chunk服务器,并且使用Checksum来校验数据是否损坏。一旦发现问题,数据要尽快利用有效的副本进行恢复。只有当一个Chunk的所有副本在GFS检测到错误并采取应对措施之前全部丢失,这个Chunk才会不可逆转的丢失。在一般情况下GFS的反应时间(译者注:指Master节点检测到错误并采取应对措施) 是几分钟。即使在这种情况下,Chunk也只是不可用了,而不是损坏了:应用程序会收到明确的错误信息而不是损坏的数据。
从程序实现的角度来说使用以下机制可以更好地实现一致性:
(1)采用追加写入而不是覆盖的方式。
(2)checkingpoint机制(每条数据追加写入的时候都包含一些额外的检验信息)。
(3)写时自检验自表示记录等等方式。
五、系统交互
设计这个系统的一个重要的原则是,最小化所有操作和Master节点的交互。
1、租约(lease)和变更顺序
Master收到变更操作请求后:
(1)选择一个Chunk副本发放定时租约作为主Chunk并返回给客户端;
(2)客户端与主Chunk进行通信进行变更操作;
(3)租约超时前,对该Chunk的变更操作都由主Chunk进行序列化控制。
(2)控制流从客户机到主Chunk、然后再到所有二级副本。
(3)数据沿着一个Chunk服务器链数序以管道方式推送。
(4)每台机器都尽量的在网络拓扑中选择一台还没有接收到数据的、离自己最近的机器(通过IP)作为目标推送数据。
(5)利用基于TCP连接的、管道式数据推送方式来最小化延迟。
3、记录追加的过程
(1)Master与Chunk副本建立一个lease。
(2)根据建好的租约,客户机将数据推送给主Chunk以及每一个Chunk副本。
(3)发送请求给主Chunk。
(4)主Chunk根据分配的序列号向Chunk副本中写数据。
追加操作会使Chunk超过尺寸:
填充当前Chunk;
通知二级副本做同样操作;
通知客户机向新Chunk追加;
追加操作不会使Chunk超过尺寸:
主Chunk追加数据;
通知二级副本写在相同位置上;
成功 - 返回偏移; 失败 - 再次操作。
(5)回复客户机追加操作成功。
4、快照
使用COW技术,瞬间完成。快照实现的过程:
(1)收回文件所有Chunk的租约;
(2)操作元数据完成元数据拷贝;
(3)客户端要写入该文件的Chunk时,Master通知该Chunk所在ChunkServer进行本地拷贝;
(4)发放租约给拷贝Chunk;
(5)返回拷贝Chunk的位置信息。
六、Master节点设计
Master节点执行所有的名称空间操作。此外,它还管理着整个系统里所有Chunk的副本:它决定Chunk的存储位置,创建忻Chunk和它的副本,协调各种各样的系统活动以保证Chunk被完全复制,在所有的Chunk服务器之间进行负载均衡,回收不再使用的存储空间。
1、名称空间管理和锁
保证Master节点上的并行操作以正确的顺序执行。文件操作需获得父目录读锁和目标文件/目录写锁。
2、副本的位置
Chunk跨机架分布:
两大目标:
(1)最大化数据可靠性和可用性
(2)最大化网络带宽利用率
3、创建,重新复制,重新负载均衡
(1)创建操作,主要考虑:
平衡硬盘使用率;
限制单ChunkServer短期创建次数(创建开销虽小,但每次创建往往意味着大量的后续写入);
跨机架分布。
(2)重复制,即有效副本不足时,通过复制增加副本数。优先考虑:
副本数量和复制因数相差多的;
live(未被删除)文件的;
阻塞客户机处理的Chunk进行重复制。策略与创建类似。
(3)重负载均衡,通过调整副本位置,平衡格机负载。策略与创建类似。新ChunkServer将被逐渐填满。
七、垃圾回收
GFS在文件删除后不会立刻回收可用的物理空间。
1、当一个文件被应用程序删除时,Master节点像对待其它修改操作一样,立刻把删除操作以日志的方式记录下来。
2、Master节点并不马上回收资源,而是把文件名改为一个包含删除时间戳、隐藏的名字。(设为隐藏文件)
3、Master节点对文件系统命名空间做常规扫描的时候,它会删除所有三天前的隐藏文件。
4、当隐藏文件被从名称空间中删除,Master服务器内存中保存的这个文件的相关元数据才会被删除。
5、Chunk服务器在和Master节点交互的心跳信息中,报告它拥有的Chunk子集的信息,Master节点回复Chunk服务器哪些Chunk在Master节点保存的元数据中已经不存在了。Chunk服务器可以任意删除这些Chunk的副本。
好处:
与创建失败的无效Chunk一致的处理方式;
批量执行开销分散,Master相对空闲时进行;
删除操作一定时间内可逆转。
坏处:
不便于用户进行 存储空间调优。
解决方案:
再次删除加速回收,不同命名空间不同复制回收策略。
过期检测:Master维护Chunk级别版本号,新租约增加Chunk版本号,并通知所有副本更新版本号,过期Chunk会因版本号过旧被检测。
八、容错机制设计
1、高可用性
(1)组件快速恢复
(2)Chunk复制
(3)Master服务器复制
Checkpoint和操作日志多机备份;
Master进程失效重启,硬件失效则新机器重启Master进程;
“影子”Master,通过操作日志“模仿”主Master操作,元数据版本稍慢。作用 - 分担一定负载、失效期暂时接管。
2、数据完整性
每个Chunk服务器必须独立维护Checksum来校验自己的副本的完整性。原因:
(1)跨Chunk服务器比较副本开销大;
(2)追加操作造成的的byte级别不一致,导致无法通过比较副本判断完整性。
Checksum校验的开销:
(1)Checksum读取开销;
(2)Checksum校验计算开销。
Checksum对读操作的性能影响:
Checksum对读操作的性能影响很小,可以基于几个原因来分析一下。因为大部分的读操作都至少要读取几个块,而我们只需要读取一小部分额外的相关数据进行椒盐,
GFS客户端代码通过每次把读取操作都对齐在Checksum block的边界上,进一步减少了这些额外的读取操作的负面影响。
九、总结
这篇论文前前后后看了大概一个星期,尽量写的让自己好读懂一些。
GFS支持了必需提供超大文件量与超大流量的 Google 搜寻引擎服务,是许多 Google 应用软件或云服务的基础,可以说是云时代的杀手级技术。
后面将会继续深入了解BigTable和MapReduce。