GFS论文阅读

The Google File System,a scalable distributed file system for large distributed data-intensive applications。本文翻译6.824的gfs概述

为什么要读这篇论文

  1. 分布式存储是很关键的抽象
    • 定义接口、语义应该是怎么样的
    • 工作原理
  2. 6.824课程的主题:并发性能、容错、副本、一致性
  3. 好系统的论文 - 真实世界应用访问网络的细节

为什么分布式存储很困难?

high performance -> shard data over many servers many servers -> constant faults fault tolerance -> replication replication -> potential inconsistencies better consistency -> low performance

一致性应该是怎样的?

理想模型:表现和单个服务的行为一样 服务用的磁盘存储 服务一次执行一条客户端的操作(即使是并发) 读操作应该能观察到之前的写操作(即使服务奔溃重启) 因此: 假设C1、C2并发写,那么C3、C4将读到什么?

1
2
3
4
    C1: Wx1
    C2: Wx2
    C3:     Rx?
    C4:         Rx?

要么1,要么2,反正值是一样的。 这就是”强”一致性模型。 但单个服务器容错差劲

为了容错使用副本,增大强一致性的复杂度

设想一个简单但不可行的副本机制: - 两个副本,S1、S2 - 客户端,对写请求,同时发给两台 - 客户端,对读请求,请求其中一台

那么,对上述的例子,C1、C2的写请求如果以不同顺序到达两台服务器, 那么C3、C4分别读S1、S2的值就不一样了,这就不是强一致性了。

要求更高的一致性需要副本间的同步,而这会变慢!因此,要在性能和一致性之间权衡。

GFS的背景

  1. Google的服务,需要通用的大而快的存储系统, 比如MapReduce、crawler/indexer、log storage/analysis、YouTube
  2. Global(超过一个数据中心):任何客户端可以访问任何服务/磁盘
  3. 每个文件在服务/磁盘,自动分片
    • 提高并行度
    • 提高空间利用率
  4. 自动的故障恢复
  5. 一次部署,只一个数据中心
  6. 只对Google应用和用户
  7. 为了有序的访问大文件:读或写;做对比,并不是像低延迟数据库小操作那样

在2003年,为什么论文被SOSP接收了?

  1. 并非因为分布式、分片、容错这些基础的想法,因为在此前就有了
  2. 巨大的可伸缩性
  3. 工业界的生产实践
  4. 弱一致性的成功应用
  5. 单点master的成功应用

系统概述

  • clients客户端(库、RPC)
  • 每个文件分割成多个独立的64M的chunks
  • chunk servers,每个chunk存在3个chunk server上
  • 每个文件的chunks,被分布在chunnk servers上 为了提供对大文件的并发读、写,如MapReduce
  • 单点master,和master的副本
  • 划分功能:master负责naming(定位文件),chunk servers负责读写数据

master的状态

  • in RAM(为了速度,也不大):
    • file name -> array of chunk handles (nv)
    • chunk handle -> version # (nv) list of chunkservers (v) primary (v) lease time (v)
  • on disk:
    • log
    • checkpoint

nv: 非易失,v: 易失

为什么用log?checkpoint?

为什么用大的chunks?

系统架构

系统架构

  1. client请求master,发送filename、offset
  2. master找到chunkhandle
  3. master响应chunkserver列表(chunk是最新的那些)
  4. client缓存handle和chunkserver
  5. client请求最近的chunkserver,发送chunkhandle、offset
  6. chunkserver读取文件,返回数据

master如何知道chunkserver上我们要的chunk?

写流程

系统架构

步骤

  1. client向master请求那个chunkserver是chunk的的当前lease(租约), 以及chunk副本的chunkserver
  2. master返回primary(当前租约的chunkserver)和secondary(副本的chunkserver), 并且client讲缓存起来。再请求master的时,应该是primary不持有当前的lease
  3. client把数据推到各个chunkserver,通过底层的网络拓扑的优化,提高传输效率。
  4. 所有chunkserver都确认收到数据后,向primary发写请求,将之前推送的数据进行写入。 于是,primary进行了写操作。有多个写操作,那么primary的对这些写操作顺序是确定的
  5. primary再把写请求推给secondary,这样,secondary也用和primary相同的顺序进行写操作
  6. 所有secondary回应primary已经完成操作
  7. primary响应client,只要任何secondary返回失败,那么响应error, 也就有可能部分secondary数据已经不一致了。error时,client要在放弃前可以重试几遍(第3步-第7步)

所以会遇到很多问题:

  1. 比如第一次写B,由于s2失败了,导致客户端重试,那么有可能这样: primary :ABCB secondary1:ABCB secondary2:A CB 注:这里是要空出来的,他们要在同一位置append

GFS对客户端的一致性保证?

需要客户端自行解决一致性问题,比如对于顺序敏感的操作,由当个客户端发起请求。 对数据设有晒重的标志,比如哈希值

思考

如果primary告诉客户端append操作成功,那么接下来的任何读请求,应该都可以看到这一结果。

那么GFS是如何满足这一性质的呢? 观察一系列失败情况的处理:crash, crash+reboot, crash+replacement, message loss, partition

  • 如果发起写操作的client挂了?
  • 如果如果client从缓存取的primary已经失效?
  • 如果如果读操作,client从缓存读的secondary已失效?
  • master要是crash+reboot,会遗失哪些文件?会遗失chunkservers关联的chunk吗?
  • 两台clients同时写,会彼此覆盖记录吗?
  • 如果一台secondary从没执行primary的写操作,那么应对client的读请求要怎么做?
  • primary推写请求给secondary前crash,会怎样?某个secondary可以漏掉这个写请求, 当选新的primary吗?
  • 如果chunkserverS4,离线很久,其中的chunk版本很老了, 此时primary和在线的chunkserver都crash,S4恢复工作时,master会不会挑S4做primary? 是否有更好的办法解决旧版本的问题?

    master需要持久化chunk的版本

  • 如果某个secondary总是失败,primary怎么办?(比如磁盘空间不足,磁盘坏了), 是否primary应该把这台secondary移除secondaries列表?然后返回给客户端操作成功? 还是维持原来的操作,继续推,继续失败?
  • 要是primary S1响应client的请求时,master和S1的网络断了?”网络分裂” master会挑新primary吗?会不会有两个primaries? 那就可能一个primary写请求,一个primary读请求,破坏了数据一致性? “split brain”脑裂。

    为了避免脑裂,master应该要等持有lease的primary过期,那么lease要设一个可接受的等待时间

  • 如果其中一个primary服务客户端写请求时,租约到期了, master挑新的primary,新primary会有被之前primary更新的数据吗?
  • master fails?还有什么办法知道chunk的信息,比如chunk的版本号、priamry、租约时间
  • master挂了,怎么检测,谁检测?是否有替代方案? master的副本ping master,没响应就接管,可行吗?
  • 整栋楼停电会怎样?电力恢复后,重启,又会怎样?
  • 如果master想增加chunk的副本数(可能副本太少了), 怎样保证增加chunk的副本时,不会错过正在发生的写操作?毕竟此时还不是secondary
  • GFS有可能违反一致性保证吗?比如,写成功了,但后续的读请求观察不到
    • 所有master及副本永久丢失状态(比如磁盘坏了),这可能更糟,直接就没响应,而不是数据问题了
    • 包含这个chunk的所有的chunk server丢失磁盘数据。
    • 服务器数据未同步,租约机制失效,可能有多个primaries,那么可能写一个,读一个

GFS是否允许client观察到一些奇怪的行为?

  • 所有clients看到的文件内容一致?
    • 是否一些client能看到,一些client看不到?
    • 连续读两次,结果是否一样?
  • 所有clinets看到的写记录,顺序都一样?

这些奇怪的现象,对应用有什么影响?比如MapReduce?

没有这些奇怪现象,会带来什么 - 严格的一致性?

比如所有client看到相同的文件内容 难以描述,可以用案例思考:

  • primary要检测到client请求的副本,或者说client不该请求副本
  • 所有secondaries要么都写,要么都不写。可能会有中间状态,直到有确定的结果。但这个过程不该暴露出结果。
  • 如果primary crash,一些副本可能错过最新的操作,新primary应该同步给他们
  • 避免client读到ex-secondary,或者说client的读也要通过primary,或者说secondary也要有租约机制

性能

性能

读操作的吞吐量: 94MB/sec for 16 chunkservers, 即一个chunkserver,6MB/sec,good? 一个磁盘顺序访问的吞吐量大概30MB/s 一个NIC大概10MB/s 差不多就是网络饱和的状态(内网交换机) So: individual server performance is low but scalability is good which is more important?

两台GFS的性能

GFS对读操作500MB/sec,写不同文件的操作比可能的最大值低。 作者指出了网络攻击(没有细节) 并发对单文件写 受限于存储最新chunk的server 15年后会怎么样,很难预测,磁盘速度将有多快?

GFS一致性定义

一致性

write操作是需要指定offset的。 并发条件下,两个写的区域如果有重合,也就是写的顺序会影响结果。 那么发出写操作的两个clients,即使收到成功,但无法确认写的结果的。所以是consistent but undefined。 相反,串行操作,client是可以确定写成功的结果,是defined。

append操作,client不需要指定offset,由primary确定append的位置,最后返回给client, 不会发生两个append相互覆盖的情况,即使出现了,那么直接告诉client失败,再重试, 直到所有chunkserver都在同一个offset进行append,所以是defined,由于操作可能试了几遍才成功, 那么有部分secondary可能已经写过了,所以是interspersed with inconsistent

值得思考的

  • 如何更好得支持小文件?
  • 如何更好得支持数十亿文件?
  • GFS作为wide-area的文件系统吗? 在不同城市存放副本? 全部副本在一个数据中心,容错度不高
  • GFS故障恢复要多久? primary、secondary? master?
  • GFS如何应对处理慢的chunkservers

GFS工程师回顾

http://queue.acm.org/detail.cfm?id=1594206

  • 文件计数是最大的问题 文件数量增长了1000x(Table 2) master的RAM不适用了 master的GC,扫描文件太慢了
  • 1000多的clients请求master,CPU负载占用大
  • 应用必须遵循GFS的设计语义和限制
  • master的故障恢复需要人工接入,数十分钟
  • BigTable,可以用在小而多的文件存储问题。Colossus将master分片到多个master上

课程总结

  1. 案例学习:性能、容错、一致性
  2. 好的想法:
    1. 通用的全局文件系统
    2. 分离naming(master)和存储(chunkserver)
    3. 文件分片,提高并发吞吐量
    4. 大文件和大chunk,对比多的小文件,减少了必要的存储开支
    5. primary定义多个的写操作顺序
    6. lease防止脑裂
  3. 不够好:
    1. 单点master性能
    2. chunkserver对小文件效率不高
    3. 缺少master故障自动恢复
    4. 可能一致性的保障太弱了