谷歌gfs论文中文版 联系客服

发布时间 : 星期二 文章谷歌gfs论文中文版更新完毕开始阅读a1493d9c3086bceb19e8b8f67c1cfad6195fe986

2.7.2 程序的实现 GFS 应用程序可以利用一些简单技术适应这个宽松的一致性模型,这些技术已经满足了其他目的的需要:依赖追加而不是重写,检查点,自验证,自标识的记录。

实际中,我们所有的应用通过追加而不是重写的方式变更文件。一种典型的应用中,写入程序从头到尾地生成一个文件。写完所有数据之后,程序原子性地将文件重命名为一个永久的文件名,或者定期地对成功写入了多少数据设置检查点。检查点也可以包含程序级别的检验和。Readers仅校验并处理上一个检查点之后的文件域,也就是人们知道的已定义状态。不管一致性和并发问题的话,该方法对我们很适合。追加比随机写更有效率,对程序失败有更弹性。检查点允许Writer递增地重启,并且防止Reader成功处理从应用程序的角度看来并未完成的写入的文件数据。

在另一种典型应用中。许多Writer为了合并结果或者作为生产者-消费者队列并发地向一个文件追加数据。记录追加的“至少追加一次”的语义维持了每个Writer的输出。Reader使用下面的方法来处理偶然的填充和重复。Writer准备的每条记录中都包含了类似检验和的额外信息,以便用来验证它的有效性。Reader可以用检验和识别和丢弃额外的填充数据和记录片段。如果偶尔的重复内容是不能容忍的(比如,如果这些重复数据将要触发非幂等操作),可以用记录的唯一标识来过滤它们,这些标识符也通常用于命名相应程序实体,例如web文档。这些记录I/O功能(除了剔除重复数据)都包含在我们程序共享的代码库(library code)中,并且适用于Google内部其它的文件接口实现。这样,记录的相同序列,加上些许重复数据,总是被分发到记录Reader中。

3. 系统交互

我们设计这个系统力图最小化master与所有操作的牵连。在这样的背景下,我们现在描述客户机、master和Chunkserver如何交互以实现数据变更、原子记录追加以及快照功能。

3.1 租约(lease)和变更顺序

变更是改变块内容或者块元数据的操作,比如写操作或者追加操作。每次变更在块所有的副本上执行。我们使用租约(lease)来维护副本间的一致性变更顺序。Master向其中一个副本授权一个块租约,我们把这个副本叫做主副本。主副本为对块的所有变更选择一个序列。应用变更的时候所有副本都遵照这个顺序。这样,全局变更顺序首先由master选择的租约授权顺序规定,然后在租约内部由主副本分配的序列号规定。

设计租约机制的目的是为了最小化master的管理开销。租约的初始过期时间为60秒。然而,只要块正在变更,主副本就可以请求并且通常会得到master无限期的延长。这些延长请求和批准信息附在master和所有Chunkserver之间的定期交换的心跳消息中。Master

有时可能试图在到期前取消租约(例如,当master想令一个在一个重命名的文件上进行的修改失效)。即使master和主副本失去联系,它仍然可以安全地在旧的租约到期后和向另外一个副本授权新的租约。

在图2 中,我们根据写操作的控制流程通过这些标号步骤图示说明了这一过程。

1.客户机询问master哪一个Chunkserver持有该块当前的租约,以及其它副本的位置。如果没有chunkserver持有租约,master将租约授权给它选择的副本(没有展示)。 2.master将主副本的标识符以及其它副本(次级副本)的位置返回给客户机。客户机为将来的变更缓存这些数据。只有在主副本不可达,或者其回应它已不再持有租约的时候,客户机才需要再一次联系master。

3.客户机将数据推送到所有副本。客户机可以以任意的顺序推送数据。Chunkserver将数据存储在内部LRU 缓存中,直到数据被使用或者过期。通过将数据流和控制流解耦,我们可以基于网络拓扑而不管哪个Chunksever上有主副本,通过调度昂贵的数据流来提高系统性能。3.2章节会作进一步讨论。

4.当所有的副本都确认接收到了数据,客户机对主副本发送写请求。这个请求标识了早前推送到所有副本的数据。主副本为接收到的所有变更分配连续的序列号,由于变更可能来自多个客户机,这就提供了必要的序列化。它以序列号的顺序把变更应用到它自己的本地状态中(alex注:也就是在本地执行这些操作,这句话按字面翻译有点费解,也许应该翻译为“它顺序执行这些操作,并更新自己的状态”)。

5.主副本将写请求转发(forward)到所有的次级副本。每个次级副本依照主副本分配的序列顺序应用变更

6.所有次级副本回复主副本并标明它们已经完成了操作。

7.主副本回复客户机。任何副本遇到的任何错误都报告给客户机。出错的情况下,写操作可能在主副本和次级副本的任意子集上执行成功。(如果在主副本失败,就不会分配序列号和转发。)客户端请求被认定为失败,被修改的域处于不一致的状态。我们的客户机代码通过重试失败的变更来处理这样的错误。在退到从头开始重试之前,客户机会将从步骤(3)到步骤(7)做几次尝试。

如果应用程序一次的写入量很大,或者跨越了多个块的范围,GFS客户端代码把它分成多个写操作。它们都遵照上面描述的控制流程,但是可能会被来自其它客户机的并发操作造成交错或者重写。因此,共享文件域可能以包含来自不同客户机的片段结尾,尽管如此,由于这些单个的操作在所有的副本上都以相同的顺序完成,副本仍然会是完全相同的。这使文件域处于2.7节提出的一致但是未定义的状态。

3.2 数据流

为了有效地利用网络,我们将数据流从控制流中解耦。在控制流从客户机到主副本再到所有次级级副本的同时,数据以管道的方式,线性地的沿着一个精心挑选的Chunkserver链推送。我们的目标是充分使用每台机器的网络带宽,避免网络瓶颈和高延时的连接,最小化推送所有数据的延时。

为了充分使用每台机器的带宽,数据线性地沿着一个Chunkserver链推送,而不是其它拓扑分布(例如,树)。这样,每台机器所有出口带宽都用于尽快地传输数据,而不是在多个接受者之间分配带宽。

为了尽可能地避免网络瓶颈和高延迟的链接(比如,交换机之间的链路inter-switch经常既是瓶颈又高延迟),每台机器将数据转发到网络拓扑中离自己最近而又没收到数据的机器。假设客户机把数据推送到Chunkserver S1到S4。它把数据推送到最近的Chunkserver,比如说S1。S1将数转转发到从S2到S4中离S1最近的Chunkserver,比如说S2,同样的,S2转发数据到S3或者S4中离S2更近的那个,诸如此类。我们的网络拓扑足够简单,以至于从IP 地址就可以精确地估计“距离”。

最后,我们通过基于TCP连接的管道传输数据方式来最小化延迟。一旦Chunkserver接收到数据,它马立即开始转发。管道方式对我们帮助特别大,因为我们采用全双工连接的交换网络。立即发送数据不会降低接收速度。在没有网络拥塞的情况下,传输B字节数据到R个副本的理想经过时间是B/T+RL,其中T是网络的吞吐量,L是在两台机器间数据传输的延迟。我们的网络连接通常是100Mbps(T),L将远小于1ms。因此,1MB的数据在理想情况下80ms左右就分发出去。

3.3 原子的记录追加

GFS提供了一种叫做记录追加的原子追加操作。传统的写操作中,客户程序指定写入数据的偏移量。对同一个域的并行写不是串行的:域可能以包含来自多个客户机的数据片段结尾。在记录追加中,然而,客户机只需指定数据。GFS将其原子地追加到文件中至少一次(例如,作为一个连续的byte序列),数据追加到GFS选择的偏移位置,然后将这个偏移量返回给给客户机。这类似于在Unix中,对以O_APPEND模式打开的文件,多个并发写操作在没有竞态条件时对文件的写入。

记录追加在我们的分布应用中经常使用,其中很多在不同机器上的客户程序并发对同一文件追加。如果我们采用传统写方式处理,客户机将需要额外的复杂、昂贵的同步机制,例如通过一个分布式锁管理器。在我们的工作中,这样的文件通常用于多生产者/单消费者队列,或者是合并来自多个客户机的结果。

记录追加是一种变更,遵循3.1节的控制流,只主副本有些额外的控制逻辑。客户机把数据推送给文件最后一个块的所有副本,然后向主副本发送请求。主副本会检查如果追加这条记录会不会导致块超过最大尺寸(64MB)。如果超过,将快填充到最大尺寸,通知次级副本做同样的操作,然后回复客户机指出操作应该在下一个块重试。(记录追加限制在至多块最大尺寸的1/4,这样保证最坏情况下数据碎片的数量仍然在可控的范围。)如果记录在最大尺寸以内,这也是通常情况,主副本服务器将数据追加到自己的副本,通知次级副本将数据写在它准确的位移上,最后回复客户机操作成功。

如果记录追加在任何副本上失败,客户端重试操作。结果,同一个块的副本可能包含不同的数据,可能包括一个记录的全部或者部分重复。GFS并不保证所有副本在字节级别完全相同。它只保证数据作为一个原子单元的至少被写入一次。这个特性可以很容易地从简单观察中推断出来:操作如果要报告成功,数据一定已经写入到了一些块的所有副本的相同偏移上。并且,至此以后,所有副本至少都和记录尾部一样长,并且将来的记录会被分配到更高的偏移,或者不同的块,即之后一个不同的副本成为了主副本。就我们的一致性保障而言,记录追加操作成功写入数据的域是已定义的(因此也是一致的),然而中间域则是不一致的(因此也就是未定义的)。我们的程序可以像我们在2.7.2节讨论的那样处理不一致的域。

3.4 快照

(alex注:这一节非常难以理解,总的来说依次讲述了什么是快照、快照使用的COW技术、快照如何不干扰当前操作)

快照操作几乎瞬间完成对一个文件或者目录树(“源”)的拷贝,并且最小化对正在进行的变更的任何干扰。我们的用户使用它快速地创建一个大数据集的分支拷贝(而且经常递归地拷贝拷贝),或者是在做修改实验之前,对当前状态做检查点,这样之后就可以轻松的提交或者回滚。

就像AFS (alex注:AFS ,即Andrew File System ,一种分布式文件系统),我们使用标准的写时拷贝(copy-on-write)技术实现快照。当master收到快照请求,它首先取消在将要快照的文件中的块的任何未解决的租约。这保证了后续对这些块的写操作都需要与master交互以找到租约持有者。这就给master一个率先创建块拷贝的机会。

租约取消或者过期之后,master把这个操作以日志的方式记录到硬盘。然后,master通过复制源文件或者目录树的元数据的方式,把这条日志记录应用到内存状态。新创建的快照文件和源文件指向相同的块。

在快照操作之后,当客户机初次想写入数据到块C,它向master发送请求查询当前的租约持有者。Master注意到块C的引用计数比1大。Master延迟回复客户机请求,然后改为选择一个新的块句柄C`。之后,master要求每个拥有C当前副本的Chunkserver创建一个叫做C`的新块。通过在作为原件的同一Chunkserver上创建新的块,我们确保数据