HDFS基础知识(上)
大家好,好久不见,我是大圣,今天更新一篇 HDFS 基础知识的文章。话不多说,直接上大纲
简单说一下最近的安排,我会出一个 HDFS 系列的文章,让大家从了解 HDFS 基础开始,一直到熟悉 HDFS 核心源码。文章的主要系列包括以下几个部分:
1 .HDFS 基础知识
2 . HDFS 源码图解
那接下来,我们开始今天的内容。
1.HDFS 架构演变
1.1 hadoop 简介
Hadoop 到目前为止发展已经有 10 余年,版本经历了无数次的更新迭代,目前业内大家把 Hadoop 大的版本分为 Hadoop1,Hadoop2,Hadoop3 三个版本。
hadoop1 版本刚出来的时候是为了解决两个问题:一个是海量数据如何存储的问题,一个是海量数据如何计算的问题。Hadoop1 的核心设计就是 HDFS 和 MapReduce。HDFS 解决了海量数据如何存储的问题,MapReduce 解决了海量数据如何计算的问题。
HDFS 的全称:Hadoop Distributed File System
1.2 HDFS 的重要性
大数据中最重要的两个东西就是存储和计算,在存储方面现在大部分的公司都采用 HDFS 作为底层的数据存储。如果公司大数据存储经常出现问题,那么就可能导致数据不可用,或者数据丢失,这在生产环境是完全不允许的。
比如还有数据小文件问题,数据文件过大导致查询速度过慢等问题,如果这些问题经常存在,那么会导致我们的数据平台是不可用的。所以我认为公司的大数据存储是大数据中比较基础而且最重要的一环。
所以对 HDFS 还是很值得深入研究的,一方面它作为大数据的底层存储的重要性不言而喻,另一方面对 HDFS 了解比较深入,它待遇是真的好呀,如下图:
1.3 HDFS 的架构演进之路
HDFS 是 Hadoop 里面的一个组件,上面说过 Hadoop 经过了 hadoop1,Hadoop2,Hadoop3 三个大的版本迭代,所以 HDFS 也经历了 HDFS1,HDFS2,HDFS3 三个版本的迭代。
1.4 分布式文件系统
我个人的理解,其实分布式文件系统就是一个超级大型的电脑,因为分布式文件系统对于用户是无感知的,用户是不知道你这个文件系统是分布式的,他用起来就和一台电脑里面存储是一样的,只是我们在底层利用多台服务器对数据进行分片和冗余来进行存储,来达到对这个系统的水平扩展。其实不光分布式存储,分布式计算也是的。
1.5 HDFS1
1) HDFS1 架构
HDFS1 是一个主从式的架构,主节点只有一个叫 NameNode。从节点有多个叫 DataNode。
NameNode 主要管理元数据(文件目录树):
- 文件与 Block 块,Block 块 与 DataNode 主机的关系。
- NameNode 为了快速响应用户的操作请求,所以把元数据加载到了内存里面
DataNode主要工作:
- 存储数据,把上传的数据划分成为固定大小的文件块(Hadoop1,默认是 64M)
- 为了保证数据安全,每个文件块默认都有三个副本
现在我们有一个 给你一朵小红花.mp4 的文件,总共128M,如果我们要进行存储的话,首先客户端向 NameNode 发送要存储这个文件的请求,然后把这个文件分割成两个 block 块,接着在 NameNode 里面会记录这个文件被分成了几个 block 块,然后决定这几个 block 存储到哪些 DataNode 服务器上面。
这里为了数据的安全性,所以我们进行了数据冗余,就是把一个 block 块增加了 两个副本,其实分布式存储就是分区和冗余,我们这里叫分块和冗余。
2)HDFS1 的缺点
HDFS1 有两个缺点:
1)NameNode 单点故障的问题
NameNode 这个主节点进程只有一个,如果这个进程宕机挂了,会导致 HDFS 不可用。
2)NameNode 内存受限的问题
当我们的元数据原来越多的时候,我们的 NameNode 单台服务器的内存会不够用。这个时候有人说,我们可以给 NameNode 加内存,但是你再加内存,元数据还是会越来越多。再说单台机器的内存再去增加是有上限的,所以这个方案不可取。
1.6 HDFS2
HDFS2 主要解决了 HDFS1 中的两个问题,下面我们来详细说一下 HDFS2 怎么解决 HDFS1 中的这个两个问题的。
1)单点故障解决方案
在HDFS1 中 主节点 NameNode 只有一个,所以存在单点故障(SPOF)问题。
方案1:
既然 NameNode 只有一个,那么我们可以再弄一个 NameNode 就可以了,然后再客户端往第一个 NameNode 写过数据之后,这个 NameNode 再把元数据同步给另外一个 NameNode,这样不就可以了吗?如下图:
我认为这样是不行的,因为架构设计的时候要求低耦合,如果我们这样设计的话,两个 NameNode 的耦合度太高了,扩展性会非常不好。
方案2:
既然我们使用 NameNode 往另外一个 NameNode 同步元数据这样耦合度太高的话,我们可以利用第三方框架去降低这个耦合度。
但是我们要注意,在我们使用第三方框架去降低两个 NameNode 的耦合度的时候,我们要确保引入的这个第三方框架它自己本身是没有单点故障这个问题的,要不然我们的本意是为了解决 NameNode 的单点故障问题,你引入了第三方框架,而这个第三方框架本身也有单点故障的问题,这样是不合理的。
我们这里面采用的是 JournalNode 集群来降低这两个 NameNode 之间的耦合度,如下图:
这个 JournalNode 有几个特点:
1)一般是奇数台,且只要正常对外提供服务的机器大于 n /2 ,整个集群就可以正常对外提供服务
2)这个集群具有强事务性,就是我去那一台 JournalNode服务器去读取数据的时候,读取的都是最新的数据
有了这个 JournalNode 集群,就可以降低两个 NameNode 之间的耦合性,左边这个NameNode 的数据可以写入到 JournalNode 集群里面,然后右边这个 NameNode 可以直接从 JournalNode 集群里面读取数据就可以了。其实你可以把这个 JournalNode 集群理解为 Kafka 集群,就是用来解耦的。
到这里看起来似乎可以解决了单点故障问题,那我们接下来看一个这样的场景。当在凌晨三点的时候,某公司运维工程师 张三正在做美梦,突然接收到我们上面架构里面的 NameNode 突然宕机了,然后他就要赶快起来,收到把状态为 StandBy 的 NameNode 给切换成 Active。
如果这样来回搞几次,张三会崩溃的,所以上面的框架还缺一个当 状态为 Active 的 NameNode 宕机之后,状态为 StandBy 的 NameNode 可以自动顶上去;也就是故障的自动切换。如下图:
我们这里使用了 Zookeeper 来监控 NameNode 状态,你可以理解当两个 NameNode 启动的时候,它们俩都会去抢这一把分布式锁,谁抢到了,谁的状态就是 Active ,另外一个状态就是 StandBy。
这里其实还有一个要注意的点,就是如果 NameNode 进程宕机不存在了,Zookeeper 是可以感受到的,但是为什么还要引入 ZKFC 呢?这是因为有些情况 Zookeeper 不一定能感受的到 NameNode 的,比如 NameNode 发生了 分钟级别的 FullGC,发生 FullGC 的时候,只有GC 线程去运行的,我们本地的工作线程是不运行的。
此时 NameNode 就不干活了, 但是ZKFC 是可以监控的到的,因为此时 NN 的进程还是在的,ZKFC 也会把这个事情告诉给ZK 的,这个时候ZK 知道过后,就会把这把锁给另外一个 NN,这样的话就完成了主备切换。
ZKFC 它会监控我们的NameNode ,并且会把 NameNode 的监控状态报告给 Zookeeper (与Zookeeper 保持心跳)。
2)内存受限解决方案
上面说到随着业务越来越多,NameNode 的元数据越来越多就会导致 NameNode 内存不够用的问题。我们可以想,如果是我们现实中遇到这个问题了,我们该怎么解决,大家可以看下面这个例子。
李四开了一家快递店,刚开始业务不是特别多,收的快递也不多,所以只需要一个仓库就可以放下这些快递了。可是随着双十一的到来,业务越来越多,收到的快递也越来越多,这个时候一个仓库已经放不下这些快递了,这个时候如果你是李四,你会怎么办呢?最简单的办法就是一个仓库不够的话,我们再建几个仓库就行了。
其实 NameNode 内存受限也是的,李四家的快递就相当于 NameNode 中的元数据,李四家的仓库就相当于 NameNode 的内存。当李四家的快递越来越多的时候,他的做法是再建几个仓库。那么当 NameNode 内存不够用的时候,我们对应的解决方案就是再水平扩展几台 NameNode 机器,如下图的架构图:
这里要注意的是上图中互为联邦的两个 NameNode 它们存储的数据是不相同的,因为这是当一台 NameNode 水平扩展的。就像李四家的一个仓库不够放快递了,他再建一个仓库放快递,那他这两个仓库放的快递肯定不是一样的,对比到我们互为联邦的 NameNode 这里也是一样的道理。其实我们就是把 NameNode 扩展成了一个集群,像 DataNode 集群那样。
还有一个要注意的是上图中互为 HA 的两个 NameNode 所存储的数据是相同的,因为这是为了解决 NameNode 单点故障的问题。
1.7 HDFS3
HDFS3 在架构上对比 HDFS2 没有大的改变,就是 NameNode 高可用方案当中可以支持多个 NameNode,在 HDFS2 的 NameNode 高可用方案只能备份一个 NameNode,但是在 HDFS3 当中可以备份多个 NameNode。
HDFS3 还有一个改变就是引入纠删码技术。
2. HDFS 支持亿级流量的密码
我们来思考一个问题:
因为 NameNode 主节点管理了元数据,用户所有的操作请求都要操作 NameNode,大一点平台一天可能需要运行几十万、上百万的任务。我们任务在运行的时候是需要不断的创建各种目录,各种文件,删除文件等,所以一个任务就会有多个请求,这些请求都写到 NameNode 这里去更新目录树。如果请求再多一点,那么对于 NameNode 来说就是亿级的流量,NameNode 是如何支撑亿级流量的呢?
2.1 NameNode 如何管理元数据
这是一台服务器,然后上面是我们的内存,下面是我们的磁盘,我们可以认为这台服务器就是 NameNode。当我们安装了 Hadoop 软件的时候,这个 NameNode 就是这样的一个情况。如下图:
现在我们要启用 HDFS 的时候,我们第一步首先要把磁盘进行格式化,就是我们需要对这个文件系统进行格式化。比如我们使用 Linux 操作系统的时候,要启用它的文件系统的时候也是需要格式化数据盘的。
对 HDFS 文件系统进行格式化过后在磁盘上面就会产生一个 fsimage 文件,然后我们执行一个命令启动 NameNode 进程,然后就会把磁盘中的 fsimage 文件加载到内存里面。如下图:
当 fsimage 加载到内存里面以后,就会有请求不断的写到 内存的 fsimage。如果此时每天有一亿个请求写到内存的 fsimage 里面,这个时候 NameNode 是完全可以扛得住的。因为一天一个亿的请求的话,一天24小时,平均下来的话,这个 qps 也不算高的。所以在 NameNode 请求的纯内存操作是没有问题的。
但是这个架构设计是有问题的,不知道小伙伴们能不能猜得到。这是因为我们的请求如果只写 NameNode 内存的 fsimage 里面的话,当 NameNode 服务器宕机的话,这个时候内存里面的数据会丢失的,这是不可取的。所以我们的请求在写到内存的同时,还需要往磁盘里面写一份。这里往磁盘里面写并不是直接写磁盘,而是顺序写磁盘上面的一个 editlog,这样的话可以加快往磁盘写的速度。
这样的话,即使 NameNode 服务器宕机了,我们也不用怕。我们可以拿磁盘里面的 fsimage 和 多个 editlog 文件合并起来,重新加载一份新的元数据加载到内存里面就可以了。
其实请求还是会写一份到 JournalNode 集群里面的,这个我们上面说过了,这是为了保证 NameNode 的高可用,我们是通过 JournalNode 集群来同步数据到 Standby NameNode 的,所以我们这个架构还会有 JournalNode集群和 StandBy NameNode 服务器,如下图:
我们把请求往 Active NameNode 的磁盘里面写是为了元数据的安全,往 JournalNode 集群里面写是为了 NameNode 的高可用。
其实我们的 StandBy NameNode 会不断的从 JournalNode 集群去读取数据,然后每隔一定的周期会把读取过来的数据在内存里面进行 checkpoint 形成新的 fsimage 文件,然后写到 StandBy NameNode 的磁盘里面。最好再拿 StandBy NameNode 磁盘里面的 fsimage 文件去替换 Active NameNode 磁盘里面的 fsimage 文件,如下图:
那么 StandBy NameNode 为什么要周期性的进行 checkpoint 操作生成新的 fsimage 文件,然后再替换 Active NameNode 里面的 fsimage 文件呢?这是因为当我们的请求往 Active NameNode 内存写的时候,同时也会写到磁盘里面的 editlog 里面,随着请求越来越多 editlog 的个数就会越写越多。
如果这时候我们重启了 Active NameNode 的时候,我们需要从磁盘加载新的元数据到内存里面的时候,当editlog 的个数越多,我们加载的速度就越来越慢,有可能我们重启一下 Active NameNode 会加载 5 – 6 分钟的元数据,这在生成环境是完全不允许的。
如果我们使用了 StandBy NameNode 去帮我们实时合并 fsimage 文件的话,这样我们 Active NameNode 磁盘里面的 editlog 文件个数就会变的很少,这样我们启动 Active NameNode 在做数据回放的时候,效率就比较高, Active NameNode 启动就非常快。
刚开始学习 HDFS 的时候,利用 StandBy NameNode 去定期做 fsimage 文件的合并的时候。我就在想,其实 Active NameNode 自己也可以定期做 checkpoint 去合并它服务器上面的 fsimage 和 editlog,但是为什么 HDFS 架构没有这样设计呢?
后来了解到是因为如果 Active NameNode 自己去做 元数据合并的话,为了数据安全,这个时候是需要对 Active NameNode 内存里面的这个 fsimage 去加锁的,因为如果不加锁的话,这个时候我们请求还会往内存 fsimage 里面写的,而我们同时又在合并 fsimage 里面的数据,这样数据就会的准确性就会出现问题。
但是你一旦把 Active NameNode 内存里面的 fsimage 加锁的话,那么在 Active NameNode 做 checkpoint 合并元数据没有完成的时候,这把锁是不会释放的,这就会导致内存里面的 fsimage 不能接收来自外部的请求,这就意味着此时 Active NameNode 这段时间是不能对外提供服务的。
经过上面一步一步推理,我们 NameNode 元数据管理的架构图就出来了,如下图:
但是此时还有一个问题,就是请求直接写到内存 fsimage 里面,它可以支持高并发,高可用,是非常快的。是可以抗住每天亿级流量的。但是这个请求同时还会写到 Active NameNode 和 JournalNode 磁盘里面,如下图:
我们知道往磁盘里面顺序写的时候,速度还是可以的,但是往磁盘里面写是不支持高并发的,这是由于磁盘的物理特性和文件系统的限制。这个时候才是 HDFS 支持亿级流量的问题所在。
写内存的话,要想支持高并发,高性能这是非常好做的。但是往磁盘里面写,想要支持高并发和高性能这是非常困难的。大家可以考虑一下 HDFS 在这一块怎么做的,下一篇文章我将告诉大家 HDFS 在这一块怎么做的,这个设计也是 HDFS 元数据管理最精华的部分。
另外 HDFS3 架构中的纠删码机制,我在下一张也会详细说一下到底是怎么回事。
本文由博客一文多发平台 OpenWrite 发布!
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net