分布式系统(二)——GFS

x33g5p2x  于2021-12-28 转载在 其他  
字(3.4k)|赞(0)|评价(0)|浏览(525)

分布式存储系统的难点

在存储系统中,为了获得巨大的性能加成,一个很自然的想法就是采用分片(sharding),将数据分割存储到多台服务器上,这样获得了更大的存储容量,而且可以并行地从多台服务器读取数据。
我们在成百上千台服务器上进行分片,大量基数的情况下,出现错误的频率也大大提升,我们需要一个自动化的从错误中恢复的方法,这就引入了容错(fault tolerance)。
实现容错的最有用的方法就是复制(replication),使用副本存储相同的数据。
有了副本,就需要考虑副本之间的不一致性(inconsistency)问题。
为了解决不一致性的问题,需要进行一定的设计,使各个副本之间保持一致,这需要网络通信来令服务器交互。因此,一致性的代价就是低性能。
这是一个循环,我们需要进行一定的权衡和牺牲。

GFS的设计目标

  1. 系统必须将故障视为一种常态,能够迅速侦测、冗余并恢复失效的组件;
  2. 系统需要支持大文件(数GB),并且能够有效地进行管理;
  3. 系统的工作负载主要是两种读操作:大规模的流式读取和小规模的随机读取,前者来自同一个客户机的连续操作读取同一个文件的连续区域,后者通常是在文件某个随机位置读取几个KB,通常后者会被合并并排序按顺序批量读取,以此提高性能;
  4. 系统的写工作负载时大规模的、顺序的数据追加方式的写操作,数据一旦被写入之后文件就很少会被修改了,同时系统也要支持小规模的随机位置写入;
  5. 支持多客户端并行追加数据到同一个文件;
  6. 关注批处理的性能,而非系统的响应速度。

GFS设计概述

GFS有一个master节点(逻辑上的一个),多个chunkserver,为client提供服务。文件被分割成固定大小的chunk,chunk创建的时候,master服务器会给每个chunk分配一个不变的、全球唯一标识的64位chunk标识,chunk服务器把chunk以linux文件的形式保存在本地的硬盘上,并且根据指定的chunk标识和字节范围来读写数据。出于可靠性考虑,每个chunk可能会被复制到多个chunkserver上。
master节点管理所有的文件系统元数据,但不存储文件数据(这使得GFS将文件系统管理和数据存储分开了),这些元数据包括名字空间、访问控制信息、文件和chunk的映射信息、当前chunk的位置信息。master节点还管理着系统范围内的活动,比如chunk租用、孤儿chunk的回收、以及chunk在chunkserver之间的迁移。master节点用心跳信息周期地和每个chunkserver通讯,发送指令到各个chunkserver并接收chunkserver的状态信息。
GFS客户端代码以库的形式被链接到client的客户程序里,使client可以通过这些函数进入GFS系统访问数据。
一次读取的流程是,首先client将文件名和客户程序指定的字节偏移,根据固定的chunk大小转换成文件的chunk索引,然后把文件名和chunk索引发送给master节点,master节点将相应的chunk表示和副本的位置信息发送回client,client用文件名和chunk索引作为key来缓存这些信息。之后客户端发送请求到其中一个最近的副本处(根据ip计算距离),请求信息包括了chunk的标识和字节范围。
tips:单一的master节点虽然大大简化了设计,但也可能导致master节点成为系统的性能瓶颈所在。因此我们必须尽量减少master节点的读写。
客户端将从master获取的元数据缓存一段时间,后续的操作将直接和chunkserver进行数据读写,除非元数据信息过期或者文件被client重新打开。

一些讨论

chunk尺寸

chunk的大小是关键的设计参数之一,通常选用64MB,这个尺寸远远大于一般文件系统的blocksize(惰性分配可以避免因为大的chunk尺寸造成的内部碎片)。这样做有许多优点:1. 减少了client和master的通信,因为只需要一次和master节点的通信就可以获取chunk的位置信息,之后就可以对同一个chunk进行多次的读写操作;2. 采用较大的chunk尺寸时,client能够对一个块进行多次操作,通过与chunkserver保持较长时间的tcp连接来降低网络负载;3. 较大的chunk尺寸减少了对master存储能力的要求,同时元数据也更有可能全部放在master的内存中,这样访问速度会快很多。
大的chunk尺寸也有一定的缺陷:这通常会产生热点问题。小的文件包含较少的chunk,如果多个client同时对小文件进行访问,存储这些chunk的chunkserver会成为热点。也许多个client同时访问一个小文件并不是GFS设计目标中的常见情景,但是当GFS初次启动的时候,一个可执行文件在GFS上存储为singe-chunk文件,之后这个可执行文件在数百台机器上同时启动,此时热点问题就出现了。为了解决热点问题,通常使用更大的复制参数来保存可执行文件(在更多的chunkserver上复制更多份)、或者错开系统启动的时间、再或者允许client从其他client读取数据(这里就引入了一个新的通信路径)。

元数据

master服务器主要存储三种类型的元数据:文件和chunk的命名空间、文件和chunk的对应关系、每个chunk副本的存放地点。命名空间和对应关系的更新采用保存变更日志的方式更新master服务器的状态,日记由操作系统记录在磁盘上,具有持久性。存放地点不会由master服务器持久化保存,master服务器在启动或者有新的chunkserver加入时,向chunkserver询问存储的chunk信息,同时定期轮询更新,以此获得chunk副本最新的存放地点。
tips:master始终通过chunkserver获取chunk的存放地点,这一设计源于一个思想:只有chunkserver才能最终确定一个chunk是否在它的硬盘上。例如,chunkservre的故障可能会导致chunk的消失,而这是master无法自动获知的。

一致性

这里的一致性是指chunk副本之间的一致性,要求所有chunk副本中保存的内容都是已定义的,并且包含最后以此修改操作写入的数据。为了达到这样的一致性,GFS有以下两项措施:对chunk的所有副本的修改操作顺序一致、使用chunk的版本号来检测副本是否因为它所在的chunk服务器宕机而错过了修改操作导致其失效。GFS通过master服务器和所有chunkserver的定期握手来找到失效的chunkserver,并且使用checksum来校验数据是否损坏。一旦发现问题,数据要尽快利用有效的副本进行恢复。

系统交互

tips:设计系统时,一个重要原则时最小化所有操作和master节点之间的交互。
使用租约(lease)对chunk的多个副本进行主从管理,master节点为chunk的一个副本建立租约,把这个副本叫做主chunk,主chunk对chunk的所有更改操作进行序列化,所有的副本都遵从这个序列进行修改。

  1. 客户机向master节点询问哪一个chunkserver持有当前的租约,以及其他副本的位置(如果没有任何一个持有租约,那么master建立一个);
  2. master将主chunk的标识符和其他副本(也叫secondary副本)的位置返回给客户机,client缓存这些数据,只有当主chunk不可用,或者主chunk回复信息表明它已经不再持有租约的时候,client才需要重新跟master节点联系;
  3. client推送数据到所有的副本上(可以以任意顺序推送)(tips:chunkserver用LRU缓存这些数据; 此处网络IO负载非常高,需要合理规划网络拓扑数据流);
  4. 当所有副本都确认接收了数据,client发送写请求到主chunkserver(这个请求标识了之前推送到副本的数据),主chunkserver为所有操作分配连续的序列号,在本地执行。
  5. 主chunkserver将写请求传递给所有二级副本,二级副本以相同的顺序执行这些操作;
  6. 二级副本完成操作后会回复主chunkserver。
  7. 主chunkserver回复client,如果失败或者部分失败,client将重新执行3~7步。

相关文章