hdfs基础知识整理

(1)HDFS为什么会块那么大?

其目的是减少寻址的开销

(2)HDFS的块抽象带来的好处

1.文件中所有的块并不需要存储在同一个磁盘中,因此他可以利用任意一个磁盘进行存储,一个大文件就可以拆分成很多个小文件存放在不同的磁盘中。
2.大大简化了存储子系统的设计,很容易就可以计算出块的个数,元数据大小不一不方便fsImage的管理。
3.如果一个块不可用了,马上可以从相关的文件中复制过去,对用户是透明的,文件马上会回到正常数量。

(3)块缓存

通常datanode从一个磁盘中访问文件,对于访问比较频繁的数据会被显示存储在datanode的缓存中,一个块仅在一个datanode的内存中。用户或应用通过在缓存池(cache pool)中增加一个cache directive来告诉namenode需要缓存的哪些文件及缓存多久,缓存池是一个用于管理缓存权限和资源使用的管理型分组/

(4)联邦HDFS

一个namenode可能管理/user下的所有文件,而另一个namenode可能管理/share写的所有文件(/user,/share都是namenode配置文件目录),多个namenode共同管理HDFS,某一个namenode失效也不会影响其他namenode。

(5)HDFS的高可用性

如果namenode失效了,可能导致所有的客户端,包括,mapreduce作业都无法正常读写或举例文件。而且系统恢复时间太长也会影响到日常维护。
Hadoop2针对以上问题增加了HDFS的高可用性的支持。

(1)配置备用的namenode,当活动的namenode失效时,备用的namenode会自动接管并开始服务,不会有明显的的中断。
(2)目前有两种高可用性共享存储:NFS过滤器或群体日志管理器。
(3)QJM(quorum journal manager)是一个专用的HDFS实现,为提供一个高可用的编辑器而设计,被推荐用于大多数HDFS部署中。QJM以一组日志节点的形式运行,每一次编辑必须写入多数日志节点。典型的,有三个journal节点,所以系统能够忍受其中任何一个丢失。这种安排与zookeeper的工作方式类似,当然必须认识到,QJM的实现并没有使用Zookeeper。

HDFS的故障切换与规避

(1)系统中有一个称之为故障转移控制器(failover controller)的新实体,管理着将活动namenode转移为备用namenode的转移过程。默认的是一种使用了zookeeper来确保有且仅有一个活动的namenode。
(2)在网速非常慢,或者网络被分割的情况下,同样也可能激发故障转移,但是先前的活动的namenode依旧是活动的namenode,高可用实现做了更进一步的优化,以确保先前活动的namenode不会执行危害系统并导致奔溃的操作,该方法称之为“规避”。

QJM与NFS过滤器的区别

QJM仅允许一个namenode像编辑日志写入数据,然而,对于先前的活动namenode而言,仍有可能响应并处理客户过时的请求,因此,设置一个SSH规避命令来杀死namenode的进程是一个好主意。当使用NFS过滤器实现共享编辑日志时,由于不可能同一时间只允许一个namenode写入数据(这也是为什么推荐QJM的原因),因此需要更有力的规避方法。规避机制包括:撤销namenode访问共享存储目录的权限等。

(6)Hadoop文件系统

(1)hadoop中的文件系统:
在这里插入图片描述
在这里插入图片描述

(2)非java程序访问HDFS
Hadoop以java API的形式提供文件系统的访问接口,非java开发的应用访问非常不方便。由WebHDFS协议提供了HTTP REST API接口,使得其他语言可以与HDFS交互。

注:HTTP接口比原生的Java客户端要慢,所以不到万不得已,尽量不要用它来传输特大数据。

HTTP访问HDFS的两种方法:直接使用HDFS的守护进程服务与来自HTTP的请求;通过代理的方式实现访问,客户端通常使用DistributedFileSystem API访问HDFS。这两者都使用了WebHDFS协议。在这里插入图片描述
第一种情况,namenode和datanode内嵌的web服务器作为WebHDFS的端点运行(dfs.webhdfs.enabled设置为true,WebHDFS默认是启动的)文件元数据由namenode管理。

第二种情况依靠独立的代理服务器通过HTTP访问HDFS。通常情况下,代理服务器,是现在不同数据中心部署的Hadoop集群之间的数据传输,或从外部网络访问云端运行的Hadoop集群。

HttpFS代理提供和WebHDFS相同的HTTP(和HTTPS)接口,客户端可以通过webhdfsURI访问这两个接口类。HttpFS代理启动独立于namenode和datanode的守护进程,使用httpfs.sh脚本,默认在一个不同的端口上监听(端口号14000)。

(7)HDFS的API之FSDataInputStream读取数据

(1)FSDataInputstream继承了java.io.DataInputStream,支持随机访问,可以从任意的位置读取数据。调用seek()来定位大于文件长度会引发IOException,与java.io.DataInputStream中的skip()不同,seek()可以移到文件中的任意一个绝对位置,skip()则只能相当于当前位置到另一个位置(相对位置)。
在这里插入图片描述

(2)FSDataInputStream类也实现了PositionedReadable接口,从一个指定偏移量处读取文件的一部分:

public interface PositionedReadable {

    int read(long var1, byte[] var3, int var4, int var5) throws IOException;

    void readFully(long var1, byte[] var3, int var4, int var5) throws IOException;

    void readFully(long var1, byte[] var3) throws IOException;
}

read()方法从文件的指定position处读取至多为length字节的数据并缓存至buffer的指定偏移量offset。返回值实际上是读到的字节数。(返回值可能小于指定的length)
length长度的字节数数据读取到buffer’中(或在只接受buffer字节数组的版本中,读取buffer.length长度字节数据),除非已经读到了文件末尾,这种情况下将抛出EOFException异常。

注意:seek()方法是一个相对高开销的操作,需要慎重使用,建议用流数据来构建应用的访问模式(比如mapreduce),而非执行大量seek()方法。

(8)HDFS的API之FileSystem写入数据

(1)最简单的方法就是指定一个Path,返回一个用于写入数据的流:

public FSDataOutputStream create(Path f) throws IOException

(2)重载方法Progressable用于传递回调接口,可以把数据写入datanode的进度通知应用,这个展示进度对mapreduce相当重要

public interface Progressable {
    void progress();
}

(3)另一个新建文件的方法就是append()方法在现有的文件末尾追加数据:

public FSDataOutputStream append(Path f) throws IOException

(9)文件模式

大量日志文件中,可能用一个表达式来获取所有的文件(类似于正则表达式),该操作称之为“通配”。FileSystem中的方法:

public FileStatus[] globStatus(Path pathPattern) throws IOException 

在这里插入图片描述
例子如下:
在这里插入图片描述
FileSystem中的listStatus()和globStatus()方法提供了可选的PathFilter对象,globStatus会匹配Path下的文件,PathFilter会过滤掉不想要的文件:

 public FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws IOException

实例PathFilter过滤文件:
在这里插入图片描述

(10)文件读取

读取数据时,如果DFSInputStream在于datanode通信时遇到错误,会尝试从这个块的另一个最近的邻近datanode读取数据。他会记住那个故障datanode,以保证以后不会反复读取该节点上后续的块。DFSInputStream也会通过校验和确认从datanode发来的数据是否完整。如果发现有损坏的块,DFSInputStream会试图从其他的datanode读取器副本,也会将损坏的块通知给namenode。这个设计的重点是,每次客户端可以直接连到datanode,且namenode会告知客户端最佳的datanode。

那么上面说的“彼此临近”到底如何解释?
在这里插入图片描述

在这里插入图片描述

(11)文件写入

写入时,DFSOutputStream将他分成一个个的数据包,并写入内部队列,称之为“数据队列”(data queue)。DataStreamer处理数据队列,如果副本数为3,那么它就会将数据流式分到3个管道中datanode。同时,DFSOutputStream也维护着一个内部数据队列来等待datanode收到确认回执,称为“确认队列”。

如果在写操作的过程红,有任何的datanode发生故障

会先关闭首先建立的管道,确认队列中所有数据包都添加回数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。为存储在另一个正常的datanode的当前数据块制定一个新的标识,并将该标识传给namenode,以便故障datanode在恢复以后删除存储的部分数据块。从管道中删除故障的datanode,基于两个正常的datanode构建一个新管线。余下的数据写入管线中正常的datanode。

在一个块被写入的期间可能有多个datanode同时发生故障,但非常少见,只要写入了dfs.namenode.replication.min的复本数(默认为1),写操作就会成功!并且这个块可以在集群中异步复制,直到达到目标副本数为止(dfs.replication的默认值为3)。

写入的文件会产生很多副本,到底每个副本放在哪个datanode上?

Hadoop默认的策略是:
(1)在运行客户端的节点上存放第一个副本(如果客户端在集群之外,就随机选择一个节点,不过会尽量避免存储太满或太忙的节点)
(2)第二个复本放在与第一个不同且随机另外选择的机架中节点(离架)
(3)第三个复本与第二个复本在同一个机架上的随机其他节点上。
(4)其他复本放在集群中随机选择的节点上,不过系统会尽量避免在同一个的机架上放太多复本。

数据写入的一致性(一致模型):

(1)刚写入的内容并不能保证立即就可以被所有的reader看到,因为数据还没有刷新缓存并存储。
(2)当前正在写入的块对其他reader是不可见的,其他的reader来读取时,数据长度为0。当他写完了当前块时,就可以被其他的reader看到了。(以块为单位)
(3)HDFS提供了一种强行将所有缓存写入到datanode中的手段,即对FSDataOutputStream调用hflush()方法。当hflush()方法返回成功以后,所有到达的数据全部被写入管道,并对所有的新reader可见。

Path p = new Path("p");
FSDataOutputStream out = fs.create(p);
out.write("content".getBytes());
out.hflush();
//assertThat是Junit4中的一个方法,用来判断前后的数据是否相等  
assertThat(fs.getFileStatus(p).getLen(),is(((long)"content".getBytes())));  

注意:hflush()不保证datanode将所有的数据写入到磁盘,仅确保数据在datanode的内存中(如果出现立即断电,数据丢失),为确保数据写入到了磁盘上,可以使用hsync()代替。

out.close()中其实隐含了hflush();

(4)hsync()操作类似于Java API数据写入本地文件,我们可以利用刷新数据流且同步之后看到文件内容一样。

FileOutputStream out = new FileOutputStream("1");
out.write("content".getBytes());
out.flush();//刷新到内存
out.getFD().sync();//同步到磁盘

注:hflush()的调用可以减少数据的丢失的可能性,但是也会增加额外的开销(hsync()开销更大)。所以在数据的鲁棒性和吞吐量之间会有所取舍,将hflush()的调用保持在一个合适的频率。

通过distcp并行复制

为了实现多线程对一组文件的并行处理,Hadoop提供了一个distcp,该程序可以并行从hadoop文件系统中复制大量数据,也可以将大量的数据复制到hadoop中。

将file1中的文件复制到file2中:

% hadoop distcp file1 file2

关键字-overwrite强制覆盖原有文件

如果文件file1中的内容修改,我们可以用命令同步到file2中:

% hadoop distcp -updata file1 file2

(1)distcp是作为一个Mapreduce作业来实现的,该复制公国是通过集群中并行运行的map完成的。这里没有reduce。
(2)默认情况下,将近20个map被使用,但是可以通过distcp指定的-m来修改map的数量。
(3)关于distcp的一个常见的使用实例是在两个HDFS集群间传送数据,例如,以下命令在第二个集群上为第一个集群/foo目录创建一个备份:

hadoop distcp -updata -delete -p hdfs://namenode1/foo hadfs://namenode2/foo

-delete选项使得distcp可以删除目标路径中任意没在原路径中出现的文件或目录,-p表示文件的状态属性(如:权限、块大小、复本数)被保留。
(4)如果两个集群运行的是HDFS的不兼容版本,你可以将webhdfs协议用于他们之间的distcp:

hadoop distcp webhdfs://namenode1:50070/foo webhdfs://namenode2:50070/foo

批注:另一个变种是使用HttpFs代理作为distcp源或者目标,这样的优点是可以设置防火墙和控制带宽。

注意:如果想保持HDFS的负载均衡,最好不要让distcp用-m指定1,这样他的任务由一个map执行,对该map所在的datanode影响较大,多个map可以缓解这种负载不均衡的现象,但也无法避免,最好使用默认的20个map即可。当然可以运用工具来控制集群中的块分布的均匀程度来进一步缓解这种负载不均衡的现象。

猜你喜欢

转载自blog.csdn.net/qq_35688140/article/details/84000839