Google File System设计思路

1.假设

假设是一切设计的前提,将场景进行限定。

  • 系统由许多易错设备构成(必须持续监控,容错并能从错误状态恢复)
  • 系统存储适量的大文件(针对大文件进行设计和优化)
  • 读考虑两种负载类型 1-大规模流式读取 2-小规模随机读取
  • 写考虑一种负载类型 1-大量连续写(文件追加)
  • 系统必须为并发文件追加提供良好定义的语义(将文件作为生产者-消费者模式的队列,或者多路归并使用)
  • 持续的高带宽比低延迟更重要(针对大数据批量设计)

2.接口设计

提供了与常规文件系统类似的接口(并没有实现标准体系的API,e.g.POSIX)。

文件在目录中分层管理,由路径名标示。

接口设计:create, delete, open, close, read, write, snapshot, record append。

  • snapshot  创建文件或目录树的拷贝
  • record append  并发安全的文件追加(原子方法)

3.架构

GFS是分布式框架,从整体上看是C/S架构,从服务端看是M-S架构。

服务器集群由单一的master和多个chunkserver组成,基于linux操作系统之上设计,并不是基于裸设备。

  • chunk  固定大小的空间单位,类似于block;每个chunk由不可变的&全局唯一的64位 chunk handle 标识, chunk handle在chunk被创建时由master统一分配。文件被切分为固定大小的chunks(这种设计必然导致文件的最后一个chunk可能存在碎片)。chunkserverchunk作为本地文件存储于本地磁盘,chunk数据的读写由chunk handle字节范围确定。为保证可靠性,每一个chunk都在多个chunk server中存在备份(通常备份数目为3,允许用户根据file namespace进行不同备份数目的个性化配置)。
  • master  master持有文件系统的元数据信息【namespace、访问控制信息、文件→块的映射信息、chunk的位置信息】;master控制全系统范围的活动【块租约管理、无根块垃圾回收、跨服务器的块迁移】;matser通过定期心跳信息与chunkserver交互【发送指令、收集状态信息(根据状态信息&既定规则决策后续处理发送具体指令)】
  • client  本质上是面向用户的接口设计,通过RPC与masterchunkserver进行交互。clientmaster交互元数据信息的处理,所有数据相关的通讯都直接与chunkserver交互。clientchunkserver都没有缓存设计;原因与前提假设相关,应用操作大多是基于大文件的流式数据处理,工作集太大不适宜做缓存设计(没有缓存就不涉及缓存一致性问题,简化了设计)。但是在元数据方面client确实设置了缓存。此处有一个比较容易产生误区的点 - chunk缓存和file缓存。chunk是GFS的概念,实际的chunk存储是依赖linux文件系统,因此物理存储上chunkserver有linux文件的使用,文件系统缓存是linux内核设计,与chunkserver无关。

4.分布式(主从设计)

单主设计(single master)

单主设计极大的简化了整体设计的复杂度,master根据全局信息决策chunk的迁移和备份

5.块大小

chunk固定大小设计 - 64MB。每个chunk副本都当作普通的linux文件存储在chunkserver机器上。chunk设计为64MB大小,远超linux系统空间管理的block块(段页)大小,因此内部碎片的影响放大了 - 惰性空间分配解决了此问题。

惰性空间分配的原理

大块设计的好处:

  • 减少client与master的交互(连续大片数据读取)
  • TCP长连接保证client与chunkserver的数据交互
  • 减少了元数据信息的量(每个块都需要元数据信息,块越大 → 元数据信息数量越少)

大块设计的坏处:容易产生热点(数据粒度大)

6.元数据

master主要存储3种类型的元数据 - 命名空间、文件与块的映射关系、块副本的位置信息。

三种元数据的存储方式也是不同的设计方案,首先三者都存储于master的内存中:

  • 前两者在master进行磁盘持久化;
  • 后者存储于chunkserver,master通过与chunkserver通信获取相关信息。

6.1 内存数据结构

元数据在内存中的数据结构设计有助于master进行定期后台巡检(统计chunkserver和chunk的状态信息,根据预定规则决策是否进行块的垃圾回收、重备份、块迁移);块迁移解决的事负载和磁盘空间使用的均衡。

所有元数据信息内存存储的设计,导致内存的大小决定了chunk数目的上限。因此元数据信息越小越好【64MB大小的chunk所需要的元数据大小不高于64B,因为使用前缀压缩的原因file namespace同样不超过64B】。

6.2 chunk位置

master并没有持久化存储chunk的位置信息,而是在master启动或者chunkserver注册加入集群的时候进行信息统计。两个时机的处理决定了即使master发生故障重启,信息也不会丢失。

master始终保持信息最新的设计方式 - 所有控制信息经过master(chunk的存储位置是master给出的)、心跳交互保证chunkserver的管理。

关于chunk位置信息为什么不进行master的持久化设计是一个很有意思的点,需要从实际应用场景的视角考虑。在成千上百的chunkserver存在的情况下,chunkserver的变化(加入、退出、重启、更名、故障等等)很频繁。另一种视角来理解这种非master非持久化设计是chunkserver才最终决定是否存储chunk(一切信息应该以chunkserver为准)。本质上两个方面都是一个问题 - chunkserver作为chunk的载体,其不确定性强,如果master进行持久化很容易造成数据一致性问题(内存、disk、chunkserver)。

6.3 操作日志

操作日志记录的是关键元数据信息的变更。

GFS的核心信息之一:

  • 元数据的唯一持久化记录;(怎么理解???master持久化存储了2种类型的元数据,对元数据的操作与元数据不是一个概念才对,详细看下文)
  • 作为判定并发操作顺序的标准逻辑时间线。

文件和块都由逻辑时间线唯一标识(版本号)。

因为其重要性,操作日志必须持在master持久化后才能对client端可见。否则即使chunkserver状态正常,也会丢失整个文件系统和client操作(master不可用,全局不可用)。

为提升master单点的可用性,从而提升GFS整体可用性,master信息会在本地和远程多备份,当且仅当本地和远程都落磁盘成功,才相应client。采用批处理(命令flush和replication)的方式提升系统吞吐量。master的故障恢复方式 - 日志重放,为提升master恢复速度,必须保证operation log足够小(重放速度不变,文件越大、恢复越慢)。要保证日志文件小,需要快照机制,快照机制就需要checkpoint。checkpoint的数据结构设计为类B树,可以在内存和磁盘上直接映射(特别注意数据结构设计 - 物理存储与实际使用息息相关)。

建立checkpoint需要时间,如何设计来“分离”建立checkpoint与新数据写入 - copy-on-write设计。

故障恢复只需要最新的完整检查点和其后续日志文件。旧的检查点和日志文件可以自由删除,但我们保留一些以防止灾难发生。检查点创建期间的失败不会影响正确性,因为恢复代码会检测并跳过不完整的检查点。

7.一致性模型

GFS有一个宽松的一致性模型,可以很好地支持我们的高度分布式应用程序,但实现起来仍然相对简单和高效。

7.1 GFS一致性保证

  • 文件命名空间变化(e.g. 文件创建)原子性;(操作日志保证全局有序)
  • 数据变化的类型决定了数据变化后文件求的状态;(数据变化类型:成功、失败、并发变化)

  • defined:明确定义的(文件区域在数据变化之后是一致的,且client可以完整的看到数据变化)
  • consistent:一致的(无论从哪个副本读取,所有client读到的是同样的数据)
  • inconsistent:不一致
  • undefined

数据的不同状态太多会带来设计和实现的复杂度。因此设计上仅区分definedundefined(不在进行状态细分)

造成数据变化的操作类型:

  • write  以应用程序指定的文件偏移量写入数据
  • record append  并发安全的追加记录(位置不确定,因为可能并发,谁先写入有master决定)

Note:在两个确定性区域之间可能会存在填充数据/重复记录等情况,但是对比用户数据量这种情况很少。

经历一系列数据变化之后,变化的文件区域会保证处于defined状态,持有最新数据 - GFS通过对chunk的所有副本按顺序执行变更的方式保证defined状态。

通过版本号判断当前chunk是否最新,过期的chunk副本将不会再发生变化(否则会造成过期判断失效),也不会对client服务。此外为空间利用和正确性保障,设计上将尽早对过期的chunk进行垃圾回收。

client会缓存chunk位置信息,这种设定会带来读到过期chunk的风险;这种可能性发生存在一定的时间窗口(缓存过期、文件重新打开 - 每次文件打开都会清空缓存);在append only的情况下,读过期chunk的影响会降低到读不到最新数据,而不会读到脏数据。(整体上来看,问题存在,只是影响的范围很小)

chunkserver存在失败的场景,这是不可避免的,GFS通过master与chunkserver心跳检测的方式判断chunkserver的状态,并且一旦发现失败的chunkserver,会马上进行chunk的再备份。(存在chunk不可用的可能性,所有的chunk备份都失效了,没来得及进行再备份)

7.2 对应用程序的影响

从7.1可以得到结论是GFS的一致性模型是比较宽松的一致性模型,存在数据读取失败的场景。

GFS的应用程序可以通过适应已经存在的技术来适配宽松的一致性模型。

8 系统交互

8.1 租约和数据变化顺序问题

master作为唯一的单点,为减少单点影响在设计上遵循master最小化参与原则。在此原则之下,看看客户端、主服务器和块服务器三者之间如何交互以实现数据变化、原子记录附加和快照。

  • 数据变化  write、append等操作(会改变chunk内容或者元数据信息)执行,被定义为数据变化
  • 原子的记录追加  
  • 快照功能(什么粒度的快照能力?客户数据文件、chunk、or chunkserver全部数据?)

租约(leases)在GFS中是什么概念?租约一般用于分布式系统中维持一致性,e.g. GFS系统master使用租约管理chunkServer

数据变化会被执行到所有的chunk副本上,租约就是用来保证副本上执行数据变化过程的一致性。主节点向其中一个副本授予块租约,我们称之为主副本(随机选主??)。主节点为该块的所有突变选择一个序列顺序。应用突变时,所有副本都遵循此顺序。因此,全局突变顺序首先由主节点选择的租约授予顺序定义,并在租约内由主节点分配的序列号定义。租约被设计来最小化master的管理开销。租约设置了初始的超时时间为60s,但是只要块在发生变化,主副本就可以无限制的请求和从master获取扩展。这些扩展请求和授权被搭载在主服务器和所有块服务器之间定期交换的 HeartBeat 消息上。

数据写入的设计中有一个点是控制流与数据流结耦设计。数据写入流程如下图所示:

如果应用程序的写入很大或跨越块边界,GFS 客户端代码会将其分解为多个写入操作。它们都遵循上述控制流程,但可能与来自其他客户端的并发操作交错并被覆盖。这就可能导致一致性但是undefined状态的发生。

8.2 数据流

我们将数据流与控制流分离,以有效地使用网络。虽然控制从客户端流向主服务器,然后流向所有辅助服务器,但数据以流水线方式沿着精心挑选的块服务器链线性推送。我们的目标是充分利用每台机器的网络带宽,避免网络瓶颈和高延迟链接,并最大限度地减少推送所有数据的延迟。为了充分利用每台机器的网络带宽,数据沿着块服务器链线性推送,而不是分布在其他拓扑结构(e.g. 树结构)中。因此,每台机器的全部出站带宽都用于尽可能快地传输数据(单向数据传输),而不是在多个接收者之间分发。

8.3 原子的记录追加

GFS 提供了一种称为记录追加的原子追加操作。在传统的写入中,客户端指定要写入数据的偏移量。对同一区域的并发写入不可序列化:该区域最终可能包含来自多个客户端的数据片段。然而,在记录追加中,客户端仅指定数据。 GFS 在选择的偏移量处以原子方式(即,作为一个连续的字节序列)将其附加到文件中,并将该偏移量返回给客户端。这类似于在 Unix 中写入以 O_APPEND 模式打开的文件而没有竞争条件(此时多个写入者同时这样做)。

8.4 快照

快照操作几乎可以立即复制文件或目录树(“源”),同时最大限度地减少正在进行的突变的任何中断。我们的用户使用它来快速创建庞大数据集的分支副本(通常是这些副本的副本,递归地),或者在尝试更改以后可以轻松提交或回滚之前检查当前状态。我们使用标准的copyOnWrite技术来实现快照。当 master 收到一个快照请求时,它首先撤销它要快照的文件中的块上的任何未完成的租约。这确保了对这些块的任何后续写入都需要与主节点交互以找到租约持有者。这将使master有机会首先创建块的新副本(即从操作优先级上看,快照优先)。

客户端在快照操作后第一次想要写入块 C 时,它会向 master 发送请求以查找当前的租约持有者。主节点注意到块 C 的引用计数大于 1。它推迟回复客户端请求,而是选择一个新的块句柄 C'。然后它要求每个拥有 C 的当前副本的块服务器创建一个名为 C' 的新块。通过在与原始数据块服务器相同的数据块服务器上创建新数据块,我们确保数据可以在本地复制,而不是通过网络复制(我们的磁盘速度大约是 100 Mb 以太网链路传输速度的三倍)。从这一点来看,请求处理与任何块的处理没有什么不同:主节点授予其中一个副本对新块 C' 的租约并回复客户端,客户端可以正常写入该块,但不知道它是刚刚从现有块创建的。

9 master operation

master执行所有的命名空间相关的操作。此外,master管理整个系统的块副本(块位置决策、新块创建及副本创建、协调系统范围的活动以保持块完全复制、平衡块服务器的负载,并回收未使用的贮存空间)。

9.1 命名空间管理和加锁

许多master operation可能需要很长时间,e.g. 快照操作必须撤销快照文件覆盖的所有块上的租约。我们不想延迟其他运行中的master operation。因此,我们允许多个操作处于活动状态,并在命名空间的区域上使用锁以确保正确的序列化。与许多传统文件系统不同,GFS 没有一个数据结构存储该目录中所有文件(按目录顺序);也不支持同一文件或目录的别名(即 Unix 术语中的硬链接或符号链接)。 GFS 在逻辑上将其命名空间表示为将完整路径名映射到元数据的查找表。通过前缀压缩,这张表可以在内存中有效地表示。命名空间树中的每个节点(绝对文件名或绝对目录名)都有一个关联的读写锁。也就是所有数据平铺处理

加锁会根据前缀进行加锁,本质上还是对树形结构的处理。这种锁定方案的一个很好的特性是它允许在同一目录中进行并发突变。例如,可以在同一个目录中同时执行多个文件创建:每个文件都获取目录名的读锁和文件名的写锁。目录名称上的读取锁定足以防止目录被删除、重命名或快照。文件名上的写锁定会将创建具有相同名称的文件的两次尝试进行序列化。 由于命名空间可以有很多节点,读写锁对象被延迟分配,一旦不使用就被删除。此外,锁以一致的总顺序获取以防止死锁:它们首先按名称空间树中的级别排序,并按字典顺序在同一级别内。

9.2 块副本放置

GFS 集群高度分布在多个级别上。它通常有数百个块服务器分布在许多机器机架上。反过来,这些块服务器可以从相同或不同机架的数百个客户端访问。不同机架上的两台机器之间的通信可能跨越一个或多个网络交换机。此外,进出机架的带宽可能小于机架内所有机器的总带宽。多级分布对分布数据的可扩展性、可靠性和可用性提出了独特的挑战。(物理部署和充分利用数据本地性的设计理念隐含其中)

块副本放置策略有两个目的:(存在矛盾,因此需要权衡)

  • 最大化数据可靠性和可用性;
  • 最大化网络带宽利用率。

对两者而言,仅将副本分布在机器上是不够的,这只能防止磁盘或机器故障并充分利用每台机器的网络带宽。我们还必须跨机架分布块副本。这确保了即使整个机架损坏或脱机(例如,由于网络交换机或电源电路等共享资源的故障),块的某些副本仍将存活并保持可用。这也意味着一个块的流量,尤其是读取,可以利用多个机架的聚合带宽。另一方面,写入流量必须流经多个机架,这是我们自愿做出的权衡。

9.3 块创建、再复制、再均衡

块副本创建的三个原因:新块创建、再复制、再均衡。

        当 master 创建一个块时,它会选择放置最初为空的副本的位置。它考虑了几个因素。 (1) 我们希望将新副本放置在磁盘空间利用率低于平均水平的块服务器上。随着时间的推移,这将均衡跨块服务器的磁盘利用率。 (2) 我们希望限制每个块服务器上“最近”创建的数量。尽管创建本身很便宜,但它可以可靠地预测即将到来的大量写入流量,因为块是在写入需要时创建的,并且在我们的追加一次读取多次工作负载中,它们通常在完全写入后实际上变为只读。 (3) 如上所述,我们希望将块的副本散布在机架上。

        一旦可用副本的数量低于用户指定的目标,master 就会重新复制一个块(再复制)。这可能由于多种原因而发生:块服务器不可用,它报告其副本可能已损坏,其中一个磁盘由于错误而被禁用,或者复制目标增加。每个需要重新复制的块都根据几个因素进行优先级排序。一是它离复制目标有多远。例如,我们赋予丢失两个副本的块比只丢失一个副本的块更高的优先级。此外,我们更喜欢首先为活动文件重新复制块,而不是属于最近删除的文件的块(参见第 4.4 节)。最后,为了最大程度地减少故障对运行应用程序的影响,我们提高了任何阻塞客户端进度的块的优先级。master 选择最高优先级的块并通过指示某个块服务器直接从现有的有效副本复制块数据来“克隆”它。新副本的放置目标与创建目标相似:均衡磁盘空间利用率,限制任何单个块服务器上的活动克隆操作,以及跨机架分布副本。为了防止克隆流量压倒客户端流量,master 限制了集群和每个 chunkserver 的活动克隆操作的数量。此外,每个块服务器通过限制其对源块服务器的读取请求来限制它在每个克隆操作上花费的带宽量。

        最后,master 周期性地重新平衡副本:它检查当前的副本分布并移动副本以获得更好的磁盘空间和负载平衡。同样通过这个过程,主服务器逐渐填满一个新的块服务器,而不是立即用新的块和随之而来的大量写入流量淹没它。新副本的放置标准与上面讨论的类似。此外,master 还必须选择要删除的现有副本。一般来说,它更喜欢删除那些在可用空间低于平均水平的块服务器上的那些,以平衡磁盘空间的使用

9.4 垃圾回收

惰性垃圾回收机制 - 文件删除后,GFS 不会立即回收可用的物理存储,在文件和块级别的常规垃圾收集期间统一回收(使系统更简单、更可靠)。

9.4.1 机制

当应用程序删除文件时,master 会立即记录删除操作,就像其他更改一样。但是,不是立即回收资源,而是将文件重命名为包含删除时间戳的隐藏名称。在 master 对文件系统命名空间的定期扫描期间,如果这些隐藏文件已经存在超过三天(超期时间间隔可配置),它会删除任何此类隐藏文件。在此之前,该文件仍然可以在新的特殊名称下读取,并且可以通过将其重命名为正常名称来取消删除。当隐藏文件从命名空间中删除时,其内存中的元数据将被删除。这有效地切断了它与所有块的链接。

在块命名空间的定期扫描过程中,主节点识别孤立块(无法从任何文件访问的块)并擦除这些块的元数据。在与 master 定期交换的 HeartBeat 消息中,每个 chunkserver 报告它拥有的块的子集,并且 master 回复所有不再存在于 master 元数据中的块的身份。块服务器可以自由地删除其此类块的副本。【chunkserver拥有的是chunk信息,master拥有的是filechunk的映射关系,因此两者之间的gap就是需要清理的孤立chunk

9.4.2 讨论

尽管分布式垃圾收集是一个难题,需要在编程语言的上下文中使用复杂的解决方案,但在我们的案例中却非常简单。我们可以很容易地识别出对块的所有引用:它们位于由 master 专门维护的文件到块的映射中。我们还可以轻松识别所有块副本:它们是每个块服务器上指定目录下的 Linux 文件。主人不知道的任何此类复制品都是“垃圾”。 - 单主设计带来的好处之一,极大地简化了GFS的整体设计,特别是数据一致性方面。

存储回收的垃圾收集方法比急切删除(惰性删除的反面)提供了几个优点

  • 首先,它在组件故障很常见的大规模分布式系统中简单可靠。块创建可能在某些块服务器上成功,但在其他块服务器上不成功,留下主服务器不知道存在的副本。副本删除消息可能会丢失,master 必须记住在失败时重新发送它们,包括它自己的和 chunkserver 的。垃圾收集提供了一种统一且可靠的方法来清理任何未知有用的副本。(允许未知失败存在)
  • 其次,它将存储回收合并到主服务器的常规后台活动中,例如定期扫描名称空间和与块服务器的握手。因此,它是分批完成的,成本是摊销的。而且,只有在主人比较空闲的时候才做。主机可以更迅速地响应需要及时关注的客户请求。(充分利用master空闲时间,而非强阻塞性的占用master周期)
  • 第三,回收存储的延迟提供了防止意外、不可逆删除的安全网。(支持删除取消)

根据我们的经验,主要缺点是延迟有时会阻碍用户在存储紧张时微调使用情况(惰性删除意味着空闲空间不会立即可用)。重复创建和删除临时文件的应用程序可能无法立即重用存储。如果删除的文件再次被显式删除,我们会通过加快存储回收来解决这些问题。我们还允许用户对命名空间的不同部分应用不同的复制和回收策略。例如,用户可以指定某个目录树中文件中的所有块都将被存储而不进行复制,并且任何已删除的文件都将立即且不可撤销地从文件系统状态中删除。

9.5 过期副本检测

如果块服务器发生故障并且在块关闭时错过了对块的突变,块副本可能会变得陈旧。对于每个块,master 维护一个块版本号来区分最新的和陈旧的副本。

每当主节点授予块的新租约时,它会增加块版本号并通知最新的副本。主服务器和这些副本都在其持久状态中记录新的版本号。这发生在任何客户端被通知之前,因此在它可以开始写入块之前。如果另一个副本当前不可用,则其块版本号将不会增加。当块服务器重新启动并报告其块集及其关联的版本号时,主服务器将检测到该块服务器具有陈旧的副本。如果 master 发现版本号大于其记录中的版本号,则 master 假定它在授予租约时失败,因此将更高的版本更新为最新版本。

主服务器在其常规垃圾收集中删除过时的副本。在此之前,当它回复客户端对块信息的请求时,它实际上认为一个陈旧的副本根本不存在。作为另一种保护措施,当master通知客户端哪个chunkserver持有一个chunk的租约时,或者当它指示一个chunkserver在克隆操作中从另一个chunkserver读取该chunk时,master会包含chunk版本号。客户端或块服务器在执行操作时验证版本号,以便始终访问最新数据。

10. 容错和错误诊断

我们在设计系统时面临的最大挑战之一是处理频繁的组件故障。组件的质量和数量共同使这些问题成为常态而不是例外:我们不能完全信任机器,也不能完全信任磁盘。组件故障可能导致系统不可用,或者更糟糕的是,数据损坏。我们讨论了我们如何应对这些挑战,以及我们在系统中内置的工具,用于在不可避免地发生问题时进行诊断。

10.1 高可用

在 GFS 集群中的数百台服务器中,有些服务器在任何给定时间都必然不可用。我们通过两种简单而有效的策略来保持整个系统的高可用性:快速恢复和复制。

10.1.1 快恢

无论它们如何终止,master 和 chunkserver 都旨在恢复它们的状态并在几秒钟内启动。实际上,我们不区分正常终止和异常终止;服务器通常只是通过终止进程来关闭。客户端和其他服务器在未完成的请求时仅会是一个小问题(超时、重新连接到重新启动的服务器并重试即可)。

10.1.2 块复制

如前所述,每个块被复制到不同机架上的多个块服务器上。用户可以为文件命名空间的不同部分指定不同的复制级别(默认值为三个)。主服务器根据需要克隆现有副本,以在块服务器离线或通过校验和验证检测损坏的副本时保持每个块完全复制(参见第 5.2 节)。尽管复制为我们提供了很好的服务,但我们正在探索其他形式的跨服务器冗余,例如奇偶校验或纠删码,以满足我们日益增长的只读存储需求。我们预计在我们非常松散耦合的系统中实现这些更复杂的冗余方案是具有挑战性但易于管理的,因为我们的流量主要由追加和读取而不是小的随机写入。

10.1.3 master复制

复制master状态以提高可靠性。master的操作日志和检查点被复制到多台机器上。只有在将其日志记录刷新到本地磁盘和所有主副本上之后,才会认为状态的突变已提交。【从应用层层面看,master是单点设计;但是从数据和高可用层面看,master数据是多备份冗余的】

为简单起见,一个master进程仍然负责所有突变以及后台活动,例如在内部更改系统的垃圾收集。当它失败时,它几乎可以立即重新启动。如果它的机器或磁盘发生故障,GFS 外部的监控基础设施会在其他地方启动一个新的主进程,其中包含复制的操作日志。客户端仅使用 master 的规范名称(例如 gfs-test),这是一个 DNS 别名,如果 master 重新定位到另一台机器,则可以更改该别名。

此外,“影子”master 提供对文件系统的只读访问,即使在主 master 关闭时也是如此。它们是阴影,而不是镜子,因为它们可能会稍微滞后于primary master(主master),通常是几分之一秒。它们增强了未主动变异的文件或不介意获得稍微陈旧结果的应用程序的读可用性。事实上,由于文件内容是从块服务器读取的,应用程序不会观察到陈旧的文件内容。在短窗口中可能过时的是文件元数据,例如目录内容或访问控制信息。

为了让自己了解情况,影子主服务器读取不断增长的操作日志的副本,并将与主服务器完全相同的更改序列应用于其数据结构。与主服务器一样,它在启动时(之后不经常)轮询块服务器以定位块副本并与它们交换频繁的握手消息以监视它们的状态。它仅依赖于主master来更新主节点创建和删除副本的决定所导致的副本位置更新。

10.2 数据完整性

每个块服务器使用校验和(checksum机制)来检测存储数据的损坏。鉴于 GFS 集群通常在数百台机器上拥有数千个磁盘,因此它经常会遇到磁盘故障,从而导致读取和写入路径上的数据损坏或丢失。 我们可以使用其他块副本从损坏中恢复,但是通过比较块服务器之间的副本来检测损坏是不切实际的(自身来校验数据完整性,但是通过其他副本来进行恢复)。为什么不能通过副本比对来确定数据完整性?- 副本的不同可能是合法的:GFS 突变的语义,特别是前面讨论的原子记录附加,并不能保证相同的副本。因此,每个块服务器必须通过维护校验和来独立验证自己副本的完整性。

chunk被分成 64 KB 的block。每个block都有相应的 32 位校验和。与其他元数据一样,校验和保存在内存中,并与日志记录一起永久存储,与用户数据分开。

对于读取,块服务器在将任何数据返回给请求者(无论是客户端还是另一个块服务器)之前验证与读取范围重叠的数据块的校验和。因此,块服务器不会将损坏传播到其他机器。如果一个块与记录的校验和不匹配,chunkserver 会向请求者返回一个错误,并将不匹配的情况报告给 master。作为响应,请求者将从其他副本读取,而主服务器将从另一个副本克隆该块。在一个有效的新副本就位后,master 指示报告不匹配的 chunkserver 删除其副本。

checksum机制对读取性能的影响很小,原因有几个。由于我们的大多数读取至少跨越几个块,因此我们只需要读取和校验相对少量的额外数据以进行验证。 GFS 客户端代码通过尝试在校验和块边界对齐读取来进一步减少这种开销。此外,在块服务器上的校验和查找和比较是在没有任何 I/O 的情况下完成的,并且校验和计算通常可以与 I/O 重叠。【设计技巧参照linux操作系统、java语言等对内存填充等的设计】

校验和计算针对附加到块末尾的写入(而不是覆盖现有数据的写入)进行了高度优化,因为它们在我们的工作负载中占主导地位。我们只是增量更新checksum(最后一个部分block checksum),并为附加填充的任何全新checksum block计算新校验和。即使最后一个部分校验和块已经损坏并且我们现在无法检测到它,新的校验和值也不会与存储的数据匹配,并且在下次读取该块时会照常检测到损坏。

相反,如果写入覆盖了块的现有范围,我们必须读取并验证被覆盖范围的第一个和最后一个块,然后执行写入,最后计算并记录新的校验和。如果我们在部分覆盖之前不验证第一个和最后一个块,新的校验和可能会隐藏未覆盖区域中存在的损坏。

在空闲期间,块服务器可以扫描和验证非活动块的内容。这使我们能够检测很少读取的块中的损坏。一旦检测到损坏,主服务器可以创建新的未损坏副本并删除损坏的副本。这可以防止不活动但损坏的块副本欺骗主节点,使其认为它有足够的有效块副本。

10.3 诊断工具

广泛而详细的诊断日志记录在问题隔离、调试和性能分析方面提供了不可估量的帮助,而成本却很低。没有日志,很难理解机器之间短暂的、不可重复的交互。 GFS 服务器会生成诊断日志,记录许多重要事件(例如块服务器的上升和下降)以及所有 RPC 请求和回复。这些诊断日志可以随意删除,不会影响系统的正确性。但是,我们尽量在空间允许的范围内保留这些日志。

RPC 日志包括在线上发送的确切请求和响应,但正在读取或写入的文件数据除外。通过将请求与回复匹配并整理不同机器上的 RPC 记录,我们可以重建整个交互历史来诊断问题。日志还用作负载测试和性能分析的跟踪。

由于这些日志是按顺序和异步写入的,因此日志记录的性能影响很小(并且远远超过了好处)。最近的事件也保存在内存中,可用于持续在线监控。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值