本文不涉及HDFS的具体细节,只是谈谈HDFS的设计思想与实现思路,帮助更好的理解HDFS的总体架构。以下内容为原创,有描述不足或者错误的地方麻烦评论区说明一下,万分感激!
本文的组织架构以及部分内容参考了李智慧老师在极客时间的专栏《从0开始学大数据》,推荐大家有时间去看看李智慧老师的专栏,很有帮助。
从0开始学大数据
伴随着互联网的迅速发展,需要计算处理的数据量急速膨胀。在稍微大一点的互联网企业,需要计算处理的数据量常常以 PB 计。一台计算机所能调度的网络带宽(通常数百 MB)、内存容量(通常几十 GB )、磁盘大小(通常数 TB)、CPU 运算速度是不可能满足这种计算要求的。
此时就产生了这样一个需求背景:
我们需要一个文件系统,可以支持海量数据的存储与计算,并且对外提供一个统一的读写API。
要想支持海量数据的存储与计算,我们需要解决几个核心问题:
既然大数据要解决的是数以 PB 计的数据计算问题,而一般的服务器磁盘容量通常 1~2TB,那么如何存储这么大规模的数据呢?
一般磁盘的连续读写速度为几十 MB,以这样的速度,几十 PB 的数据恐怕要读写到天荒地老。
磁盘大约是计算机设备中最易损坏的硬件了,通常情况一块磁盘使用寿命大概是一年,如果磁盘损坏了,数据怎么办?
在计算机领域,实现更强的计算能力和更大规模的数据存储有两种思路,
“垂直伸缩”(scaling up),通过升级 CPU、内存、磁盘等将一台计算机变得更强大;
“水平伸缩”(scaling out),添加更多的计算机到系统中,从而实现更强大的计算能力。
显然垂直伸缩意味着更高的成本,而且垂直伸缩是有极限的,水平伸缩则没有(或者说极限远比垂直伸缩更高)。所以,我们要想解决我们的问题,只能从水平伸缩下手。
目前来看,我们只能走水平伸缩的路子,那么水平伸缩就一定可以满足需求吗?
网站实时处理通常针对单个用户的请求操作,虽然大型网站面临大量的高并发请求,但是每个用户之间的请求是独立的,只要网站的分布式系统能将不同用户的不同业务请求分配到不同的服务器上,我们在每台服务器上启动一个进程去处理这些请求,同时只要这些分布式的服务器之间耦合关系足够小,就可以通过添加更多的服务器去处理更多的用户请求及由此产生的用户数据,保障了系统的可伸缩性。
面对着PB级别的数据,程序本身可能只有KB或者MB级别。既然数据量比程序本身要庞大的多,那么将数据发送给程序是不划算的。我们可以换一种思路,将程序通过网络传输发送给数据所在的地方,这一点实际上是和水平伸缩的思路符合的。
如果说我看得比别人更远些,那是因为我站在巨人的肩膀上。 ——牛顿
上面这段话基本上每个人都听过,技术发展亦是如此。每一项流行性新技术的诞生不仅意味着一个亟待解决的技术难题,同时也是对前人技术的不断总结与创新。
RAID(独立磁盘冗余阵列)技术是将多块普通磁盘组成一个阵列,共同对外提供服务。主要是为了改善磁盘的存储容量、读写速度,增强磁盘的可用性和容错能力。
这里不探讨RAID的具体实现细节,只描述下RAID碰到我们上面说的3个问题是怎样去解决的?
毕竟实现细节可能各种各样,百花齐放,但是设计思维是共通的,是永恒的,这也是我写这篇文章的目的,从设计思维出发来帮助我们更好的理解HDFS的各种细节。
1.数据存储容量的问题:RAID 使用了 N 块磁盘构成一个存储阵列,如果使用 RAID 5,数据就可以存储在 N-1 块磁盘上,这样将存储空间扩大了 N-1 倍。
2.数据读写速度的问题。RAID 根据可以使用的磁盘数量,将待写入的数据分成多片,并发同时向多块磁盘进行写入,显然写入的速度可以得到明显提高;同理,读取速度也可以得到明显提高。
3.数据可靠性的问题。使用 RAID 10、RAID 5 或者 RAID 6 方案的时候,由于数据有冗余存储,或者存储校验信息,所以当某块磁盘损坏的时候,可以通过其他磁盘上的数据和校验数据将丢失磁盘上的数据还原。
上面这段话比较长,其实总结起来就3个关键字:
N块磁盘构成存储阵列
并发写入
冗余备份
经过上面的探讨,我们其实已经有了一点较为完善的思路,可以帮助我们去解决刚开始提到的3个问题。
1.数据存储容量的问题:多台服务器对外呈现一个统一的文件系统,综合CPU,磁盘,内存,网络带宽等,对外提供统一的读写API。
2.数据读写速度的问题:我们把数据分成多块,并发的写入系统。
3.数据可靠性的问题:我们对每个分块都进行冗余备份,一个数据块对应多个副本。
以上内容我总结成了下面的思维导图。
学习任何一门新技术,我推荐从问题出发,一步步的来解决问题。
还是回到刚开始的三个问题:
这个不难想出,分布式集群中最常见的主从架构。
客户端先和主服务器通信,获取具体的存储位置,客户端再和从服务器请求相应的数据。
更进一步,主服务器负责和客户端进行交互,从服务器来负责存储具体的数据。
从服务器启动后,从本地配置文件中获取主服务器的地址,和主服务器通信,汇报本机存储信息。
主服务器存储着元数据信息,包括文件系统树、整棵树所有的文件和目录、每个文件的块列表、块所在的节点信息等。
为了照顾到使用性和容错性,数据分别保存在内存和磁盘中。
这里也使用一个比较经典的思维——心跳机制,从服务器定时和主服务器通信,一旦超时连接,则认为从服务器挂掉了。
这里不得不提到冗余备份。主服务器一旦确定从服务器挂掉,从本地内存中获取该从服务器拥有的存储信息,然后新建一个备份。如果从服务器确定磁盘损坏,则会将该磁盘存储信息报告给主服务器,主服务器查到了相同的信息存储在哪些其他的服务器,只需要新建一个备份就行。相应的请求也会转移到备份的从服务器上面。
这实际上体现了一种常见的保障系统可用性的思维——失效转移。
主从热备,共享元数据信息,当主的挂掉后,备份服务器立马转正,从而保证系统的可用性。
1.首先我们应该能想到,可以直接将整个文件系统打成镜像,系统重启后直接加载镜像就好了;
2.但是这样会有一个问题,打一个镜像得很长时间,这段时间系统不就不可用了?
这里也有一个经典的解决方案:
我们可以将每次写入的操作都记录到日志文件里面,这样另起一个线程专门用来将日志合并到镜像里面不就好了。
3.这样还有一个问题,如果主服务器突然挂了,日志文件的数据写入没有合并到镜像文件里面,这样系统重启后,系统还是很长时间不可用怎么办?
遇到这个问题,有个常见的解决方案,就是设置检查点 ,每隔一定时间将日志文件里面的写入操作合并到镜像文件里面,为了加强系统的容错性,检查点要放到另外一台服务器上面,这台服务器专门用来负责处理将日志文件合并到镜像文件。定时的将新生成的镜像文件发送给主服务器,并且覆盖主服务器的老镜像。
当然,还有一种解决方案也比较常见,就和我们每次装系统一样,每隔一段时间,生成一份快照,注意这里的快照不同于上面的镜像。
一台的内存不够,那我加一台;两台还不够,我再加!这其实就是上面讲到的水平伸缩的思想。
又是老问题了,相信你能脱口而出:冗余备份!没错,我一台服务器上面一个数据准备几个副本不就行了,当然还有一种更好的方案:在多台主服务器使用共享存储或者使用分布式编辑日志。
数据按照固定大小进行切分,要是大小不足怎么办?大小不足也算一个块,这样管理起来不就方便啦
数据块不能太小——首先数据块对应的元数据信息都差不多,数据块太小主服务器内存就不够用了,其次,数据块太小,对于机械硬盘来讲,每个数据块都是连续的地址,切换数据块,寻址耗时太长。
数据块不能太大——太大了怎么达到并发写入的要求呢?别忘了数据分块的目的就是为了并发写入的啊
这个思路也比较常见,合并和压缩呗。
节点不能太近——一挂可能全部挂
节点也不能太远——距离越远传输时间越长,这是根据香农定理得出来的。
这个考虑到香农定理,显然越近越好,因为根据前面的结论,数据是客户端在从服务器上面下载的,所以指的是客户端和从服务器之间的距离。
这里也有一个经典的思维,既然数据量越大,网络传输越不可靠,我们就想办法把数据往小了切分 。
我们使用校验和来保障数据的完整性, 由于校验和的大小远小于数据的大小,所以校验和的可靠性要比数据高得多。
读数据——客户端读完了数据的校验和 与 主服务器内存里面的校验和比较,就知道数据读取是否成功了。
写数据——对于数据块来讲,数据往小了切,一段一段的写入,每一段都包括数据和校验值,对于每一段数据,我们每个备份节点一台一台的写,每一台写完了直接校验。
实际上HDFS就是按照上面的思路来设计的,当然上面的思路只能粗略的帮你理解HDFS的设计思想,更具体的内容以后我再写篇文章来描述。
上面每一个问题都对应了HDFS中的一个或几个知识点,具体的请看下面的思维导图。
个人博客网站:
https://www.shockang.com