笔记:GFS
课程概要
主要讨论Google第一代文件系统GFS。
正文
这篇论文的价值?
-
分布式存储系统是分布式系统的关键
接口和语义化应该如何定义、内部是如何工作的
-
GFS 涵盖的内容在6.824中有很多相关知识的涉及
并行性能、容错、复制、一致性
-
这篇论文属于分布式系统的标志性论文之一,从应用程序到网络。并且得到工业界的验证
构建分布式存储的难点在哪儿?
- 高性能:跨服务器的数据文件分片获取
- 多服务器:标志着更多的故障
- 容错:多副本
- 复制:潜在的不一致
- 一致性:导致性能降低
我们究竟想要如何样的一致性?
- 理想模型:所有的操作就像单机一样
- 服务端使用磁盘存储
- 服务端一次只执行一次客户端操作(即使并发)
- 读始终是最新的写入结果(即使服务器重启或崩溃)
因此,假设有几个场景c1和c2是并发写。在写完成后,发起C3和C4读取。那么会得到什么结果?
答案:可能是1或者是2,但是两个读都应该是相同的值返回(强一致性模型),但是单机无法做到好的容错。在现实分布式系统中肯定存在副本。
为了容错而产生的复制,让强一致性变得棘手。多副本同步写入,势必带来的是性能影响。性能/一致性之间需要取舍
一个简单的场景:两台副本服务器、S1和S2,客户端先发起并行写操作,然后立即读取。
GFS
论文概要
Big & Fast:Google的很多服务都需要统一的存储系统,Mapreduce、爬虫、索引、日志存储、分析、YouTube(?)、相册等等
Global:跨多个数据中心访问,任何客户端可以读取任何数据中心的文件。
sharding:一个文件可能跨多个服务器/磁盘。并行性能和磁盘利用
automatic recovery:出现故障能自动恢复
一个数据中心一次部署
暂时只供内部使用
大文件顺序访问:读/追加。而GFS又不是DB(事务)
在2003年,GFS是凭借什么获得了SOSP最佳论文?
文中的分布式、分区、错误容忍已经在分布式领域出现很久并不是第一次,主要的原因是:大规模在工业界,真实的世界中应用。对弱一致性和单master的实践。
GFS架构
客户端(library,RPC — 这个被使用者隐藏了起来)
每个文件被切分到独立的64MB块中
chunk server:每个块都有三份副本,所有的文件都可以迁移到其他chunk 服务器。支持并行读/写(MapReduce)
也可以存储大文件
分工独立:master负责读写分发,chunkserver负责具体数据
Master的数据
内存(容量小,速度快):
- file name table:文件名和chunkserver的映射map (写磁盘 Non-Volatile)
- chunk table: 文件版本(Non-Volatile)、chunkserver列表(Non-Volatile)、租约时间(Volatile)、primary信息,它负责写数据,然后派发副本(Volatile)
磁盘:log(日志,每次写操作)、checkpoint(快照恢复,当重启时能快速恢复到保存的最新状态)
客户端如何读取一个文件?
- 发送一个请求<filename,offset> 到master
- master根据offset查询chunk server(无缓存时)
- Master响应最新的chunkserver table给客户端
- 客户端缓存chunk handle 和chunkserver服务器列表
- 客户端发送请求<chunk handle,offset>到最近的chunkserver
- chunkserver从磁盘读取文件并响应给客户端(客户端接收的是buffer,library会合并为真正文件)
master如何知道chunk是在哪个chunkserver? 每次写都会经过master
客户端如果是追加一条记录需要经过哪些步骤?
- 客户端询问master最新的chunk offset
- 如果master没有找到primary chunk(租约过期)
- 当没有chunkserver可写,并且最新的版本号 丢失时直接返回错误
- 从其他的副本选举出新的primary chunkserver,以及文件的最新版本
- 增加版本号,并写日志到磁盘
- 告诉primary和secondary最新的版本号是多少
- 副本写新的版本到磁盘,并且响应结果给新的Primary
- Master告诉client 主副节点,client会对响应结果缓存。只有当Primary访问不到时才重新请求
- Client发送文件到所有的chunkserver(存内存)
- Client通知Primary进行append
- Primary检查当前租约是否过期,chunkserver是否有足够的存储空间
- Primary根据offset重新计算新的偏移位置
- Primary写chunk 文件(64MB)
- Primary通知secondary 新的offset,并且append 文件到chunk file
- Primary等待所有的副本响应(包括错误的响应,譬如空间不足、超时等)
- Primary通知客户端成功或者失败
- 客户端可以从第一步开始重试
GFS是如何保证一致性的?
GFS只 保证一次原子写入,数据已经在一个副本落盘。在经过一定时间后,所有的副本数据会一致。
假设出现网络分区,导致脑裂(split brain,存在两个primary)。出现这种情况,GFS引入60秒的租约时间,出现primary不能连通,就必须等待超时(剩余的时间client可以通过cache的primary信息计算)。
当Primary响应客户端已经成功append 记录,随后客户端立即进行读取,看到的应是最新的文件。(注意:不是所有的客户端都能看到最新的文件,GFS是弱一致性。需要等待时间同步到其他节点,通过“就近”复制.)
GFS 如何容错?
错误情况:机器崩溃,崩溃后重启、崩溃后被其他机器替换、消息丢失、分区。
假设设计的GFS是强一致性,需要处理什么问题?
e.g 保证所有的客户端看到的文件内容相同
- Primary应能识别重复写入的请求(保持幂等性),或者是客户端应该处理重复发送
- 所有的副本要不全部成功,要不全部失败。(必须全部副本写入才能响应成功)
- Primary崩溃,有些操作可能会被丢失。新的Primary需要与副本通信找回记录
- 避免读取到旧的数据,所有的读必须去Primary获取最新的记录。或者是在租约期内的副本。
GFS性能
- 读取吞吐量大(94MB/sec/16台chunkserver),平均每台6MB/sec(机械磁盘顺序查找的速度是30MB/sec、每个网卡大约10MB/sec的速度)交换机的连接接近饱和。因此每台机器的性能还是比较低,得益于可扩展性不错,在生产环境中单个GFS集群可以达到500MB/sec。但当遇到大量文件写入时,也会因为网络瓶颈而导致受限。
- 对文件进行append追加时,其最大的限制是最后一块chunk的追加,因为主 Chunk 会检查这次记录追 加操作是否会使 Chunk 超过最大尺寸(64MB)。如果超过了最大尺寸,主 Chunk 首先将当前 Chunk 填充到最大尺寸,之后通知所有其他副本做同样的操作,然后通知client要求其对下一个 Chunk 重新进行记录追加操作。
值得思考的问题
- 为了更好的支持小文件存储,需要些什么?
- 如何支持上亿的文件?
- 能否把GFS用作通用的文件存储系统?
- 跨异地复制如何优雅?
- 现在的部署是一个GFS部署一个数据中心,那么数据中心容灾如何考虑?
- GFS如何应对缓慢的chunk服务器?
关于GFS工程师采访反馈: GFS: Evolution on Fast-forward
- 文件数量的增加是一个大问题
- Master的内存是有上限的
- 文件数量增加,Master的GC就会慢下来
- master所能承载的客户端连接有限
- 应用程序需要按照GFS改造
- master的故障转移需要手动处理(10s)
在后面使用BigTable解决小文件存储,并且开发的第二代GFS——Colossus正是为了解决单点master问题
总结
GFS的论文阐述了性能、错误容忍、一致性相关的设计和评价。GFS是MapReduce中的基础设施。
亮点:
- 集群式文件系统演变为基础设施
- 存储与管理的分离标志(name管理在master,文件存储在chunkserver)
- 并行吞吐
- 大文件分块存储减少开销
- 主chunk顺序写设计
- 利用租期来解决“脑分裂”问题
不足:
- 单master的限制:内存和CPU
- 对小文件存储不够友好
- master的故障转移不够好
- 弱一致性
参考文献