Hadoop简介(2.5.1版本)
- 分布式存储系统HDFS(Hadoop Distributed File System)
- 分布式存储系统
- 提高了高可靠性、高扩展性和高吞吐率的数据存储服务
- 分布式计算框架MapReduce
- 分布式计算框架
- 具有易于编程、高容错性和高扩展性等优点。
HDFS介绍以及整体结构 Hadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。
HDFS的结构
HDFS优点
- 高容错性
- 数据自动保存多个副本
- 副本丢失之后,自动恢复
- 适合批处理 -移动计算而非数据 -数据位置暴露给计算框架
- 适合大数据处理
- GB、TB甚至PB级数据
- 百万规模以上的文件数目
- 10k+ 节点
- 可构建在廉价机器上
- 通过多副本提高可靠性
- 提供容错和恢复机制
HDFS缺点
- 低延迟数据访问
- 比如毫秒级
- 低延迟与高吞吐率
- 小文件存取
- 占用NameNode大量内存
- 寻道时间超过读取时间
- 并发写入、文件随机修改
- 一个文件只能有一个写者
- 仅支持append(2.X支持 但是一般不用)
HDFS架构
HDFS数据存储单元(block)
- 文件被切分成固定大小的数据块
- 默认数据块大小为128M(2.X),可配置
- 若文件大小不到128M,则单独存在一个block
- 一个文件存储方式
- 按大小被切分成若干个block,存储到不同节点上
- 默认情况下每个block都有三个副本 -Block大小和副本数通过Client端上传文件时设置,文件上传成功之后副本数可以变更,Block size不可变更
HDFS设计思想
NameNode(NN) -NameNode主要功能:接收客户端的读写服务
- NameNode保存metadate信息包括
- 文件owership和permissions -文件包含哪些块
- Block保存在哪个DataNode(由DataNode启动时上报)
- NameNode的metadate信息在启动后会加载到内存
- metadata存储到磁盘文件名为“fsimage”
- Block的文件信息不会保存到fsimage
- edits记录对matadata的操作日志
补充:SecondaryNameNode(SNN - 2.X没有)
- 它不是NN的备份(但可以做备份),它的主要工作是帮助NN合并edits log,减少NN启动时间
- SNN执行合并时机
- 根据配置文件设置的时间间隔fs.checkpoint.period 默认3600s
- 根据配置文件设置edits log大小fs.checkpoint.size规定edits文件的最大默认是64M
- 当删除一个文件的时候其实并不是马上删除,而是在edits log中记录,到一定时间与fsimage通过SNN进行合并的时候进行删除。由于涉及大多的IO和消耗CPU,所以在NN中不做数据操作的合并,而是让另一个机器的CPU去计算实现SNN根据时间来不断合并各个NN,这样用户体验感比较好,速度也是比较快。 -那么通过SNN合并之后的新的FSimage和edits log会被推送到NN中并且替换原来的FSimage和edits log,这样NN 里面隔段时间就是新的数据
SNN合并流程
首先是NN中的Fsimage和edits文件通过网络拷贝,到达SNN服务器中,拷贝的同时,用户的实时在操作数据,那么NN中就会从新生成一个edits来记录用户的操作,而另一边的SNN将拷贝过来的edits和fsimage进行合并,合并之后就替换NN中的fsimage。之后NN根据fsimage进行操作(当然每隔一段时间就进行替换合并,循环)。当然新的edits与合并之后传输过来的fsimage会在下一次时间内又进行合并。
HDFS--DN
- 存储数据(Block)
- 启动DN线程的时候会向NN汇报block信息,之后NN根据汇报的block信息来找到block数据
- 通过向NN发送心跳保持与其联系(3秒一次),如果NN 10分钟没有收到DN的心跳,则认为其已经lost,NN就会向DN发消息,告诉其副本不够,并copy其上的block到其他的DN中。
Block的副本放置策略
- 第一个副本:放置在上传文件的DN;如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点;
- 第二个副本:放置在于第一个副本不同的机甲的节点上;
- 第三个副本:与第二个副本相同机架的节点;
- 更多副本:随机节点。
HDFS读写流程
- 读路程-reading data from hdfs
- 客户端(client)用FileSystem的open()函数打开文件
- DistributedFileSystem用RPC调用NameNode,得到文件的数据块信息。
- 对于每一个数据块,NameNode返回保存数据块的DataNode的地址。
- DistributedFileSystem返回FSDataInputStream给客户端,用来读取数据。
- 客户端调用stream的read()函数开始读取数据。
- DFSInputStream连接保存此文件第一个数据块的最近的DataNode。
- Data从DataNode读到客户端(client)
- 当此数据块读取完毕时,DFSInputStream关闭和此DataNode的连接,然后连接此文件下一个数据块的最近的DataNode。
- 当客户端读取完毕数据的时候,调用FSDataInputStream的close函数。
- 在读取数据的过程中,如果客户端在与DataNode通信出现错误,则尝试连接包含此数据块的下一个DataNode。
@Test
public void download() throws Exception {
Path path = new Path("/user/test");
FSDataInputStream inputStream = fs.open(path);
IOUtils.copyBytes(inputStream, new FileOutputStream("D://test1.txt"), conf);
}
- 写路程-writing data to hdfs
- 客户端调用用FileSystem的create()来创建文件
- DistributedFileSystem用RPC调用NameNode,在文件系统的命名空间中创建一个新的文件。
- NameNode首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。
- DistributedFileSystem返回DFSOutputStream,客户端用于写数据。
- 客户端开始写入数据,DFSOutputStream将数据分成块,写入data queue。
- Data queue由Data Streamer读取,并通知NameNode分配DataNode,用来存储数据块(每块默认复制3块)。分配的DataNode放在一个pipeline里。
- Data Streamer将数据块写入pipeline中的第一个DataNode。第一个DataNode将数据块发送给第二个DataNode。第二个数据节点将数据发送给第三个DataNode。
- DFSOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。
- 如果数据节点在写入的过程中失败:
- 关闭pipeline,将ack queue中的数据块放入data queue的开始。
- 当前的数据块在已经写入的DataNode中被NameNode赋予新的标示,则错误节点重启后能够察觉其数据块是过时的,会被删除。
- 失败的DataNode从pipeline中移除,另外的数据块则写入pipeline中的另外两个DataNode。
- NameNode则被通知此数据块是复制块数不足,将来会再创建第三份备份。
- 当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的DataNode,并等待ack queue返回成功。最后通知NameNode写入完毕。
@Test
public void upload() throws Exception {
Path path = new Path("/user/test");
FSDataOutputStream outputStream = fs.create(path);
IOUtils.copyBytes(new FileInputStream("D:\\test.txt"), outputStream, conf);
}
HDFS文件权限
java 实现 HDFS基本操作
package org.set.hadoop.hdfs;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
*
* HdfsDemo
* @since V1.0.0
* Created by SET on 2016-09-08 20:54:55
* @see
*/
public class HdfsDemo {
//默认是加载src目录下的配置文件
private Configuration conf;
private FileSystem fs;
@Before
public void setup() throws Exception {
conf = new Configuration();
fs = FileSystem.newInstance(conf);
}
@After
public void close() throws Exception {
fs.close();
}
@Test
public void mkdir() throws Exception {
Path path = new Path("/hdfs");
boolean flag = fs.mkdirs(path);
if(flag) {
System.out.println("mkdir /hdfs success@!");
}
}
@Test
public void del() throws Exception {
Path path =new Path("/hdfs");
boolean flag = fs.delete(path, true);
if(flag) {
System.out.println("del /hdfs success@!");
}
}
@Test
public void upload() throws Exception {
Path path = new Path("/user/test");
FSDataOutputStream outputStream = fs.create(path);
IOUtils.copyBytes(new FileInputStream("D:\\test.txt"), outputStream, conf);
}
@Test
public void download() throws Exception {
Path path = new Path("/user/test");
FSDataInputStream inputStream = fs.open(path);
IOUtils.copyBytes(inputStream, new FileOutputStream("D://test1.txt"), conf);
}
@Test
public void ls() throws Exception {
Path path = new Path("/user/");
FileStatus[] fss = fs.listStatus(path);
for (FileStatus s : fss) {
System.out.println(s.getPath() + "-" + s.getOwner() + "-" + s.getModificationTime());
}
}
@Test
public void uploadSeq() throws Exception {
Path path = new Path("/user/seq");
SequenceFile.Writer seq = new SequenceFile.Writer(fs, conf, path, Text.class, Text.class);
File file = new File("D:\\seq");
for (File f: file.listFiles()) {
seq.append(new Text(f.getName()), new Text(FileUtils.readFileToString(f)));
}
}
@Test
public void getSeq() throws Exception {
Path path = new Path("/user/seq");
SequenceFile.Reader reader = new SequenceFile.Reader(fs, path, conf);
Text key = new Text();
Text value = new Text();
while(reader.next(key, value)) {
System.out.println(key.toString());
System.out.println(value.toString());
}
}
}