如果让我们自己来设计一个分布式文件系统,咋办?
下图是普通分布式文件系统
一个文件有多少个块:文件大小/块的大小
950m的文件,100m的块大小
总共块的个数:10
最后一个块的占用空间:50m,其他占用的空间为100m
快的大小在创建文件的时候可以指定,不指定则使用系统默认的设置,在Hadoop2.x里一个块对应的磁盘上的空间:128m
HDFS有主从架构。HDFS集群由一个NameNode组成,它是一个主服务器,管理文件系统名称空间并管理客户机对文件的访问。此外,还有许多datanode,通常每个节点一个,管理连接到它们运行的节点的存储。HDFS公开一个文件系统名称空间,并允许用户数据存储在文件中。在内部,文件被分割成一个或多个块,这些块存储在一组数据节点(datanode)中。NameNode执行文件系统操作,如打开、关闭和重命名文件和目录。它还确定块到datanode的映射。datanode负责服务来自文件系统客户端的读写请求。根据NameNode的指令,datanode还执行块的创建、删除和复制。
上图中
NN:
1)负责客户端请求的响应
2)负责元数据(文件的名称、副本系数、Block存放的DN)的管理DN:
1)存储用户的文件对应的数据块(Block)
2)要定期向NN发送心跳信息,汇报本身及其所有的block信息,健康状况
一个典型的部署是一台机器运行一个namenade,集群中的其他机器都运行一个DataNode。该体系结构不排除在同一台机器上运行多个DataNode
但在实际部署中却很少出现这种情况。
作用:
存储结构:
启动过程:
创建的时候指定文件格式
ALTER TABLE table_name SET FILEFORMAT PARQUET;
CREATE TABLE table_name (x INT, y STRING) STORED AS PARQUET;
单独指定默认的文件格式
SET hive.default.fileformat=Orc
HDFS支持传统的分层文件组织。用户或应用程序可以在这些目录中创建目录并存储文件。文件系统名称空间层次结构与大多数现有文件系统相似;可以创建和删除文件,将文件从一个目录移动到另一个目录,或者重命名文件。
NameNode维护文件系统名称空间。对文件系统名称空间或其属性的任何更改都由NameNode记录。应用程序可以指定由HDFS维护的文件的副本数量。一个文件的副本数量称为该文件的副本因子。这些信息由NameNode存储。
数据副本
HDFS被设计为在大型集群中的机器之间可靠地存储非常大的文件。它以块序列的形式存储每个文件。为了容错,复制文件的块。每个文件的块大小和复制因子都是可配置的。
除了最后一个块之外,文件中的所有块大小都相同。
应用程序可以指定文件的副本数量。副本因子可以在文件创建时指定,以后可以更改。HDFS中的文件只写一次(除了追加和截断之外),并且在任何时候都有一个写入者。
NameNode就块的复制做出所有决定。它定期从集群中的每个datanode接收心跳和数据块报告。接收到心跳表示DataNode正常工作。块报表包含datanode上所有块的列表。
上图代表数据中心,两个机架,黄色代表客户端所在的节点(默认三个副本)
第一个副本存放在同client的节点上面;
第二个副本存放在不同第一个副本机架的随意一个节点;
第三个副本存放在与第二个副本相同机架的另一个节点上;
如果只有一个机架,则在不同节点存储;如果高于三个副本则高于三的随意挑选机架和节点。
官网安装文档
Hadoop伪分布式安装步骤
http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html
下载Hadoop
http://archive.cloudera.com/cdh5/cdh/5/
2.6.0-cdh5.7.0
或则 wget http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.7.0.tar.gz
jdk安装
解压:tar -zxvf jdk-7u79-linux-x64.tar.gz -C ~/app
添加到系统环境变量: ~/.bash_profile
export JAVA_HOME=/home/hadoop/app/jdk1.7.0_79
export PATH=$JAVA_HOME/bin:$PATH
使得环境变量生效: source ~/.bash_profile
验证java是否配置成功: java -v
安装ssh
sudo yum install ssh
ssh-keygen -t rsa
cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
下载并解压hadoop
下载:直接去cdh网站下载
解压:tar -zxvf hadoop-2.6.0-cdh5.7.0.tar.gz -C ~/app
hadoop配置文件的修改(hadoop_home/etc/hadoop)
hadoop-env.sh
export JAVA_HOME=/home/hadoop/app/jdk1.7.0_79
core-site.xml
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop000:8020</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/app/tmp</value>
</property>
hdfs-site.xml
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
配置文件路径:$HADOOP_HOME$/etc/hadoop
主要配置文件:
•hdfs-site.xml
•core-site.xml
大数据量下的namenode问题:
解决方案:
内存结构
内存预估
文件元数据对象约占200byte,block元数据约占180byte:
总内存 =198 * num(Directory + Files) + 176 * num(blocks) + 2% * 总内存
场景:
方案:
原因:长期运行的集群增删节点,节点增删磁盘等
影响:
方案:
参数:
整体空间占用:30%
threshold: 10
balance之后每个dn的空间占用:20-40%
dn1:60% ;dn2:10%;dn3:30%
整体:
dn1:40% ;dn2:30%;dn3:30%
建议:
其他管理命令
复制策略:1tb的数据需要3tb的磁盘空间。
纠删码:只需要复制策略50%的磁盘空间,而且同样可以保存数据的可靠性。
原理解释
X1=1
X2=2
X3=3
X1+X2+X3=6
X1+2*X2+3*X3=14
X1+3*X2+4*X3=14
我们需要求出X1,X2,X3的值,那么我们最少需要的方程是? 3
如果有四个方程,允许其中丢失任意一个方程
X1=1
X2=2
X3=3
视为我们的数据
X1+X2+X3=6
X1+2*X2+3*X3=14
X1+3*X2+4*X3=14
视为一个校验/冗余数据
如果是复制策略:要允许任意2份数据丢失,我们需要:3*3=9份数据。恢复的时候只需要找到相同的block直接复制就可以。
如果是纠删码策略:要允许任意2份数据丢失,我们需要:3+2=5份数据,时间换空间的策略。恢复的时候需要的时间大;因为各个block都要参与进来。
一个文件有n个块,最少需要的数据块数是多少?n+2;
不是针对一个文件所有的block块进行纠删码的计算,而是按照一定的size切分成block group,按照block group来计算冗余块。(平衡减少时间)
hdfs ec [COMMAND]
[-listPolicies]
[-addPolicies -policyFile ]
[-getPolicy -path ]
[-removePolicy -policy ]
[-setPolicy -path [-policy ] [-replicate]]
[-unsetPolicy -path ]
[-listCodecs]
[-enablePolicy -policy ]
[-disablePolicy -policy ]
[-help ]
将冷门数据以纠删码格式转存,减少空间占用:
• hdfs storagepolicies [COMMAND]
[-listPolicies]
[-setStoragePolicy -path -policy ]
[-getStoragePolicy -path ]
[-unsetStoragePolicy -path ]
[-help ]
• hdfs mover [-p | -f ]
https://blog.csdn.net/bingdianone/article/details/83863115#t15
官网文档参考
http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html#dfs
hdfs dfs等于hadoop fs
[root@hadoop data]# hdfs dfs -ls /
18/11/09 21:40:49 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 1 items
drwxr-xr-x - root supergroup 0 2018-09-02 08:37 /hbase
[root@hadoop data]# hdfs dfs -get /hell.txt
[root@hadoop data]# hdfs dfs -mkdir -p /text/a/b
18/11/09 21:44:57 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop data]# hdfs dfs -ls -R /text
18/11/09 21:46:25 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
drwxr-xr-x - root supergroup 0 2018-11-09 21:44 /text/a
drwxr-xr-x - root supergroup 0 2018-11-09 21:44 /text/a/b
[root@hadoop data]# hdfs dfs -rm -R /text/a/b
[root@hadoop data]# hdfs dfs -put hell.txt /
18/11/09 21:43:16 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop data]# hdfs dfs -ls /
18/11/09 21:43:25 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 2 items
drwxr-xr-x - root supergroup 0 2018-09-02 08:37 /hbase
-rw-r--r-- 1 root supergroup 13 2018-11-09 21:43 /hell.txt
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
</properties>
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<!--添加hadoop依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!--添加单元测试的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
package com.imooc.hadoop.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
/**
* Hadoop HDFS Java API 操作
*/
public class HDFSApp {
public static final String HDFS_PATH = "hdfs://hadoop000:8020";
FileSystem fileSystem = null;
Configuration configuration = null;
/**
* 创建HDFS目录
*/
@Test
public void mkdir() throws Exception {
fileSystem.mkdirs(new Path("/hdfsapi/test"));
}
/**
* 创建文件
*/
@Test
public void create() throws Exception {
FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/a.txt"));
output.write("hello hadoop".getBytes());
output.flush();
output.close();
}
/**
* 查看HDFS文件的内容
*/
@Test
public void cat() throws Exception {
FSDataInputStream in = fileSystem.open(new Path("/hdfsapi/test/a.txt"));
IOUtils.copyBytes(in, System.out, 1024);
in.close();
}
/**
* 重命名
*/
@Test
public void rename() throws Exception {
Path oldPath = new Path("/hdfsapi/test/a.txt");
Path newPath = new Path("/hdfsapi/test/b.txt");
fileSystem.rename(oldPath, newPath);
}
/**
* 上传文件到HDFS
*
* @throws Exception
*/
@Test
public void copyFromLocalFile() throws Exception {
Path localPath = new Path("/Users/rocky/data/hello.txt");
Path hdfsPath = new Path("/hdfsapi/test");
fileSystem.copyFromLocalFile(localPath, hdfsPath);
}
/**
* 上传文件到HDFS
*/
@Test
public void copyFromLocalFileWithProgress() throws Exception {
InputStream in = new BufferedInputStream(
new FileInputStream(
new File("/Users/rocky/source/spark-1.6.1/spark-1.6.1-bin-2.6.0-cdh5.5.0.tgz")));
FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/spark-1.6.1.tgz"),
new Progressable() {
public void progress() {
System.out.print("."); //带进度提醒信息
}
});
IOUtils.copyBytes(in, output, 4096);
}
/**
* 下载HDFS文件
*/
@Test
public void copyToLocalFile() throws Exception {
Path localPath = new Path("/Users/rocky/tmp/h.txt");
Path hdfsPath = new Path("/hdfsapi/test/hello.txt");
fileSystem.copyToLocalFile(hdfsPath, localPath);
}
/**
* 查看某个目录下的所有文件
*/
@Test
public void listFiles() throws Exception {
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for(FileStatus fileStatus : fileStatuses) {
String isDir = fileStatus.isDirectory() ? "文件夹" : "文件";
short replication = fileStatus.getReplication();
long len = fileStatus.getLen();
String path = fileStatus.getPath().toString();
System.out.println(isDir + "\t" + replication + "\t" + len + "\t" + path);
}
}
/**
* 删除
*/
@Test
public void delete() throws Exception{
fileSystem.delete(new Path("/"), true);
}
@Before
public void setUp() throws Exception {
System.out.println("HDFSApp.setUp");
configuration = new Configuration();
fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, "hadoop");
}
@After
public void tearDown() throws Exception {
configuration = null;
fileSystem = null;
System.out.println("HDFSApp.tearDown");
}
}
首先看看出场的角色,第一个是client客户端,用来发起读写请求,读取HDFS上的文件或往HDFS中写文件;第二个是Namenode,唯一的一个,会协调所有客户端发起的请求;第三个是DataNode,负责数据存储,跟Namenode不一样,DataNode有很多个,有时候能达到数以千计。
优点:
缺点: