大家看gfs的论文和《大规模分布式存储系统》中的GFS部分
为什么我们阅读这个论文?
- Map/reduce使用这种文件系统
- 处理存储出错的案例学习
- 用一致性换取简单和性能(trading consistency for simplicity and performance)
- 后续设计的动机(motivation for subsequent designs)
- 好的性能 —— 良好的并行I/O性能
- 好的系统论文 —— 从apps到网络都有细节说明
- 关于课程6.284的全部主题都会在这个论文中出现,性能、容错、一致性
一致性是什么?
- 正确性条件
- 当数据存在副本,同时被应该程序并发访问的时候,正确性非常重要。
- 如果一个应用程序进行写操作,那么之后的读操作可以观察到什么?如果这个读操作来自其他应用程序又会看到什么?
- 弱一致性
- read()可能返回不新鲜的数据 ———— 不是最近写操作的结果
- 强一致性
- read()返回的结果数据是最近一次的写操作结果
- 一般的权衡:
- 强一致性对程序的写操作(application writers)表现不错
- 强一致性将会影响性能
- 更多的正确性条件(通常被称为一致性模型)
一致性模型的历史
- 在架构,系统和数据库社区存在独立发展
- 带有私有缓存的并行处理器访问共享内存
- 并行客户端访问分布式文件系统
- 分布式数据库之上的并行事务
- 不同的模型考虑不同的权衡
- 可串行性(serializability)
- 顺序一致性(sequential consistency)
- 线性一致性(linearizability)
- 单项一致性模型(entry consistency)
- 松散一致性(release consistency)
- ......
“理想”的一致性模型
- 一个存在副本的文件表现的跟单一文件系统一样,就像很多客户端访问存在同一个机器的单一磁盘的文件
- 如果一个程序写操作,之后的读操作会获取到这个写的结果。
- 如果两个程序同时写同一份文件会怎么样?
- 在文件系统中这种行为经常是未定义的 —— 文件也许会混合两个写操作的内容
- 如果两个应用程序并发写同一个目录会怎么样?
- 一个一个顺序执行
不一致的来源
- 并发
- 机器失败
- 网络割裂
来自GFS论文的例子
- 主节点是备份分区B,客户端添加1,主节点将1发送给自己和分区备份A, 告诉客户端失败,同时客户端访问分区B,可能获取到老的值
为什么理想中的一致性模型在分布式文件系统中的实现这么困难?
- 理想的一致性模型协议非常复杂 —— 后面的课程我们会看到很难实现正确的系统
- 协议需要客户端和服务器进行通信,这样会消耗性能
GFS的设计者为了最求更好的性能和更简单的设计而放弃理想的一致性模型
- 能否使应用程序开发人员的生活困难
- 在一个理想的系统中应用程序的行为不容易被观察到
- 如:获取到过期的数据
- 如:重复添加记录
- 应用数据不是你的银行账号,所以这样可能不存在问题
- 今天的论文是展现下面因素权衡的一个例子
- 一致性
- 容错性
- 性能
- 简单的设计
GFS的目标
- 创建共享文件系统
- (管理)成千上万的物理机器
- 存储大量的数据集
GFS存储什么?
- 作者没有说明,我们可以根据论文猜猜,可能包括如下部分
- 搜索索引和数据库
- Web上面的全部HTML文件
- Web上面的全部图片文件
- ......
文件属性:
- TB级别的数据集
- 很多文件巨大
- 作者在2003的时候建议存储100 MB大小的文件1M份, 100 TB的数据量
- 文件只支持追加方式
主要挑战:
- 因为存在很多机器,所以出错的情况很常见,假设一台机器一年出错一次,那么当存在1000台机器的时候,每天都有三台机器出现问题。
- 高性能:很多并发的读写操作,Map/Reduce工作会从GF读取数据,然后保存最后的结果,注意:保存的不是中间临时文件。
- 有效的使用网络
高层次的设计
- 定义目录、文件、命名、打开/读/写操作,但是不是符合posix标准
成百上千带有硬盘的Linux块服务器
- 存储64MB的块(an ordinary Linux file for each chunk)
- 每个块在三台服务器上面做备份
- Q: 为什么是3个备份?
- Q: 除了数据可用,三备份方案给我们带来了什么? 负载均衡热文件的读取
- Q: 为什么不把每一份文件存储在RIAD硬盘? RAID不是常用品,我们想给整台机器做容错,而不是仅仅针对存储系统。
- Q: 为什么chunk这么大?
GFS的主控服务器知道目录层次
- 对于目录而言,知道里面有哪些文件
- 对于文件而言,知道哪些数据块服务器存储了相关的64MB大小数据块
- 主控服务器在内存中保存状态信息,每个chunk在主控服务器上面只保存64bytes大小的元数据
- 主控服务器有为元数据准备的可回收数据库,可以从断电故障后快速恢复。
- 同时存在备份的主控服务器(shadow master),数据略比主控服务器服务器延迟,可以被提升为主控服务器
基本操作
- 客户端读操作:
- 向主控服务器发送文件名和偏移量
- 主控服务器回复带有相关chunk的数据块服务器集合,客户端临时缓存这些信息,然后访问最近的数据块服务器
- 客户端写操作:
- 询问主控服务器,我应该往哪里写文件
- 如果文件大小超过64MB,主控服务器也许会选择一些新的数据块服务器,one chunk server is primary it chooses order of updates and forwards to two backups
两种不同的容错计划
- 一种为了主控服务器设计
- 一种为了数据块服务器设计
主控服务器容错
- 单台主控服务器
- 客户端都是跟主控服务器交互
- 主控服务器整理全部的操作
- 长期存储有限的信息
- 命名空间(目录)
- 文件到chunk的映射
- 操作日志会改变他们(上面的命名空间和映射?)
- 操作日志存在多个备份
- 客户端进行修改操作,状态在修改数据记录到操作日志之后才返回
- 我们可以看到操作日志在多数系统中都发挥核心作用
- 操作日志在实验中发挥核心作用
- 限制操作日志的文件大小
- 为主控服务器的状态创建检查点
- 删除操作日志中检查点之前的全部操作
- 检查点复制备份
- 恢复
- 操作日志从最新的检查点进行恢复
- chunk的位置信息则通过询问数据块服务器获取
- 主控服务器单点故障
- 恢复很快,因为主控服务器的状态文件很小,也许会有很小时间的不可用
- 影子服务器,它数据落后于主控服务器,它们用log中备份的数据进行回复。服务器执行只读操作,返回的数据也许不是最新的。
- 如果主控服务器不能恢复,然后主控服务器有重新启动,系统必须避免出现两台主控服务器的出现。
- 我们将会在后面的一些课程看到强一致性的方案,同时将会更加复杂。
数据块服务器容错
- 主控服务器授予一个备份服务器契约,这个备份成为主块服务器,将确定的操作顺序。
- 客户端将数据发生给副本
- Replicas form a chain
- Chain respects network topology
- Allows fast replication
- 客户端发生写请求给主Chunk服务器
- 主Chunk服务器分配序列号
- 主Chunk服务器在本地应用修改
- 主Chunk服务器向副本发送修改数据的请求
- 主Chunk服务器接收到全部副本的ack消息之后,回复客户端
- 如果一个副本没有回复,那么客户端会重试
- 如果副本的数量少于某个值,master服务器会备份chunks,重新负载备份
chunk数据的持久化
- 有些数据因为错过了更新,所以过时了。
- 通过chunk的版本号判断数据是否不新鲜的,在发生租约前,增加chunk版本号码,将数据发送到主数据块服务器,同时在其他数据块服务器中备份,主服务器和数据块服务器长久的存储版本信息。
- 发送版本号给客户端
- 版本号帮助主控服务器和客户端判断备份是否不新鲜
并发的写/追加
- 客户端们也许会并发的同时写文件的同一个区域。
- 结果是这些写操作的混合--no guarantees
few applications do this anyway, so it is fine
- 在Unix系统上面的并发写也会导致奇怪的输出
- 很多客户端也许想并发的往一个长文件里面添加
- GFS支持原子操作,保证至少一次添加,主Chunk服务器选择记录需要添加到的文件位置,然后发送给其他副本。如果和一个副本的联系失败,那么主Chunk服务器会告诉客户端重试,如果重试成功,有些副本会出现追加两次的情况(因为这个副本追加成功两次)。当GFS要去填塞chunk的边缘时,如果追加操作跨越chunk的边缘,那么文件也可能存在空洞。
一致性模型
- 目录操作的强一致性
- Master服务器原子的修改元数据,目录操作发生在理想情况
- 如果Master服务器下线,只剩下备份服务器,这时只允许只读操作,同时返回的数据可能不新鲜。
- chunk操作的弱一致性
- 一个失败的突变使的chunk变得不一致
- 主chunk服务器更新chunk文件,但是同步给副本时失败,这时副本的数据就过时了,
- 客户端可能读到的数据不是最新的,当刷新获取新的租约的时候,客户端会获取到新的版本
- 一个失败的突变使的chunk变得不一致
- 作者主张弱一致性对app而言不是什么大问题
- 大多数文件更新操作只是追加
- 应用程序可以使用添加记录中的uid判断是否重复
- 应用程序也许只是读取到少量的数据(而不是不新鲜的数据)
- 应用程序可以使用临时文件和原子的重命名操作
- 大多数文件更新操作只是追加
性能
- 巨大的读操作总吞吐量(3个副本,striping ???)
- 125 MB/sec
- 接近网络饱和状态
- 写入不同的文件低于可能的最大值
- 作者怪网络堆栈
- chunk直接的复制操作会引起延迟
- 并发追加同一份文件
- 被服务器存在的最新的chunk所限制
总结
- GFS使用的比较重要的容错技术riz
- 操作日志、检查点
- chunk之间的主备备份(but with consistencies??)
- 我们将会在其他系统中也看到这里
- 哪些在GFS中工作很好
- 巨大的顺序读写操作
- 追加
- 巨大的吞吐量
- 数据之间的容错
- 哪些在GFS中做的不怎么好
- master服务器的容错
- 小文件(master服务器的瓶颈)
- 多个客户端并发的向同一份文件更新操作(除了追加)