hdfs常用命令
hdfs namenode -format 启动前先格式化文件系统
hadoop-daemon.sh start namenode|datanode 启动namenode和datanode
hdfs dfs -rm -r /user/it/output 删除文件系统的文件
hdfs dfs -mkdir -p /usr/atguigu/input 在hdfs上创建文件夹
hdfs dfs -put ./wcinput/wc.input /usr/it/input 向hdfs上传文件
hdfs dfs -ls /user/atguigu/output 查看文件
hdfs dfs -cat /user/atguigu/output/part-r-00000
jps java下查看进程和命令,这是一个java命令
HDFS的优缺点
- 优点
1.高容错性
2.适合大数据处理,规模达到GB、TB甚至PB,文件规模可以达到百万数量级
3.数据流失访问,能保证数据的一致性
4.可构建在廉价机器上,通过多副本机制,提高可靠性 - 缺点
1.不适合低延时数据访问
2.无法高效对小文件进行存储(占用namenode内存、小文件的寻址时间大于读取时间)
3.不支持并发写入、文件随机修改,支持追加
HDFS的组成
- Client:就是客户端。
(1)文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行存储;
(2)与NameNode交互,获取文件的位置信息;
(3)与DataNode交互,读取或者写入数据;
(4)Client提供一些命令来管理HDFS,比如启动或者关闭HDFS;
(5)Client可以通过一些命令来访问HDFS; - NameNode:就是Master,它是一个主管、管理者。
(1)管理HDFS的名称空间;
(2)管理数据块(Block)映射信息;
(3)配置副本策略;
(4)处理客户端读写请求。 - DataNode:就是Slave。NameNode下达命令,DataNode执行实际的操作。
(1)存储实际的数据块;
(2)执行数据块的读/写操作。 - Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
(1)辅助NameNode,分担其工作量;
(2)定期合并Fsimage和Edits,并推送给NameNode;
(3)在紧急情况下,可辅助恢复NameNode。
HDFS文件块大小
- HDFS的块比磁盘的块大,其目的是为了最小化寻址开销。如果块设置得足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而,传输一个由多个块组成的文件的时间取决于磁盘传输速率。
- 如果寻址时间约为10ms,而传输速率为100MB/s,为了使寻址时间仅占传输时间的1%,我们要将块大小设置约为100MB(10ms100100M/s = 100M)。默认的块大小128MB。
HFDS的Shell操作
基本语法:
hadoop fs 具体命令
常用命令:
(0)启动Hadoop集群(方便后续的测试)
[testuser@hadoop102 hadoop-2.7.2]$ sbin/start-dfs.sh
[testuser@hadoop103 hadoop-2.7.2]$ sbin/start-yarn.sh
(1)-help:输出这个命令参数
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -help rm
(2)-ls: 显示目录信息
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -ls /
(3)-mkdir:在hdfs上创建目录
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -mkdir -p /sanguo/shuguo
(4)-moveFromLocal从本地剪切粘贴到hdfs
[testuser@hadoop102 hadoop-2.7.2]$ touch kongming.txt
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -moveFromLocal ./kongming.txt /sanguo/shuguo
(5)-appendToFile :追加一个文件到已经存在的文件末尾
[testuser@hadoop102 hadoop-2.7.2]$ touch liubei.txt
[testuser@hadoop102 hadoop-2.7.2]$ vi liubei.txt
输入
san gu mao lu
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -appendToFile liubei.txt /sanguo/shuguo/kongming.txt
(6)-cat:显示文件内容
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -cat /sanguo/shuguo/kongming.txt
(7)-tail:显示一个文件的末尾
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -tail /sanguo/shuguo/kongming.txt
(8)-chgrp 、-chmod、-chown:linux文件系统中的用法一样,修改文件所属权限
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -chmod 666 /sanguo/shuguo/kongming.txt
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -chown testuser:testuser /sanguo/shuguo/kongming.txt
(9)-copyFromLocal:从本地文件系统中拷贝文件到hdfs路径去
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -copyFromLocal README.txt /
(10)-copyToLocal:从hdfs拷贝到本地
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -copyToLocal /sanguo/shuguo/kongming.txt ./
(11)-cp :从hdfs的一个路径拷贝到hdfs的另一个路径
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -cp /sanguo/shuguo/kongming.txt /zhuge.txt
(12)-mv:在hdfs目录中移动文件
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -mv /zhuge.txt /sanguo/shuguo/
(13)-get:等同于copyToLocal,就是从hdfs下载文件到本地
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -get /sanguo/shuguo/kongming.txt ./
(14)-getmerge :合并下载多个文件,比如hdfs的目录 /aaa/下有多个文件:log.1, log.2,log.3,...
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -getmerge /user/testuser/test/* ./zaiyiqi.txt
(15)-put:等同于copyFromLocal
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -put ./zaiyiqi.txt /user/testuser/test/
(16)-rm:删除文件或文件夹
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -rm /user/testuser/test/jinlian2.txt
(17)-rmdir:删除空目录
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -mkdir /test
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -rmdir /test
(18)-du统计文件夹的大小信息
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -du -s -h /user/testuser/test
2.7 K /user/testuser/test
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -du -h /user/testuser/test
1.3 K /user/testuser/test/README.txt
15 /user/testuser/test/jinlian.txt
1.4 K /user/testuser/test/zaiyiqi.txt
(19)-setrep:设置hdfs中文件的副本数量
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt
HDFS客户端操作
maven pom文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.2</version>
</dependency>
</dependencies>
log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
HDFS的API操作
- 连接到hdfs,并新建文件夹
@Test
public void testMkdirs() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
// 配置在集群上运行
// configuration.set("fs.defaultFS", "hdfs://hadoop102:9000");
// FileSystem fs = FileSystem.get(configuration);
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 创建目录
fs.mkdirs(new Path("/0103/test/user"));
// 3 关闭资源
fs.close();
}
- 文件上传
@Test
public void testCopyFromLocalFile() throws IOException, InterruptedException, URISyntaxException {
// 1 获取文件系统
Configuration configuration = new Configuration();
configuration.set("dfs.replication", "2");
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 上传文件
fs.copyFromLocalFile(new Path("e:/hello.txt"), new Path("/hello.txt"));
// 3 关闭资源
fs.close();
System.out.println("over");
}
- 文件下载
@Test
public void testCopyToLocalFile() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 执行下载操作
// boolean delSrc 指是否将原文件删除
// Path src 指要下载的文件路径
// Path dst 指将文件下载到的路径
// boolean useRawLocalFileSystem 是否开启文件校验
fs.copyToLocalFile(false, new Path("/hello1.txt"), new Path("e:/hello1.txt"), true);
// 3 关闭资源
fs.close();
}
- 文件夹删除
@Test
public void testDelete() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 执行删除
fs.delete(new Path("/0103/"), true);
// 3 关闭资源
fs.close();
}
- 文件名更改
@Test
public void testRename() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 修改文件名称
fs.rename(new Path("/hello.txt"), new Path("/hello6.txt"));
// 3 关闭资源
fs.close();
}
- 文件详情查看,查看文件名称、权限、长度、块信息
@Test
public void testListFiles() throws IOException, InterruptedException, URISyntaxException{
// 1获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 获取文件详情
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
while(listFiles.hasNext()){
LocatedFileStatus status = listFiles.next();
// 输出详情
// 文件名称
// 长度
System.out.println(status.getLen());
// 权限
System.out.println(status.getPermission());
// 组
System.out.println(status.getGroup());
// 获取存储的块信息
BlockLocation[] blockLocations = status.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
// 获取块存储的主机节点
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println(host);
}
System.out.println("----------------分割线-----------------");
}
}
- 判断文件和文件夹
@Test
public void testListStatus() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件配置信息
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 判断是文件还是文件夹
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus fileStatus : listStatus) {
// 如果是文件
if (fileStatus.isFile()) {
System.out.println("f:"+fileStatus.getPath().getName());
}else {
System.out.println("d:"+fileStatus.getPath().getName());
}
}
// 3 关闭资源
fs.close();
}
- 使用hdfs的IO流实现文件上传
@Test
public void testPut() throws Exception{
//获取hdfs的客户端
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop102:9000"), new Configuration(), "testuser");
//创建输入流
FileInputStream fis = new FileInputStream("h://hello.txt");
//创建输出流
FSDataOutputStream fos = fileSystem.create(new Path("/999/dianda/hello.txt"));
//流的拷贝
IOUtils.copyBytes(fis,fos,new Configuration());
//关闭资源
fileSystem.close();
System.out.println("over");
}
程序运行报错
Exception in thread "main" java.lang.UnsatisfiedLinkError:org.apache.hadoop.util.NativeCrc32.nativeComputeChunkedSumsByteArray(II[BI[BIILjava/lang/String;JZ)V
at org.apache.hadoop.util.NativeCrc32.nativeComputeChunkedSumsByteArray(Native Method)
at org.apache.hadoop.util.NativeCrc32.calculateChunkedSumsByteArray(NativeCrc32.java:86)
at org.apache.hadoop.util.DataChecksum.calculateChunkedSums(DataChecksum.java:430)
at org.apache.hadoop.fs.FSOutputSummer.writeChecksumChunks(FSOutputSummer.java:202)
.............
需要选择正确的版本并且在 Hadoop\bin和 C:\windows\system32 上添加/替换 ‘hadoop.dll’和‘winutils.exe
注意如果这个hadoop.dll的版本要和hadoop的一致,可以稍微高一点,低了可能就会报这个异常
下载的路径:https://github.com/steveloughran/winutils
- 使用HDFS的IO流实现文件下载
// 文件下载
@Test
public void getFileFromHDFS() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 获取输入流
FSDataInputStream fis = fs.open(new Path("/hello1.txt"));
// 3 获取输出流
FileOutputStream fos = new FileOutputStream(new File("e:/hello1.txt"));
// 4 流的对拷
IOUtils.copyBytes(fis, fos, configuration);
// 5 关闭资源
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
fs.close();
}
- 定位文件读取
1.下载第一块
@Test
public void readFileSeek1() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 获取输入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.2.tar.gz"));
// 3 创建输出流
FileOutputStream fos = new FileOutputStream(new File("e:/hadoop-2.7.2.tar.gz.part1"));
// 4 流的拷贝
byte[] buf = new byte[1024];
for(int i =0 ; i < 1024 * 128; i++){
fis.read(buf);
fos.write(buf);
}
// 5关闭资源
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
}
- 下载第二块
@Test
public void readFileSeek2() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "testuser");
// 2 打开输入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.2.tar.gz"));
// 3 定位输入数据位置
fis.seek(1024*1024*128);
// 4 创建输出流
FileOutputStream fos = new FileOutputStream(new File("e:/hadoop-2.7.2.tar.gz.part2"));
// 5 流的对拷
IOUtils.copyBytes(fis, fos, configuration);
// 6 关闭资源
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
}
- 合并文件
在window命令窗口中执行 type hadoop-2.7.2.tar.gz.part2 >> hadoop-2.7.2.tar.gz.part1
.
HDFS写数据流程
1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
2)NameNode返回是否可以上传。
3)客户端请求第一个 block上传到哪几个datanode服务器上。
4)NameNode返回3个datanode节点,分别为dn1、dn2、dn3。
5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
6)dn1、dn2、dn3逐级应答客户端。
7)客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,dn1收到一个packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
8)当一个block传输完成之后,客户端再次请求NameNode上传第二个block的服务器。(重复执行3-7步)。
网络拓扑
节点距离:两个节点到达最近的共同祖先的距离总和。
Hadoop2.7.2副本节点选择
第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本和第一个副本位于相同机架,随机节点。
第三个副本位于不同机架,随机节点。
HDFS读数据流程
1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以packet为单位来做校验)。
4)客户端以packet为单位接收,先在本地缓存,然后写入目标文件。
NameNode和SecondaryNameNode
工作机制
第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建fsimage和edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对数据进行增删改查。
第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要checkpoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行checkpoint。
(3)NameNode滚动正在写的edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。
NN和2NN工作机制详解:
Fsimage:namenode内存中元数据序列化后形成的文件。
Edits:记录客户端更新元数据信息的每一步操作(可通过Edits运算出元数据)。
namenode启动时,先滚动edits并生成一个空的edits.inprogress,然后加载edits和fsimage到内存中,此时namenode内存就持有最新的元数据信息。client开始对namenode发送元数据的增删改查的请求,这些请求的操作首先会被记录的edits.inprogress中(查询元数据的操作不会被记录在edits中,因为查询操作不会更改元数据信息),如果此时namenode挂掉,重启后会从edits中读取元数据的信息。然后,namenode会在内存中执行元数据的增删改查的操作。
由于edits中记录的操作会越来越多,edits文件会越来越大,导致namenode在启动加载edits时会很慢,所以需要对edits和fsimage进行合并(所谓合并,就是将edits和fsimage加载到内存中,照着edits中的操作一步步执行,最终形成新的fsimage)。secondarynamenode的作用就是帮助namenode进行edits和fsimage的合并工作。
secondarynamenode首先会询问namenode是否需要checkpoint(触发checkpoint需要满足两个条件中的任意一个,定时时间到和edits中数据写满了)。直接带回namenode是否检查结果。secondarynamenode执行checkpoint操作,首先会让namenode滚动edits并生成一个空的edits.inprogress,滚动edits的目的是给edits打个标记,以后所有新的操作都写入edits.inprogress,其他未合并的edits和fsimage会拷贝到secondarynamenode的本地,然后将拷贝的edits和fsimage加载到内存中进行合并,生成fsimage.chkpoint,然后将fsimage.chkpoint拷贝给namenode,重命名为fsimage后替换掉原来的fsimage。namenode在启动时就只需要加载之前未合并的edits和fsimage即可,因为合并过的edits中的元数据信息已经被记录在fsimage中。
Fsimage和Edits
namenode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件
fsimage_0000000000000000000
fsimage_0000000000000000000.md5
seen_txid
VERSION
1)Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件idnode的序列化信息。
2)Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到edits文件中。
3)seen_txid文件保存的是一个数字,就是最后一个edits_的数字
4)每次NameNode启动的时候都会将fsimage文件读入内存,并edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将fsimage和edits文件进行了合并。
oiv查看fsimage文件
- 基本语法
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
[testuser@hadoop102 current]$ pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current
[testuser@hadoop102 current]$ hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-2.7.2/fsimage.xml
[testuser@hadoop102 current]$ cat /opt/module/hadoop-2.7.2/fsimage.xml
fsimage.xml
<inode>
<id>16386</id>
<type>DIRECTORY</type>
<name>user</name>
<mtime>1512722284477</mtime>
<permission>testuser:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16387</id>
<type>DIRECTORY</type>
<name>testuser</name>
<mtime>1512790549080</mtime>
<permission>testuser:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16389</id>
<type>FILE</type>
<name>wc.input</name>
<replication>3</replication>
<mtime>1512722322219</mtime>
<atime>1512722321610</atime>
<perferredBlockSize>134217728</perferredBlockSize>
<permission>testuser:supergroup:rw-r--r--</permission>
<blocks>
<block>
<id>1073741825</id>
<genstamp>1001</genstamp>
<numBytes>59</numBytes>
</block>
</blocks>
</inode >
可以看出,fsimage中没有记录块所对应datanode,
oev查看edits文件
- 基本语法
hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径
[testuser@hadoop102 current]$ hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-2.7.2/edits.xml
[testuser@hadoop102 current]$ cat /opt/module/hadoop-2.7.2/edits.xml
edits.xml
<?xml version="1.0" encoding="UTF-8"?>
<EDITS>
<EDITS_VERSION>-63</EDITS_VERSION>
<RECORD>
<OPCODE>OP_START_LOG_SEGMENT</OPCODE>
<DATA>
<TXID>129</TXID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD</OPCODE>
<DATA>
<TXID>130</TXID>
<LENGTH>0</LENGTH>
<INODEID>16407</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943607866</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME>DFSClient_NONMAPREDUCE_-1544295051_1</CLIENT_NAME>
<CLIENT_MACHINE>192.168.1.5</CLIENT_MACHINE>
<OVERWRITE>true</OVERWRITE>
<PERMISSION_STATUS>
<USERNAME>testuser</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
<RPC_CLIENTID>908eafd4-9aec-4288-96f1-e8011d181561</RPC_CLIENTID>
<RPC_CALLID>0</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ALLOCATE_BLOCK_ID</OPCODE>
<DATA>
<TXID>131</TXID>
<BLOCK_ID>1073741839</BLOCK_ID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_SET_GENSTAMP_V2</OPCODE>
<DATA>
<TXID>132</TXID>
<GENSTAMPV2>1016</GENSTAMPV2>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD_BLOCK</OPCODE>
<DATA>
<TXID>133</TXID>
<PATH>/hello7.txt</PATH>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>0</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<RPC_CLIENTID></RPC_CLIENTID>
<RPC_CALLID>-2</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_CLOSE</OPCODE>
<DATA>
<TXID>134</TXID>
<LENGTH>0</LENGTH>
<INODEID>0</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943608761</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME></CLIENT_NAME>
<CLIENT_MACHINE></CLIENT_MACHINE>
<OVERWRITE>false</OVERWRITE>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>25</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<PERMISSION_STATUS>
<USERNAME>testuser</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
</DATA>
</RECORD>
</EDITS >
checkpoint时间设置
1)通常情况下,SecondaryNameNode每隔一小时执行一次,默认配置在[hdfs-default.xml]文件中,用户如果修改在hdfs-site中修改
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600</value>
</property >
2)一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次。
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60</value>
<description> 1分钟检查一次操作次数</description>
</property >
NameNode故障处理
NameNode故障后,可以采用如下两种方法恢复数据。
方法一:将SecondaryNameNode中数据拷贝到NameNode存储数据的目录;
1. kill -9 namenode进程
2. 删除NameNode存储的数据(/opt/module/hadoop-2.7.2/data/tmp/dfs/name)
[testuser@hadoop102 hadoop-2.7.2]$ rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/name/*
3. 拷贝SecondaryNameNode中数据到原NameNode存储数据目录
[testuser@hadoop102 dfs]$ scp -r testuser@hadoop104:/opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary/* ./name/
4. 重新启动namenode
[testuser@hadoop102 hadoop-2.7.2]$ sbin/hadoop-daemon.sh start namenode
方法二:使用-importCheckpoint选项启动NameNode守护进程,从而将SecondaryNameNode中数据拷贝到NameNode目录中。
1.修改hdfs-site.xml中的
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>120</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/opt/module/hadoop-2.7.2/data/tmp/dfs/name</value>
</property>
2. kill -9 namenode进程
3. 删除NameNode存储的数据(/opt/module/hadoop-2.7.2/data/tmp/dfs/name)
[testuser@hadoop102 hadoop-2.7.2]$ rm -rf /opt/module/hadoop-2.7.2/data/tmp/dfs/name/*
4. 如果SecondaryNameNode不和NameNode在一个主机节点上,需要将SecondaryNameNode存储数据的目录拷贝到NameNode存储数据的平级目录,并删除in_use.lock文件
[testuser@hadoop102 dfs]$ scp -r testuser@hadoop104:/opt/module/hadoop-2.7.2/data/tmp/dfs/namesecondary ./
[testuser@hadoop102 namesecondary]$ rm -rf in_use.lock
[testuser@hadoop102 dfs]$ pwd
/opt/module/hadoop-2.7.2/data/tmp/dfs
[testuser@hadoop102 dfs]$ ls
data name namesecondary
5. 导入检查点数据(等待一会ctrl+c结束掉)
[testuser@hadoop102 hadoop-2.7.2]$ bin/hdfs namenode -importCheckpoint
6. 启动namenode
[testuser@hadoop102 hadoop-2.7.2]$ sbin/hadoop-daemon.sh start namenode
集群安全模式
NameNode启动时,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作(合并fsimage和edits)。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的fsimage文件和一个空的编辑日志。此时,NameNode开始监听DataNode请求。但是此刻,NameNode运行在安全模式,即NameNode的文件系统对于客户端来说是只读的。
系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中。在系统的正常操作期间,NameNode会在内存中保留所有块位置的映射信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的块位置信息之后,即可高效运行文件系统。
如果满足“最小副本条件”,NameNode会在30秒钟之后就退出安全模式。所谓的最小副本条件指的是在整个文件系统中99.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。
- 基本语法
bin/hdfs dfsadmin -safemode get (功能描述:查看安全模式状态)
bin/hdfs dfsadmin -safemode enter (功能描述:进入安全模式状态)
bin/hdfs dfsadmin -safemode leave (功能描述:离开安全模式状态)
bin/hdfs dfsadmin -safemode wait (功能描述:等待安全模式状态,监控安全模式)
案例:
(1)先进入安全模式
[testuser@hadoop102 hadoop-2.7.2]$ bin/hdfs dfsadmin -safemode enter
(2)执行下面的脚本,编辑一个脚本
#!/bin/bash
bin/hdfs dfsadmin -safemode wait
bin/hdfs dfs -put ~/hello.txt /root/hello.txt
(3)再打开一个窗口,执行
[testuser@hadoop102 hadoop-2.7.2]$ bin/hdfs dfsadmin -safemode leave
NameNode多目录配置
NameNode的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性
配置如下:
(1)在hdfs-site.xml文件中增加如下内容
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/name1,file:///${hadoop.tmp.dir}/dfs/name2</value>
</property>
(2)停止集群,删除data和logs中所有数据。
[testuser@hadoop102 hadoop-2.7.2]$ stop-dfs.sh
[testuser@hadoop102 hadoop-2.7.2]$ rm -rf data/ logs/
[testuser@hadoop103 hadoop-2.7.2]$ rm -rf data/ logs/
[testuser@hadoop104 hadoop-2.7.2]$ rm -rf data/ logs/
(3)格式化集群并启动。
[testuser@hadoop102 hadoop-2.7.2]$ bin/hdfs namenode –format
[testuser@hadoop102 hadoop-2.7.2]$ sbin/start-dfs.sh
(4)查看结果
[testuser@hadoop102 dfs]$ ll
总用量 12
drwx------. 3 testuser testuser 4096 12月 11 08:03 data
drwxrwxr-x. 3 testuser testuser 4096 12月 11 08:03 name1
drwxrwxr-x. 3 testuser testuser 4096 12月 11 08:03 name2
DataNode
工作机制
1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
2)DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令,如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟30秒没有收到某个DataNode的心跳,则认为该节点不可用。
4)集群运行中可以安全加入和退出一些节点。
数据完整性
1)当DataNode读取block的时候,它会计算checksum。
2)如果计算后的checksum,与block创建时值不一样,说明block已经损坏。
3)client读取其他DataNode上的block。
4)datanode在其文件创建后周期验证checksum,
掉线时限参数设置
DataNode进程死亡或者网络故障造成DataNode无法与NameNode通信,NameNode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。HDFS默认的超时时长为10分钟+30秒。如果定义超时时间为timeout,则超时时长的计算公式为:
timeout = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval。
而默认的dfs.namenode.heartbeat.recheck-interval 大小为5分钟,dfs.heartbeat.interval默认为3秒。
需要注意的是hdfs-site.xml 配置文件中的heartbeat.recheck.interval的单位为毫秒,dfs.heartbeat.interval的单位为秒。
<property>
<name>dfs.namenode.heartbeat.recheck-interval</name>
<value>300000</value>
</property>
<property>
<name> dfs.heartbeat.interval </name>
<value>3</value>
</property>
服役新数据节点
随着业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点
- 环境准备
(1)克隆一台虚拟机
(2)修改ip地址和主机名称
(3)修改xsync文件,增加新增节点的ssh无密登录配置
(4)删除原来HDFS文件系统留存的文件(/opt/module/hadoop-2.7.2/data) - 服役新节点具体步骤
(1)在namenode的/opt/module/hadoop-2.7.2/etc/hadoop目录下创建dfs.hosts文件白名单名字可以随便写,只要绝对路径指向正确既可以
(2)在namenode的hdfs-site.xml配置文件中增加dfs.hosts属性[testuser@hadoop105 hadoop]$ pwd /opt/module/hadoop-2.7.2/etc/hadoop [testuser@hadoop105 hadoop]$ touch dfs.hosts [testuser@hadoop105 hadoop]$ vi dfs.hosts 添加如下主机名称(包含新服役的节点) hadoop102 hadoop103 hadoop104 hadoop105
(3)刷新namenode<property> <name>dfs.hosts</name> <value>/opt/module/hadoop-2.7.2/etc/hadoop/dfs.hosts</value> </property>
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfsadmin -refreshNodes
Refresh nodes successful
(4)更新resourcemanager节点
(5)在NameNode的slaves文件中增加新主机名称 hadoop105[testuser@hadoop102 hadoop-2.7.2]$ yarn rmadmin -refreshNodes 18/06/24 14:17:11 INFO client.RMProxy: Connecting to ResourceManager at hadoop103/192.168.1.103:8033
(6)单独命令启动新的数据节点和节点管理器
(7)在web浏览器上检查是否启动[testuser@hadoop105 hadoop-2.7.2]$ sbin/hadoop-daemon.sh start datanode starting datanode, logging to /opt/module/hadoop-2.7.2/logs/hadoop-testuser-datanode-hadoop105.out [testuser@hadoop105 hadoop-2.7.2]$ sbin/yarn-daemon.sh start nodemanager starting nodemanager, logging to /opt/module/hadoop-2.7.2/logs/yarn-testuser-nodemanager-hadoop105.out
退役旧数据节点
- 在namenode的/opt/module/hadoop-2.7.2/etc/hadoop目录下创建dfs.hosts.exclude文件
2.在namenode的hdfs-site.xml配置文件中增加dfs.hosts.exclude属性[testuser@hadoop102 hadoop]$ pwd /opt/module/hadoop-2.7.2/etc/hadoop [testuser@hadoop102 hadoop]$ touch dfs.hosts.exclude [testuser@hadoop102 hadoop]$ vi dfs.hosts.exclude
3.刷新namenode、刷新resourcemanager<property> <name>dfs.hosts.exclude</name> <value>/opt/module/hadoop-2.7.2/etc/hadoop/dfs.hosts.exclude</value> </property>
4.检查web浏览器,退役节点的状态为decommission in progress(退役中),说明数据节点正在复制块到其他节点[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfsadmin -refreshNodes Refresh nodes successful [testuser@hadoop102 hadoop-2.7.2]$ yarn rmadmin -refreshNodes 18/06/24 14:55:56 INFO client.RMProxy: Connecting to ResourceManager at hadoop103/192.168.1.103:8033
5.等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役
6.从include文件中删除退役节点,再运行刷新节点的命令[testuser@hadoop105 hadoop-2.7.2]$ sbin/hadoop-daemon.sh stop datanode stopping datanode [testuser@hadoop105 hadoop-2.7.2]$ sbin/yarn-daemon.sh stop nodemanager stopping nodemanager
(1)从namenode的dfs.hosts文件中删除退役节点hadoop105
(2)刷新namenode,刷新resourcemanager
7.从namenode的slave文件中删除退役节点hadoop105[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfsadmin -refreshNodes Refresh nodes successful [testuser@hadoop102 hadoop-2.7.2]$ yarn rmadmin -refreshNodes 18/06/24 14:55:56 INFO client.RMProxy: Connecting to ResourceManager at hadoop103/192.168.1.103:8033
8.如果数据不均衡,可以用命令实现集群的再平衡[testuser@hadoop102 hadoop-2.7.2]$ sbin/start-balancer.sh starting balancer, logging to /opt/module/hadoop-2.7.2/logs/hadoop-testuser-balancer-hadoop102.out Time Stamp Iteration# Bytes Already Moved Bytes Left To Move Bytes Being Moved
Datanode多目录配置
1.datanode也可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本
2.具体配置如下
hdfs-site.xml
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>
hadoop集群之间的递归数据复制
[testuser@hadoop102 hadoop-2.7.2]$ bin/hadoop discp hdfs://haoop102:9000/user/testuser/hello.txt hdfs://hadoop103:9000/user/testuser/hello.txt
Hadoop存档
- hdfs存储小文件弊端每个文件均按块存储,每个块的元数据存储在NameNode的内存中,因此hadoop存储小文件会非常低效。因为大量的小文件会耗尽NameNode中的大部分内存。但注意,存储小文件所需要的磁盘容量和存储这些文件原始内容所需要的磁盘空间相比也不会增多。例如,一个1MB的文件以大小为128MB的块存储,使用的是1MB的磁盘空间,而不是128MB。
- 解决存储小文件办法之一Hadoop存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少NameNode内存使用的同时,允许对文件进行透明的访问。具体说来,Hadoop存档文件对内(对于文件本身)还是一个一个独立文件,对NameNode(对外)而言却是一个整体,减少了NameNode的内存。
例:
(1)需要启动yarn进程(存档相当于走的是mapreduce程序)
[testuser@hadoop102 hadoop-2.7.2]$ start-yarn.sh
(2)归档文件
把/user/atguigu目录里面的所有文件归档成一个叫myhar.har的归档文件,并把归档后文件存储到/user/my路径下。
[testuser@hadoop102 hadoop-2.7.2]$ bin/hadoop archive -archiveName myhar.har -p /user/testuser /user/myhar.har
(3)查看归档
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr /user/my/myhar.har
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -lsr har:///user/my/myhar.har
(4)解归档文件
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -cp har:/// user/my/myhar.har/* /user/testuser
快照管理
快照相当于对目录做一个备份。并不会立即复制所有文件,而是指向同一个文件。当写入发生时,才会产生新文件。
- 基本语法
(1)hdfs dfsadmin -allowSnapshot 路径 (功能描述:开启指定目录的快照功能) (2)hdfs dfsadmin -disallowSnapshot 路径 (功能描述:禁用指定目录的快照功能,默认是禁用) (3)hdfs dfs -createSnapshot 路径 (功能描述:对目录创建快照) (4)hdfs dfs -createSnapshot 路径 名称 (功能描述:指定名称创建快照) (5)hdfs dfs -renameSnapshot 路径 旧名称 新名称 (功能描述:重命名快照) (6)hdfs lsSnapshottableDir (功能描述:列出当前用户所有可快照目录) (7)hdfs snapshotDiff 路径1 路径2 (功能描述:比较两个快照目录的不同之处) (8)hdfs dfs -deleteSnapshot <path> <snapshotName> (功能描述:删除快照)
例:
(1)开启/禁用指定目录的快照功能
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfsadmin -allowSnapshot /user/testuser/data
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfsadmin -disallowSnapshot /user/testuser/data
(2)对目录创建快照
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfs -createSnapshot /user/testuser/data
通过web访问hdfs://hadoop102:50070/user/testuser/data/.snapshot/s…..// 快照和源文件使用相同数据
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfs -lsr /user/testuser/data/.snapshot/
(3)指定名称创建快照
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfs -createSnapshot /user/testuser/data mysnap
(4)重命名快照
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfs -renameSnapshot /user/testuser/data/ mysnap mysnapshot
(5)列出当前用户所有可快照目录
[testuser@hadoop102 hadoop-2.7.2]$ hdfs lsSnapshottableDir
(6)比较两个快照目录的不同之处 相对于后面的路径来说的 + 表示增 -表示少
[testuser@hadoop102 hadoop-2.7.2]$ hdfs snapshotDiff /user/testuser/data/ . .snapshot/mysnapshot
(7)恢复快照
[testuser@hadoop102 hadoop-2.7.2]$ hdfs dfs -cp /user/testuser/input/.snapshot/mysnapshot /user
回收站
1.默认回收站,如图,默认值fs.trash.interval=0,0表示禁用回收站,可以设置删除文件的存活时间。默认值fs.trash.checkpoint.interval=0,检查回收站的间隔时间。如果该值为0,则该值设置和fs.trash.interval的参数值相等。要求fs.trash.checkpoint.interval<=fs.trash.interval。
2.启用回收站
修改core-site.xml,配置垃圾回收时间为1分钟。
<property>
<name>fs.trash.interval</name>
<value>1</value>
</property>
3.查看回收站
回收站在集群中的路径是删除目录下的 .Trash文件中:删除/user/testuser下的所有文件,则在该目录下新建.Trash目录,即:/user/testuser/.Trash/….
4.修改访问垃圾回收站用户名称,进入垃圾回收站用户名称,默认是dr.who,修改为testuser用户
[core-site.xml]
<property>
<name>hadoop.http.staticuser.user</name>
<value>testuser</value>
</property>
5.通过程序删除的文件不会经过回收站,需要调用moveToTrash()才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);
6.恢复回收站数据
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -mv /user/testuser/.Trash/Current/user/testuser/input /user/testuser/input
7.清空回收站
[testuser@hadoop102 hadoop-2.7.2]$ hadoop fs -expunge
yarn命令
使用yarn进行任务调度
hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar wordcount /usr/atguigu/input /user/atguigu/output
mr-jobhistory-daemon.sh start historyserver 启动历史服务器,在mapred-site.xml中配置
使用yarn进行任务调到