HDFS是一个分布式文件系统,具有良好的扩展性、容错性以及易用的API。核心思想是将文件切分成等大的数据块,以多副本的形式存储到多个节点上。HDFS采用了经典的主从软件架构,其中主服务被称为NameNode,管理文件系统的元信息,而从服务被称为DataNode,存储实际的数据块,DataNode与NameNode维护了周期性的心跳,为了防止NameNode出现单点故障,HDFS允许一个集群中存在主NameNode,并通过ZooKeeper完成Active NameNode的选举工作。HDFS提供了丰富的访问方式,用户可以通过HDFS shell,HDFS API,数据收集组件以及计算框架等存取HDFS上的文件。
在大数据场景中,存在两种解决方案:纵向扩展(scale-up)和横向扩展(scale-out)。纵向扩展利用现有的存储系统,通过不断增加存储容量来满足数据增长的需求;横向扩展则是以网络连接的节点为单位扩大存储容量(集群)。由于纵向扩展存在价格昂贵、升级困难以及总存在物理瓶颈等问题,大数据领域通常会采用横向扩展方案。横向扩展的难点在于如何构建一个分布式文件系统,解决以下这些问题。
基于现有文件系统的主从架构(Master/Slaves):给定N个网络互联的节点,每个节点上装有linux操作系统,且配有一定量的内存和硬盘,选出一个节点作为Master,记录文件的元信息,其他节点作为Slave,存储实际的文件。为了确保数据的可靠性,将每个文件保存到三个不同节点上。
当客户端(Client)需要写入一个文件时,首先与Master通信,获取文件存放节点列表,如果该文件是合法的(比如不存在重名文件等),则Master根据一定的负载均衡策略将三个节点位置信息发回客户端,这时客户端与这三个Slave节点建立网络连接,将文件写入对应的三个节点,读文件过程类似。
该系统从一定程度上能够解决分布式存储问题,但存在以下两个不足:
1)难以负载均衡:该分布式文件系统以文件为单位存储数据。由于用户的文件大小往往是不统一的,难以保证每个节点上的存储负载是均衡的。2)难以并行处理:一个好的分布式文件系统不仅能够进行可靠的数据存储,还应考虑如何上层计算引擎高效的分析。由于数据是以文件为单位存储的,当多个分布在不同节点上的任务并行读取一个文件时,会使得存储文件的节点出口网络带宽成为瓶颈,从而制约上层计算框架的并行处理效率。
为了解决文件级别分布式系统存在的不足,块级别的分布式文件系统出现了,这类系统核心思想时将文件分成等大的数据块(128MB),并以数据块为单位存储到不同节点上,进而解决文件级别的分布式系统存在的负载均衡和并行处理问题。
HDFS采用了主从架构,主节点被称为NameNode,只有一个,管理元信息和所有从节点,从节点称为DataNode,通常存在多个存储实际的数据块,HDFS各组件功能如下:
1.NameNode
NameNode是HDFS集群管理者,负责管理文件系统元信息和所有DataNode。
1)管理元信息:NameNode维护着整个文件系统的目录树,各个数据块信息等。2) 管理DataNode:DataNode周期性向NameNode汇报心跳以表明自己活着,一旦NameNode发现某个DataNode出现故障,会在其他存活DataNode上重构丢失的数据块。
一个HDFS集群中只存在一个对外服务的NameNode,称为Active NameNode,为了防止单个NameNode出现故障后导致整个集群不可用,用户可启动一个备用NameNode,称为Standby NameNode,为了实现NameNode HA(High Availability,高可用),需解决好两者的切换和状态同步问题。1)主/备切换:HDFS提供了手动方式和自动方式完成主备NameNode切换,手动方式是通过命令显示修改NameNode角色完成的,通常用于NameNode滚动升级。自动模式是通过ZooKeeper实现的,可在主NamNode不可用时,自动将备用NameNode提升为主NameNode,以保证HDFS不间断对外提供服务。
2)状态同步:主/备NameNode并不是通过强一致协议保证状态一致的,而是通过第三方的共享存储系统。主NameNode将EditLog(修改日志,比如创建和修改文件)写入共享存储系统,备用NameNode则从共享存储系统中读取这些修改日志,并重新执行这些操作,以保证与主NameNode的内存信息一致。
目前HDFS支持两种共享存储系统:NFS(Network File System)和QJM(Quorum Journal Manager),其中QJM是HDFS内置的高可用日志存取系统,其基本原理是用2N+1台JournalNode存储EditLog,每次写数据操作大多数(大于等于N+1)返回成功确认即认为该次写成功,该算法所能容忍的是最多有N台机器挂掉,QJM能够构建在普通商用机器之上,比NFS更加廉价,因此受众更广。
2.DataNode
DataNode存储实际的数据块,并周期性通过心跳向NameNode汇报自己的状态信息。
3.Client
用户通过客户端与NameNode和DataNode交互,完成HDFS管理(比如服务启动与停止)和数据读写等操作。文件的分块操作也是在客户端完成的。当向HDFS写入文件时,客户端首先将文件切分成等大的数据块(默认一个数据块大小为128MB),之后从NameNode上领取三个DataNode地址,并在它们之间建立数据流水线,进而将数据块流式写入这些节点。
NameNode Federation机制:
随着数据块和访问量的增加,单个NameNode会成为制约HDFS扩展性的瓶颈,为了解决该问题,HDFS提供了NameNode Federation机制,允许一个集群中存在多个对外服务的NameNode,各自管理目录树的一部分(对目录水平分片)。需要注意的是,在NameNode Federation中,每个主NameNode均存在单点故障问题,需为之分配一个备用NameNode。为了向用户提供统一的目录命名空间,HDFS在NameNode Federation之上封装了一层文件系统视图ViewFs,可将一个统一的目录命名空间映射到多个NameNode上。
1.容错性设计
HDFS内置了良好的容错性设计策略,以降低各种故障情况下数据丢失的可能性。
NameNode故障:NameNode内存中记录了文件系统的元信息,这些元信息一旦丢失,将导致整个文件系统数据不可用。HDFS允许为每个Active NameNode分配一个Standby NameNode,以防止单个NameNode宕机后导致元信息丢失和整个集群不可访问。
DataNode故障:每个DataNode保存了实际的数据块,这些数据块在其他DataNode上存在相同的副本。DataNode能通过心跳机制向NameNode汇报状态信息,当某个DataNode宕机后,NameNode可在其他节点上重构该DataNode上的数据块,以保证每个文件的副本数在正常水平线上。
数据块校验:DataNode保存数据时,会同时生成一个校验码。当存取数据块时,如果发现校验码不一致,则认为该数据块已经损坏,NameNode会通过其他节点上的正常副本重构受损的数据块。
2.副本放置策略
数据块副本放置策略直接决定了每个数据块多个副本存放节点的选择,在保证写性能较优的情况下,尽可能提高数据的可靠性。
一个集群由多个机架构成,每个机架由16~64个物理节点组成,机架内部的节点是通过内部交换机通信的,机架之间的节点是通过外部节点通信的。HDFS默认采用的三副本放置策略:
客户端与DataNode同节点:上层计算框架处理HDFS数据时,每个任务实际上就是一个客户端,任务运行在与DataNode相同的计算节点上(HDFS和YARN同节点部署)。三副本放置策略如下:第一个副本写到同节点的DataNode上,另外两个副本写到另一个相同机架的不同DataNode上。
客户端与DataNode不同节点:当HDFS之外的应用程序向HDFS写数据时,HDFS会随机选择一个DataNode作为第一个副本放置节点,其他两个副本写到另一个相同机架的不同DataNode上。
3.集中式缓存管理
HDFS允许用户将一部分目录或文件缓存在off-heap内存中,以加速对这些数据的访问效率,该机制被称为集中式缓存管理,引入带来了许多显著的优势:
1)提高集群的内存利用率。当使用操作系统的缓存时,对一个数据块的重复读会导致所有副本都会被放到缓冲区当中,造成内存浪费;当使用集中式缓存时,用户可以指定n个副本中的m个被缓存,可以节约n-m的内存。
2)防止那些被频繁使用的数据从内存中清除。
3)提高数据读取效率:
1.HDFS shell HDFS提供了两类shell命令:用户命令和管理员命令。
1)用户命令 HDFS提供了文件操作命令dfs、文件一致性检查命令fsck、分布式文件复制命令distcp.
在HDFS创建目录/user/input
bin/hdfs dfs -mkdir -p /user/input
2)管理员命令
管理员命令主要是针对服务生命周期管理的,比如启动/关闭NameNode/DataNode等
sbin/hadoop-daemon.sh start namenode
sbin/hadoop-daemon.sh stop namenode
2.HDFS API HDFS对外提供了丰富的编程API,允许用户使用Java、python等语言(Thrift)编写应用程序访问HDFS。
3.数据收集组件 Sqoop:Sqoop允许用户指定数据写入HDFS的目录、文件格式(支持Text和SequenceFile两种格式)、压缩方式等
4.计算引擎 1)上层计算框架可通过InputFormat和OutputFormat两个可编程组件访问HDFS上存放的文件,其中InputFormat能够解析输入文件,将之逻辑上划分成多个可并行处理的InputSplit,并进一步将每个InputSplit解析成一系列key/value对;OutputFormat可将数据以指定的格式写入输出文件。Hadoop为常见的数据存储格式分别设计了InputFormat和OutputFormat实现,以方便MapReduce、Spark等上层计算框架使用,比如Text文件的实现是TextInputFormat和TextOutputFormat,SequenceFile的实现是SequenceFileInputFormat和SequenceFileOutputFormat。
2)另一种访问HDFS数据的方式是SQL、HIVE、Impala等查询引擎均允许用户直接使用SQL访问HDFS中存储的文件。
1.hdfs-site.xml
<configuration>
<!--配置分片的数量-->
<property>
<name>dfs.replication</name>
<value>3</value>
<description>分片数量</description>
</property>
<!--指定hdfs的nameservice为ns1,需要和core-site.xml中的保持一致 -->
<property>
<name>dfs.nameservices</name>
<value>yfcluster</value>
<description>为namenode集群定义一个services name</description>
</property>
<!-- ns1下面有两个NameNode,分别是nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.yfcluster</name>
<value>nn1,nn2</value>
<description>nameservice包含哪些namenode,为各个namenode起名</description>
</property>
<property>
<name>dfs.namenode.rpc-address.yfcluster.nn1</name>
<value>kafka1:8020</value>
<description> 名为nn1的namenode 的rpc地址和端口号,rpc用来和datanode通讯</description>
</property>
<property>
<name>dfs.namenode.rpc-address.yfcluster.nn2</name>
<value>kafka2:8020</value>
<description> 名为nn2的namenode 的rpc地址和端口号,rpc用来和datanode通讯</description>
</property>
<property>
<name>dfs.namenode.http-address.yfcluster.nn1</name>
<value>kafka1:50070</value>
<description> 名为nn1的namenode 的http地址和端口号,web客户端</description>
</property>
<property>
<name>dfs.namenode.http-address.yfcluster.nn2</name>
<value>kafka2:50070</value>
<description> 名为nn2的namenode 的http地址和端口号,web客户端</description>
</property>
<!--默认情况下,hadoop 在NameNode所在机器里运行SecondaryNameNode,http监听端口是50090,在浏览器输入NameNode的地址和50090端口号,为了安全分别放到两台机器上,在浏览器输入NameNode的地址和50090端口号,就可看到其信息-->
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>kafka2:50090</value>
<description>命名空间和事务在本地文件系统永久存储的路径</description>
</property>
<!-- 指定NameNode的元数据在JournalNode上的存放位置,这样nameNode2可以从Jn集群中获得最新的nameNode信息 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://kafka1:8485;kafka2:8485;kafka3:8485/yfcluster</value>
<description>namenode间用于共享编辑日志的journal节点列表</description>
</property>
<!-- 指定journal的存放位置 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/moudle/hadoop3/hdfs/journaldata</value>
<description>journalnode上用于存放edits日志的目录</description>
</property>
<!--配置nameNode数据存放位置-->
<property>
<name>dfs.namenode.name.dir</name>
<value>/moudle/hadoop3/hdfs/name</value>
<description>在本地文件系统所在的NameNode的存储空间和持续化处理日志</description>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>/moudle/hadoop3/hdfs/data</value>
<description>数据节点的块本地存放目录</description>
</property>
<!-- HDFS的HA功能的防脑裂方法。可以是内建的方法(例如shell和sshfence)或者用户定义的方法。
建议使用sshfence(hadoop:9922),括号内的是用户名和端口,注意,这需要NN的2台机器之间能够免密码登陆
fences是防止脑裂的方法,保证NN中仅一个是Active的,如果2者都是Active的,新的会把旧的强制Kill。-->
<!-- 配置隔离机制 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
<description>配置隔离机制</description>
</property>
<!-- ssh 登录秘钥坐在位置 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
<description>密钥认证文件</description>
</property>
<!-- nameNode故障时自动切换-->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
<description>是否开启自动故障转移。建议开启,true</description>
</property>
<!-- 配置切换实现方式-->
<property>
<name>dfs.client.failover.proxy.provider.yfcluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
<description>客户端连接可用状态的NameNode所用的代理类</description>
</property>
<!--namenode的hdfs-site.xml是必须将dfs.webhdfs.enabled属性设置为true,否则就不能使用webhdfs的LISTSTATUS、LISTFILESTATUS等需要列出文件、文件夹状态的命令,因为这些信息都是由namenode来保存的。-->
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<!-- JournalNode的HTTP地址和端口。-->
<name>dfs.journalnode.http-address</name>
<value>0.0.0.0:8480</value>
</property>
<property>
<!-- JournalNode RPC服务地址和端口-->
<name>dfs.journalnode.rpc-address</name>
<value>0.0.0.0:8485</value>
</property>
<!-- 配置hdfs的操作权限,false表示任何人都有权限 -->
<property>
<!-- 是否在HDFS中开启权限检查。为了避免权限检查可以设置为false或使用命令hadoop fs -chmod -R 777 /-->
<name>dfs.permissions.enabled</name>
<value>true</value>
</property>
<!-- 默认情况下ACLs是关闭的,想要开启此功能需要在hdfs-site.xml的配置项里找到dfs.namenode.acls.enabled把属性设为true才可以。文件系统提供更精细化的权限控制。/-->
<property>
<!-- 是否在hdfs开启acl,默认为false-->
<name>dfs.namenode.acls.enabled</name>
<value>true</value>
</property>
<!-- 默认情况下,用户在HDFS上创建的文件和目录的权限是rwxr-xr-x,即732,x表示有ls权限。032表示屏蔽位,默认权限是777-屏蔽位对应的数字,因此,777-032=745, 745是权限位 /-->
<property>
<name>fs.permissions.umask-mode</name>
<value>032</value>
</property>
</configuration>
2.core-site.xml
<!-- 用来指定hdfs的namenode名称-->
<property>
<name>fs.defaultFS</name>
<value>hdfs://yfcluster</value>
</property>
<!-- 用来指定hadoop运行时文件产生目录-->
<property>
<name>hadoop.tmp.dir</name>
<value>/moudle/hadoop3/tmp</value>
</property>
<!-- 指定zookeeper地址-->
<property>
<name>ha.zookeeper.quorum</name>
<value>kafka2:2181,kafka3:2181</value>
</property>
<!-- 当前用户全设置成root -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
<!-- 不开启权限检查 -->
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
</configuration>
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有