HDFS (Hadoop Distributed File System), 它是一个文件系统, 用于存储文件, 通过目录树来定位文件 ; 其次, 它是分布式的, 由很多服务器联合起来实现其功能, 集群中的服务器有各自的角色。
使用场景 : 适合一次写入, 多次读出的场景, 且不支持文件的修改。
优点
高容错性
适合处理大数据
可构建在廉价机器之上
缺点
不适合低延时数据访问
无法高效的对大量小文件进行存储
不支持高并发写入、文件随机修改
NameNode : 就是Master, 是一个管理者。
1. 管理HDFS的名称空间 ;
2. 配置副本策略 ;
3. 管理数据块 (Block) 映射信息 ;
4. 处理客户端读写请求。
DataNode : 就是Slave, NameNode下达命令, DataNode执行实际的操作。
1. 存储实际的数据块 ;
2. 执行数据块的读写操作。
Client : 就是客户端。
1. 文件切分。文件上传HDFS时, Client将文件切分成一个一个的Block, 然后进行上传 ;
2. 与NameNode交互, 获取文件的位置信息 ;
3. 与DataNode交互, 读取或者写入数据 ;
4. Client提供一些命令来管理HDFS, 比如NameNode格式化 ;
5. Client通过一些命令来访问HDFS, 比如对HDFS增删改操作。
SecondaryNameNode : 并非NameNode的热备。当NameNode挂掉时, 它并不能替换NameNode。
1. 辅助NameNode, 分担其工作量, 比如定期合并Fsimage和Edits, 并推送给NameNode ;
2. 在紧急情况下, 可辅助恢复NameNode。
HDFS中的文件在物理上是分块存储 (Block), 块的大小可以通过配置参数(dfs.blocksize)来规定, 默认Hadoop1.x版本中是64M ; Hadoop2.x版本中是128M。
如果HDFS的块设置太小, 则会增加寻址时间, 降低效率。
如果HDFS的块设置太大, 则从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间, 导致程序在处理该块数据时, 会非常慢。
HDFS块大小的设置主要取决于磁盘传输速率。
基本语法 : bin/hadoop fs 具体命令 或 bin/hdfs dfs 具体命令
[user01@node01 hadoop-3.1.3]$ sbin/start-dfs.sh
[user01@node02 hadoop-3.1.3]$ sbin/start-yarn.sh
[user01@node01 hadoop-3.1.3]$ hdfs dfs -help rm
[user01@node01 hadoop-3.1.3]$ hdfs dfs -ls /
[user01@node01 hadoop-3.1.3]$ hdfs dfs -mkdir -p /test
[user01@node01 hadoop-3.1.3]$ hdfs dfs -moveFromLocal ./test.txt /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -copyFromLocal ./test2.txt /test/test2.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -put ./test4.txt /test/
[user01@node01 hadoop-3.1.3]$ hdfs dfs -get /test/test.txt ./
[user01@node01 hadoop-3.1.3]$ hdfs dfs -getmerge /test/* ./test3.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -copyToLocal /test/test2.txt ./
[user01@node01 hadoop-3.1.3]$ hdfs dfs -cp /test/test2.txt /test2/test3.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -mv /test/test.txt /test2/
[user01@node01 hadoop-3.1.3]$ hdfs dfs -appendToFile ./test.txt /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -cat /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -chmod 777 /test/test.txt
[user01@node01 hadoop-3.1.3]$ hfds dfs -chown user02:user02 /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -chgrp group01 /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -tail /test/test4.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -rm /test/test.txt
[user01@node01 hadoop-3.1.3]$ hdfs dfs -rmdir /test3
-s 显示所有总大小
-h 以人类可读的方式格式化文件的大小,而不是字节数
-v 选项显示标题行
-x 不包括快照
[user01@node01 hadoop-3.1.3]$ hdfs dfs -du -s -h /test
[user01@node01 hadoop-3.1.3]$ hdfs dfs -setrep 5 /test/test.txt
首先, 需在Windows中配置Hadoop的环境变量
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" strict="true" name="XMLConfig">
<Appenders>
<!-- 类型名为Console,名称为必须属性 -->
<Appender type="Console" name="STDOUT">
<!-- 布局为PatternLayout的方式,
输出样式为[INFO] [2018-01-22 17:34:01][org.test.Console]I'm here -->
<Layout type="PatternLayout"
pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" />
</Appender>
</Appenders>
<Loggers>
<!-- 可加性为false -->
<Logger name="test" level="info" additivity="false">
<AppenderRef ref="STDOUT" />
</Logger>
<!-- root loggerConfig设置 -->
<Root level="info">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
@Test
public void testCopyToLocal() throws URISyntaxException, IOException, InterruptedException {
//1. 获取文件系统
URI uri = new URI("hdfs://LIFE:9820");
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(uri, conf, "atguigu");
//2. 下载操作
// boolean delSrc 指是否将原文件删除
// Path src 指要下载的文件路径
// Path dst 指将文件下载到的路径
// boolean useRawLocalFileSystem 是否开启文件校验
fileSystem.copyToLocalFile(false, new Path("/test.txt"),
new Path("e:/test.txt"), true);
//3. 关闭资源
fileSystem.close();
}
@Test
public void testDelete() throws URISyntaxException, IOException, InterruptedException {
//1. 获取文件系统
URI uri = new URI("hdfs://LIFE:9820");
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(uri, conf, "atguigu");
//2. 删除操作
fileSystem.delete(new Path("/test/test.txt"), true);
//3. 关闭资源
fileSystem.close();
}
@Test
public void testRename() throws URISyntaxException, IOException, InterruptedException {
//1. 获取文件系统
URI uri = new URI("hdfs://LIFE:9820");
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(uri, conf, "atguigu");
//2. 修改文件名
fileSystem.rename(new Path("/test/test.txt"), new Path("/test/test4.txt"));
//3. 关闭资源
fileSystem.close();
}
@Test
public void testListFiles() throws URISyntaxException, IOException, InterruptedException {
//1. 获取文件系统
URI uri = new URI("hdfs://LIFE:9820");
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(uri, conf, "atguigu");
//2. 获取文件详情
RemoteIterator<LocatedFileStatus> listFiles = fileSystem.listFiles(new Path("/"), true);
// 输出详情
while (listFiles.hasNext()) {
LocatedFileStatus status = listFiles.next();
// 文件名
System.out.println(status.getPath().getName());
// 长度
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("------------- 分割线 ---------------");
}
//3. 关闭资源
fileSystem.close();
}
@Test
public void testListStatus() throws URISyntaxException, IOException, InterruptedException {
//1. 获取文件系统
URI uri = new URI("hdfs://LIFE:9820");
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(uri, conf, "atguigu");
//2. 判断是文件还是文件夹
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for (FileStatus fileStatus : fileStatuses) {
// 如果是文件
if(fileStatus.isFile()){
System.out.println("file:" + fileStatus.getPath().getName());
}else{
System.out.println("dire:" + fileStatus.getPath().getName());
}
}
//3. 关闭资源
fileSystem.close();
}
For the common case, when the replication factor is three, HDFS’s
placement policy is to put one replica on the local machine if the
writer is on a datanode, otherwise on a random datanode, another
replica on a node in a different (remote) rack, and the last on a
different node in the same remote rack.对于常见情况,当副本数为3时,HDFS的放置策略是,如果writer位于datanode上,则将一个副本放在本地机器上,否则将另一个副本放在另一个(远程)机架上的节点上。
第一个副本在Client所处的节点上。如果客户端在集群外, 则随机选择一个。
第二个副本在另一个机架的随机一个节点。
第三个副本在第二个副本所在机架的随机节点。
由于NameNode的元数据是存储在内存中的, 故存在元数据丢失的风险, 因此会在磁盘中备份元数据的FsImage(镜像文件)。当内存中的元数据更新时, 如果同时更新FsImage, 就会导致效率过低, 但如果不更新, 就会发生数据一致性问题, 一旦NameNode节点断电, 就会产生数据丢失。因此, 引入Edits(编辑日志)文件 (只作追加操作, 效率很高) , 每当元数据有更新或者添加元数据时, 修改内存中的元数据并追加到Edits中。但是随着时间延长, 会导致Edits文件数据过大, 效率降低, 而且一旦断电, 恢复元数据需要的时间过长。因此, 需要定期进行FsImage和Edits的合并, 如果这个操作由NameNode节点完成, 则会导致NameNode节点压力过大, 工作效率降低, 因此, 引入一个新的节点SecondaryNameNode, 专门用于对FsImage和Edits的合并。
NameNode被格式化后, 将在$HADOOP_HOME/data/tmp/dfs/name/current目录下产生如下文件
fsimage_00000000000000000000
fsimage_00000000000000000000.md5
seen_txid
VERSION
- oiv 查看FsImage文件
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
hdfs oiv -p XML -i $HADOOP_HOME/data/tmp/dfs/name/current/fsimage_000000000000000025 -o $HADOOP_HOME/fsimage.xml
- oev 查看Edits文件
hdfs oev -p 文件类型 -i 编制日志 -o 转换后文件输出路径
hdfs oev -p XML -i $HADOOP_HOME/data/tmp/dfs/name/current/edits_000000000000000000012_00000000000000000013 -o $HADOOP_HOME/edits.xml
- CheckPoint时间设置
通常情况下, SecondaryNameNode每一小时执行一次。
[hdfs-default.xml]
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600</value>
</property>
一分钟检查一次操作次数, 当操作次数达到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 >
1、NameNode启动
NameNode启动时,首先将镜像文件(Fsimage)载入内存,并执行编辑日志(Edits)中的各项操作。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的Fsimage文件和一个空的编辑日志。此时,NameNode开始监听DataNode请求。这个过程期间,NameNode一直运行在安全模式,即NameNode的文件系统对于客户端来说是只读的。
2、DataNode启动
系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中。在系统的正常操作期间,NameNode会在内存中保留所有块位置的映射信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的块位置信息之后,即可高效运行文件系统。
3、安全模式退出判断
如果满足“最小副本条件”,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
dfs.namenode.name.dir</name>
file:///${hadoop.tmp.dir}/name1,file:///${hadoop.tmp.dir}/name2</value>
</property>
(2)停止集群,删除data和logs中所有数据。 [user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
[user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
[user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
(3)格式化集群并启动。 [user01@node01 hadoop-3.1.3]$ bin/hdfs namenode –format
[user01@node01 hadoop-3.1.3]$ sbin/start-dfs.sh
dfs.namenode.heartbeat.recheck-interval</name>
300000</value>
</property>
dfs.heartbeat.interval</name>
3</value>
</property>
服役新DataNode节点
[user01@node04 hadoop-3.1.3]$ hdfs --daemon start datanode
[user01@node04 hadoop-3.1.3]$ yarn --daemon start nodemanager
如果数据不均衡, 可以用命令实现集群的再平衡。[user01@node01 hadoop-3.1.3]$ sbin/start-balancer.sh
退役旧DataNode节点
白名单用于确定允许访问NameNode的DataNode节点,内容配置一般与workers文件内容一致。 黑名单用于在集群运行过程中退役DataNode节点。
node01
node02
node03
node04
(3) 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts配置
dfs.hosts</name>
/opt/module/hadoop-3.1.3/etc/hadoop/whitelist</value>
</property>
dfs.hosts.exclude</name>
/opt/module/hadoop-3.1.3/etc/hadoop/blacklist</value>
</property>
(4) 将hdfs-site.xml配置文件分发至所有DataNode节点node04
(2) 刷新NameNode和NodeManager状态hdfs dfsadmin -refreshNodes
yarn dfsadmin -refreshNodes
hdfs dfsadmin -refreshNodes
DataNode多目录配置
dfs.datanode.data.dir</name>
file:///${hadoop.tmp.dir}/data1,file:///${hadoop.tmp.dir}/data2</value>
</property>
(2)停止集群,删除data和logs中所有数据。[user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
[user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
[user01@node01 hadoop-3.1.3]$ rm -rf data/ logs/
(3)格式化集群并启动。[user01@node01 hadoop-3.1.3]$ bin/hdfs namenode –format
[user01@node01 hadoop-3.1.3]$ sbin/start-dfs.sh
一个文件块会占用NameNode150字节的内存。当HDFS中存入大量小文件时, 磁盘存储效率降低, 且会耗尽NameNode大量内存。
解决方法一 : 采用har归档方式,将小文件归档
HDFS存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少NameNode内存使用的同时,允许对文件进行透明的访问。具体说来,HDFS存档文件对内还是一个一个独立文件,对NameNode而言却是一个整体,减少了NameNode的内存。
mapreduce.job.jvm.numtasks</name>
10</value>
How many tasks to run per jvm,if set to -1 ,there is no limit</description>
</property>
未完…