初识Hadoop-HDFS


HDFS-Hadoop Distributed Filesystem是Hadoop自带的一个分布式文件系统,可以将数据存储在分散的数据节点上。

适用场景

  1. 存储超大文件
  2. 流式数据访问,适合一次写入,多次读取,每次读取都读取全部数据或大部分数据的场景。

不适用场景

  1. 低时间延迟的数据访问,HDFS适合高吞吐的访问模式,传统的关系型数据库的访问方式并不适合HDFS,要求低延迟的场景可以使用Hbase。
  2. 大量的小文件,因为namenode将文件的元数据存储在内存中,因此该文件系统所能存储的文件总量受限于namenode节点的内存容量。一般来说,每个文件,目录以及数据块所占用的内存是150字节,那么存储100万个文件,假设每个文件占用一个数据块,那么至少需要1502100万字节(300M)的内存。
  3. 多用户写入,任意修改文件,HDFS中的文件的写入只支持单个写入者,而且写操作是以只添加的方式在文件末尾写入。不支持多个写入者,也不支持在文件的任意位置进行修改。

重要概念

数据块

对于磁盘来说有默认的数据块大小(一般512字节),这是磁盘进行读写的最小单位,构建于单个磁盘之上的文件系统可以通过磁盘块来管理存储的数据,该文件系统的数据块大小可以是磁盘块的整数倍。HDFS中也有数据块的概念,默认是128M(设计的比较大的原因是为了减少寻址开销),但是HDFS中小于一个块大小的文件不会占据整个块的空间。

namenode、datanode

HDFS系统中有两类节点-管理节点(namenode),工作节点(datanode),namenode只有一个处于运行状态,工作节点有多个。

  1. namenode 负责管理文件系统的命名空间,它维护着文件系统树以及整棵树内所有的文件和目录。存储文件到文件数据块的映射关系。这些信息以命名空间镜像文件编辑日志文件的形式永久保存在本地磁盘上。namenode也记录着每个文件的各个数据块所在的数据节点(datanode)信息,但是它并不永久保存这类信息,因为这些信息会在系统启动时根据数据节点发来的信息重建。
  2. datanode是文件系统的工作节点,它们负责存储和检索数据块,并定期向namenode报告它们所存储的数据块列表。
  3. namenode容错,如果namenode损坏,整个HDFS将不可用,因为文件系统树存储在namenode,如果没有这些信息就无法根据datanode的文件块信息进行文件重建。所以需要对namenode进行容错处理。一般有两种方式:
    1. 备份namenode上关于文件系统元数据的文件。Hadoop可以通过配置使namenode在多个文件系统上保存元数据的持久文件。并且这些写操作是原子的。一般的配置是,将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)。
    2. 运行一个辅助的namenode,这个节点负责定期合并主namenode上的命名空间镜像文件编辑日志文件以保持和主节点同步,当主节点发生故障时启用。但是这样也难免丢失部分数据,所以一般是将NFS中的持久化数据复制到辅助namenode。

块缓存

通常datanode从磁盘中读取文件块。但是对于频繁访问的文件对应的数据块可能被缓存在datanode的内存中。

联邦HDFS(扩展namenode)

namenode在内存中保存每个文件和每个数据块的引用关系。所以namenode的内存将成为系统横向扩展的瓶颈。在2.X版本中引入的联邦HDFS允许系统添加namenode实现扩展,其中每个namenode管理文件系统命名空间的一部分。例如一个namenode管理/user目录下的所有文件,另一个管理/order下的所有文件。

命令

  1. hadoop fs -help

  2. 拷贝本地文件到HDSF:
    hadoop fs -copyFromLocal input/docs/foo.txt (hdfs://localhost/user/tom/)foo.txt

  3. 拷贝HDFS文件到本地并校验是否更改:
    hadoop fs -copyToLocal (hdfs://localhost/user/tom/)foo.txt foo.txt
    md5 input/docs/foo.txt foo.txt

  4. 创建目录
    hadoop fs -mkdir books

  5. 列出目录文件
    hadoop fs -ls
    drwxr-xr-x - tom supergroup 0 2019-1-5 19:35 books
    drwxr-xr-x 1 tom supergroup 119 2019-1-5 19:35 foo.txt
    第1列是文件模式,第2列是这个文件的备份数(目录没有复本的概念,目录作为元数据保存在namenode中,而非datanode中),第3列第4列显示文件的所属用户和组别,第5列显示文件大小(字节)

  6. distcp并行复制文件(通过mapreduce实现,只有maper没有reducer),典型用法是在两个hdfs集群间复制数据,例如,以下命令在第二个集群上为第一个集群foo目录创建了一个备份:
    hadoop distcp -update -delete -p hdfs://namenode1/foo hdfs://namenode2/foo
    distcp

Hadoop文件系统

HDFS只是Hadoop文件系统的一种实现,java抽象类org.apache.hadoop.fs.FileSystem定义了Hadoop文件系统的客户端借口,以下是其几个典型实现。
hadoop文件系统-1
hadoop文件系统-2
可以通过uri选择合适的文件系统,例如命令行中列出本地文件系统根目录下所有文件
hadoop fs -ls file:///
如果非java应用想要访问HDFS可以通过http或者https直接访问或者通过代理
http访问HDFS
direct access方式需要HDFS的节点作为http服务器响应请求(dfs.webhdfs.enabled设置为true)

java接口FileSystem

URL方式访问数据

public class URLCat{
	static{
		//为了让URL可以识别"hdfs"的uri
		//每个Jvm只能调用一次该方法
		URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
	}
	public static void main(String[] args) trows Exception{
		InputStream in = null;
		try{
			in = new URL(args[0]).openStream();
			//process in
			IOUtils.copyBytes(in, System.out, 4096, false);
		} finally{
			IOUtils.closeStream(in);
		}
	}
}

运行

% export HADOOP_CLASSPATH=hadoop-examples.jar
% hadoop URLCat hdfs://localhost/user/tom/foo.txt

FileSystem Api方式访问数据

因为有时候setURLStreamHandlerFactory已经被其他组件调用了,我们无法再设置,所以可以使用FileSystem Api的方式访问数据。
Hadoop中通过Path对象而不是File对象来表示文件
读取

public class FileSystemCat{
	public static void main(String[] args) throws Exception{
		String uri = args[0];
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(uri), conf);
		InputStream in = null;
		try{
			//不指定缓冲区,使用默认缓冲区4K
			in = fs.open(new Path(uri));
			IOUtils.copyBytes(in, System.out, 4096, false);
			//回到起点,seek操作开销比较高
			in.seek(0);
			IOUtils.copyBytes(in, System.out, 4096, false);
		}finally{
			IOUtils.closeStream(in);
		}
	}
}

写入

public class FileCopyWithProgress{
	public static void main(String[] args) throws Exception{
		String localSrc = args[0];
		String dst = args[1];
		InputStream in = new BufferedInputStream(new FileInputSream(localSrc));
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(dst), conf);
		OutputStream out = fs.create(new Path(dst), new Progressable(){
			//反馈进度
			public void progress(){
				System.out.print(".");
			}
		});
		IOUtils.copyBytes(in, out, 4096, true);
	}
}
% hadoop FileCopyWithProgress input/docs/foo.txt hdfs://localhost/user/tom/foo.txt

列出一组hdfs一组路径中的文件信息

public class listStatus{
	public static void main(String[] args) throws Exception{
		String uri = args[0];
		Configuration conf = new Configuration();
		FileSystem fs = FileSystem.get(URI.create(uri), conf);

		Path[] paths = new Path[args.length];
		for(int i = 0; i<paths.length; i++){
			paths[i] = new Path(args[i]);
		}
		FileStatus[] status = fs.listStatus(paths);
		//将FileStatus转换为Path
		Path[] listedPaths = FileUtil.stat2Paths(status);
		for(Path p : listedPaths){
			System.out.println(p);
		}
	}
}

数据流

读取数据流

客户端读取hdfs数据

写入数据流

写入hdfs数据流

一致模型

文件系统的一致模型描述了文件读/写数据的可见性,HDFS为性能牺牲了一些POSIX的要求。
新建一个文件后可以在文件系统的命名空间中立即可见:

Path p = new Path("p");
Fs.create(p);
assertThat(fs.exists(p), is(true));

但是,写入文件的内容并不保证能立即可见:
当写入的数据超过一个块后,第一个数据块对新的reader就是可见的。之后的块也不例外,总之,当前正在写入的块对其他reader不可见。

Path p = new Path("p");
OutputStream out = fs.create(p);
out.write("content".getBytes("UTF-8"));
out.flush();
assertThat(fs.getFileStatus(p).getLen(), is(0L));

HDFS提供了一种强行将所有缓存刷新到datanode中的手段,即对FSDataOutputStream调用hflush()方法。当hflush()方法返回成功后,对所有新的reader而言,HDFS能保证文件中到目前为止写入的数据均到达所有datanode的写入管道并对所有新的reader均可见。

Path p = new Path("p");
FSDataOutputStream out = fs.create(p);
out.write("content".getBytes("UTF-8"));
out.hflush();
assertThat(fs.getFileStatus(p).getLen(), is((long)"content".getBytes("UTF-8")));

在hdfs中关闭文件(out.close())其实隐含了执行hflush()

需要注意的是,hflush()不能保证datanode已经将数据写到磁盘上,仅确保数据在datanode的内存中(如果断电,数据可能丢失),为确保数据写入磁盘,可以用hsync()

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值