1.概述
1.1.分布式文件系统在大数据中的基石地位
分布式文件系统是整个大数据技术的基础,是大数据技术栈的核心组件,其解决了海量数据的管理问题,可以说没有分布式文件系统就没有大数据技术。其中作为GFS,google file system,是一个大型的分布式存储系统,GFS的架构是分布式文件系统最经典的架构,目前市面上很多分布式存储系统都是参照的GFS来实现的,比如:HDFS。万变不离其宗,可以说弄懂GFS,体会其思想,就掌握了当前市面上的所有的分布式文件系统。
GFS最先出现在03年谷歌发表的论文中,该论文很短,很像一篇设计文档,其中详细论述了谷歌如何设计以及落地实现自己的分布式文件系统的。该论文直接在网上搜索就能搜到。
1.1.谷歌当初面对的问题
GFS之所以出现是因为Google当时的业务确实碰见了需要进行海量数据存储的需求,当时谷歌设计GFS时面对的需求可以概括为:
- 数据量大,数据格式复杂,有大文件也有小文件。
- 运行在普通机器上,失效是常态。
- 对吞吐量要求很高,实时性要求不高。
针对以上需求,GFS给出的一整套解决办法合起来就是GFS。
1.2.谷歌如何解决这些问题的
1.数据量大,数据格式复杂,有大文件也有小文件
数据量大,一台机器装不下,必须用,布式集群来装,集群中每台机器上存放的数据量都很大。这就意味着什么?
首先一份文件最好是被分成几部分放在不同节点上的,这样可以并发的去读,能拉高吞吐量。
其次单节点上属于同一份文件的数据最好是连续存放的,这样不用不需要在进行IO的时候需要跳几下才能拼凑齐数据。
也就是说用来存储大数据的分布式文件系统最好是顺序读写的,而不是随机读写的。
这里扩展的聊一下随机读写和顺序读写的概念:
顺序读写 (Sequential Read/Write): 顺序写,是指数据连续地写入到文件中,通常是在磁盘上相邻的存储区域,形成连续的数据块。顺序读,是指按照文件内数据存储的物理顺序连续地读取数据块。 顺序读写模式下,磁头不需要频繁移动,减少了寻道时间,比起随机读写来说速度要快。
随机读写 (Random Read/Write): 随机写,是指数据以非连续的方式写入文件的不同位置,每次写入操作可能涉及到磁头在磁盘上的大幅度跳跃。随机读:是指不连续地从文件的不同位置读取数据块,每个读取请求可能会跳转到文件内的任意位置。随机读写速度要远低于顺序读写。
最后谷歌总结之后在文件读写上给出了最后的答案是:
为了保证性能,只支持顺序读写,也就是说在数据操作上最多就支持读写操作以及追加操作,不支持修改数据这一类随机读写操作。
GFS是如何实现顺序读写的?
答:分片
将存储空间分为一个个大小固定的片,一个文件会被分为一个或者多个片,在逻辑上HDFS中一个块中存放的一定是一个文件,不可能一个片中存放着来自两个文件的内容。这样在块中文件数据一定是顺序读写的。当要访问一个完整的文件的时候,我们只需要去各个节点上将属于它的块全部找出来即可。集群中会有地方统一记录哪个文件分为了哪些片存在哪些节点上。
2.运行在普通机器上,失效是常态
这其实就是容错问题,由于运行在普通机器上,机器的数量也多,因此结点失效是常态,所以将分片再进行复制,存储到不同的结点上去。也就是一个分片会存在多个副本存在不同的节点上。
当然分片会带来一致性问题:
一致性问题是复制引出的问题,要进行切片的复制就会牵扯到一致性问题,如何保证分布式存储的所有切片副本是一致的这是个问题。但是“强一致性”过于拉低集群整体性能影响吞吐量,因此GFS采用了“弱一致性”。也是由GFS第一次提出了“弱一致性”是可以接受的,这有悖于当初学术界的主流观点,当时学术界的主流观点是系统必须呈现出良好的状态,即具有“强一致性”。
GFS,google file system,是一个大型的分布式存储系统,GFS的架构是分布式文件系统最经典的架构,目前市面上很多分布式存储系统都是参照的GFS来实现的,比如:HDFS。
总结一下,GFS在数据读写上采用的机制:
一个文件分为多个片,为了容错一个片被分为多个副本,读写中采用最终一致性。
以上其实就是整个GFS设计的理论核心,接下来就来看一下落地的实现是什么样子的。
2.系统架构
一个GFS集群包含一个Master节点、多个Chunk Server服务器,多个Client。
Chunk Server:
存储文件时,GFS将文件分割成固定大小(64MB)的Chunk,在Chunk创建时,Master服务器会给每个Chunk分配一个不变的、全球唯一的64位标识——Handle句柄,Chunk Server把Chunk以普通Linux文件的形式保存在本地硬盘上,并且根据指定的Handle句柄和字节范围来读写数据。为了避免节点异常带来的数据损坏,GFS把每个Chunk以副本Replica的方式冗余备份到不同的Chunk Server服务器上。默认使用3副本策略。
Master负责维护整个集群的元数据,Master节点通过心跳信息周期性地跟每一个Chunk Server服务器通信,发送指令到各个Chunk Server服务器,并接收各个Chunk Server服务器的状态信息。
Master
master上有两张重要的表:
文件名到存储该文件的所有chunk handle(单个chunk)之间的映射。
存放每个chunk handle(由于复制的关系同一个chunk handle存在多份,存在不同的服务器上)的chunk server之间的列表,以及chunk server的优先级(从哪个里面优先读)、chunk handle的版本号、chunk handle的过期时间。
master上的这些数据都存放在内存中,为了方式宕机丢失数据,master上会有log,log存放在磁盘中,log种详细记录了数据操作,master重启时会加载磁盘中的log,通过重写一遍日志中的操作来加载数据。
Client
Client客户端代码以库的形式被链接到客户程序中,客户端代码实现了GFS文件系统的API接口函数、应用程序与Master节点和Chunk Server服务器通信、以及对数据进行读写操作。Client客户端与Master节点的通信只是获取元数据,所有的数据操作都是Client客户端直接跟Chunk Server服务器进行交互的,如上图所示,数据流和控制流是分开的,这样就避免了Master节点成为性能瓶颈
3.读操作
1.客户端将文件名和offset(偏移量)发给master
2.偏移量/64MB就可以知道是在哪个chunk里面,然后找到该chunk handle对应的chunk server列表
3.去primary chunk server读数据。读数据的时候会验证primary chunk server和master上存储的该chunk handle的版本号是否一致,如果不一致则去其余备份中进行读,如果所有备份中的版本号都和master上该chunk handle的版本号不一致,说明全局数据都不是正确的,直接返回读取失败。当然这里还有一个短期的cache策略,会将读到的数据短期缓存在cache中以应对短时间内多次读重复数据的情况。
4.写操作
- 客户端向 Master 询问目前哪个Chunk Server持有该Chunk的Lease
- Master 向客户端返回Primary Replica和其他Replica的位置
- 客户端将数据推送到所有的Replica上。Chunk Server会把这些数据保存在缓冲区中,等待所有 Replica 都接收到数据。
- 客户端发送写请求给 Primary,Primary 为来自各个客户端的修改操作选定执行序列号,并按顺序地应用于其本地存储的数据。
- Primary 将写请求转发给其他 Secondary Replica,Replica 们按照相同的顺序应用这些修改
- Secondary Replica 响应 Primary,示意自己已经完成操作。
- Primary 响应客户端,并返回该过程中发生的错误
为什么需要primary来通知其它结点一起写:
由于是在并发的工作环境下,会有多个client向各个chunk server发起写请求,请求到达各个chunk server的顺序不一定是相同的,如果直接由client通知让各个chunk server写,容易出现乱序。
chunk server A上的写入顺序是client A 、client B,chunk server B上写入的顺序是client B、client A。
通过primary来通知各个chunk server写,可以控制全局顺序都是一致的。
5.追加操作
说起追加操作就要先说一下GFS的顺序写问题:
GFS中所谓的顺序写是指一个chunk中的数据是顺序写的,但是chunk不一定是顺序的,比如一个视频它就可能被分成几个chunk来存储,这几个chunk不一定是连续存储的。当一个文件要多个chunk才能装下,那么这些chunk的顺序是由master来进行登记维护的。这样追加写操作就比较容易实现了,只要在存原有文件结尾的chunk中进行追加就行了,如果追加的内容还需要增加chunk才能装下,那么直接增加chunk然后将顺序关系上报给master即可。
下面是追加的具体过程:
追加是写操作里面的一种特殊情况,和新写不同的点在于,追加需要先定位到起始位置。
追加写分两种情况:
无primary
找master获取文件的最后一个chunk,继续在master去查找该chunk handle的chunk server列表,然后根据列表去所有chunk server里随机拿一个version和master上记录的最新version是一致的chunk server将其选为primary chunk server,更新version,再更新primary chunk server上的数据。
如果找不到version对的chunk server只能说明所有chunk server都和master失联已久或者除了其他状况,没有一个chunk server上的数据是对的,这时候拒绝写入即可。
有primary
找master获取文件的最后一个chunk,继续在master查找primary chunk server,比对primary chunk server上的version,一致则更新version,更新primary chunk server上的数据。不一致则接着向下找一个version一致的chunk server将其重新选举为primary chunk server,然后更新version、写数据。
如果一直找不到version匹配的chunk server,这时候拒绝写入即可。
6.数据一致性
由于GFS是分布式的原因,在文件的某一部分被修改后,它可能进入以下三种状态的其中之一:
客户端读取不同的 Replica 时可能会读取到不同的内容,那这部分文件是不一致的(Inconsistent)。
所有客户端无论读取哪个 Replica 都会读取到相同的内容,那这部分文件就是一致的(Consistent)。
所有客户端都能看到上一次修改的所有完整内容,且这部分文件是一致的,那么我们说这部分文件是确定的(Defined)。
也就是说,在一致性和强度上,Defined>Consistent>Inconsistent。