Hadoop

Hadoop

大数据简介

最早提出“大数据”时代到来的是全球知名咨询公司麦肯锡,其称∶“数据,已经渗透到当今每一个行业和业务职能领域,成为重要的生产因素。人们对于海量数据的挖掘和运用,预示着新一波生产率增长和消费者盈余浪潮的到来。”

2019年,央视推出了国内首部大数据产业题材纪录片《大数据时代》,节目细致而生动地讲述了大数据技术在政府治理、民生服务、数据安全、工业转型、未来生活等方面给我们带来的改变和影响。

大数据的5v特征

一、Volume:数据量大,包括采集、存储和计算的量都非常大。大数据的起始计量单位至少是P(1000个T)、E(100万个T)或Z(10亿个T)。

二、Variety:种类和来源多样化。包括结构化、半结构化和非结构化数据,具体表现为网络日志、音频、视频、图片、地理位置信息等等,多类型的数据对数据的处理能力提出了更高的要求。

三、Value:数据价值密度相对较低,或者说是浪里淘沙却又弥足珍贵。随着互联网以及物联网的广泛应用,信息感知无处不在,信息海量,但价值密度较低,如何结合业务逻辑并通过强大的机器算法来挖掘数据价值,是大数据时代最需要解决的问题。

四、Velocity:数据增长速度快,处理速度也快,时效性要求高。比如搜索引擎要求几分钟前的新闻能够被用户查询到,个性化推荐算法尽可能要求实时完成推荐。这是大数据区别于传统数据挖掘的显著特征。

五、Veracity:数据的准确性和可信赖度,即数据的质量。

在这里插入图片描述

简而言之,大数据指非常庞大、复杂的数据集,特别是来自新数据源的数据集,其规模之大令传统数据处理软件束手无策,却能帮助我们解决以往非常棘手的业务难题.

应用领域

  • 电商领域

    ​ 精准广告定位,个性化推荐,大数据杀熟

  • 传媒领域

    ​ 精准营销,才你喜欢,交互推荐

  • 金融领域

    ​ 理财投资,通过对个人的信用评估,风险承担能力评估,集合众多理财产品、推荐响应的投资理财产品。

  • 交通领域

    ​ 拥堵预测、智能红绿灯、导航最优规划

  • 电信领域

    ​ 基站选址优化、舆情监控、客户用户画像

  • 安防领域

    ​ 犯罪预防、天网监控

  • 医疗领域

    ​ 智慧医疗、疾病预防、病源追踪

思考

​ 大数据场景下

​ 1.海量数据如何存储?

​ 2.海量数据如何计算?

1.Hadoop简介

1.1 组件

Hadoop由4部分组成

1)HDFS:(Hadoop Distribute File System)分布式文件系统,海量数据存储解决方案
2)MapReduce:Hadoop的分布式运算编程框架
3)Yarn:分布式资源调度平台和任务监控平台
4)Commons: HADOOP底层技术支持  

主要用来解决:大数据存储,大数据分析. 核心组件:HDFS,MapReduce

1.2 特点

(1) 高可靠性  :Hadoop底层将数据以多个副本的形式存储在不同的机器上,保证数据的安全可靠。
(2) 高扩展性 :当存储hdp集群的存储能力和运算资源不足时,可以横向的扩展机器节点来达到扩容和增强运算能力 。
(3) 高效性 :在MapReduce的思想下能够在节点之间动态地移动运算,且是分布式并行工作的,所以运海量数据非常高效。
(4) 高容错性 : Hadoop能够自动保存数据的多个副本,当有存储数据的节点宕机以后, 会自动的复制副本维持集群中副本的个数 ,并且能够自动将失败的任务重新分配。
(5) 低成本 :hadoop可以运行在廉价的机器上并行工作,达到高效,安全,效率于一身目的。

2.HDFS分布式文件系统

(Hadoop Distribute File System )分布式文件系统,解决了海量数据无法单台机器存储的问题,将海量的数据存储在不同的机器中 ,由HDFS文件系统统一管理和维护, 提供统一的访问目录和API !

2.1 原理简介

存储原理
在这里插入图片描述

访问原理

在这里插入图片描述

2.2 演示案例分析

在这里插入图片描述

2.3 安装

2.3.1 安装环境和整体步骤
环境
	java环境 
	集群中的每台机器的ip  
	主机名
	域名映射
    关闭防火墙 
    时间同步
	ssh免密
步骤 
	上传
	解压
	配置
	分发
	初始化
	启动
2.3.2 安装过程

1.上传

rz 命令上传 将hadoop-3.1.1.tar.gz 上传到/opt/apps

cd /opt/apps
rz 上传

在这里插入图片描述

2.解压

tar -zxvf hadoop-3.1.1.tar.gz 

在这里插入图片描述

在这里插入图片描述

bin 主要是一些可执行文件命令
etc 主要是一些Hadoop配置文件
sbin 超级命令 集群启动
share 共享目录 帮助文档

删除文档 rm -rf /opt/apps/hadoop-3.1.1/share/doc

3.配置

配置JavaHome
/opt/apps/hadoop-3.1.1/etc/hadoop/hadoop-env.sh

vi /opt/apps/hadoop-3.1.1/etc/hadoop/hadoop-env.sh
shift + G 最后一行 o 下一行插入

export JAVA_HOME=/opt/apps/jdk1.8.0_191
配置namenode
/opt/apps/hadoop-3.1.1/etc/hadoop/hdfs-site.xml

vi hdfs-site.xml

将复制的内容粘贴到<configuration></configuration>标签中
注意 先进入到插入模式 按i 再粘贴


   <!-- 集群的namenode的位置  datanode能通过这个地址注册-->
	<property>
	     <name>dfs.namenode.rpc-address</name>
		 <value>linux01:8020</value>
	</property>
	 <!-- namenode存储元数据的位置 -->
	<property>
	     <name>dfs.namenode.name.dir</name>
		 <value>/opt/hdpdata/name</value>
	</property>
	 <!-- datanode存储数据的位置 -->
	<property>
	     <name>dfs.datanode.data.dir</name>
		 <value>/opt/hdpdata/data</value>
	</property>
	 <!-- secondary namenode机器的位置-->
	<property>
		<name>dfs.namenode.secondary.http-address</name>
		<value>linux02:50090</value>
	</property>

4.分发安装包

进入到apps文件夹中 
cd /opt/apps
将hadoop文件远程复制linux02 linux03
scp -r  hadoop-3.1.1  linux02:$PWD
scp -r  hadoop-3.1.1  linux03:$PWD

在这里插入图片描述

5.初始化

进入到hadoop的bin文件夹下 进行初始化   linux01
./hadoop   namenode   -format

初始化后 opt下会多出一个文件夹hdpdata

在这里插入图片描述

在这里插入图片描述

6.启动

单节点启动

先启动namenode
进入到hadoop的sbin目录下   linux01
./hadoop-daemon.sh   start  namenode      单个启动namenode

启动成功后可以访问页面
http://linux01:9870

在这里插入图片描述

启动datanode
进入到hadoop的sbin目录下  linux01 linux02 linux03
./hadoop-daemon.sh  start  datanode 

在这里插入图片描述

启动成功后页面可以看到对应节点的信息

在这里插入图片描述

一键启停

为了方便今后启动我们将sbin目录配置到环境变量中
vi  /etc/profile  
export  JAVA_HOME=/opt/apps/jdk1.8.0_191
export  HADOOP_HOME=/opt/apps/hadoop-3.1.1
export  PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
保存后 
source /etc/profile
配置集群文件  hadoop的etc/hadoop/目录下workers 告知有哪些机器
vi workers
linux01
linux02
linux03

修改hadoop的sbin下的 start-dfs.sh stop-dfs.sh
vi start-dfs.sh 
在第一行后插入
HDFS_DATANODE_USER=root
HDFS_DATANODE_SECURE_USER=hdfs 
HDFS_NAMENODE_USER=root 
HDFS_SECONDARYNAMENODE_USER=root

vi stop-dfs.sh
在第一行后插入
HDFS_DATANODE_USER=root
HDFS_DATANODE_SECURE_USER=hdfs 
HDFS_NAMENODE_USER=root 
HDFS_SECONDARYNAMENODE_USER=root

配置后 可以在任意目录下 
stop-dfs.sh 停止
start-dfs.sh 启动

在这里插入图片描述

在这里插入图片描述

2.4 常用客户端命令

hdfs dfs  回车 查看指令

[root@linux01 hadoop-3.1.1]# hdfs dfs
Usage: hadoop fs [generic options]
        [-appendToFile <localsrc> ... <dst>]
        [-cat [-ignoreCrc] <src> ...]         
        [-checksum <src> ...]
        [-chgrp [-R] GROUP PATH...]
        [-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]   
        [-chown [-R] [OWNER][:[GROUP]] PATH...]
        [-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>] 
        [-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
        [-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]
        [-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]  
        [-createSnapshot <snapshotDir> [<snapshotName>]]
        [-deleteSnapshot <snapshotDir> <snapshotName>]
        [-df [-h] [<path> ...]]
        [-du [-s] [-h] [-v] [-x] <path> ...]
        [-expunge]
        [-find <path> ... <expression> ...]
        [-get [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
        [-getfacl [-R] <path>]
        [-getfattr [-R] {-n name | -d} [-e en] <path>]
        [-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
        [-head <file>]
        [-help [cmd ...]]
        [-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
        [-mkdir [-p] <path> ...]
        [-moveFromLocal <localsrc> ... <dst>]
        [-moveToLocal <src> <localdst>]
        [-mv <src> ... <dst>]
        [-put [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
        [-renameSnapshot <snapshotDir> <oldName> <newName>]
        [-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
        [-rmdir [--ignore-fail-on-non-empty] <dir> ...]
        [-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
        [-setfattr {-n name [-v value] | -x name} <path>]
        [-setrep [-R] [-w] <rep> <path> ...]
        [-stat [format] <path> ...]
        [-tail [-f] <file>]
        [-test -[defsz] <path>]
        [-text [-ignoreCrc] <src> ...]
        [-touchz <path> ...]
        [-truncate [-w] <length> <path> ...]
        [-usage [cmd ...]]
很多指令和linux相同 我们在使用时 只要前面加上hdfs  dfs 即可 
例如 创建文件夹
hdfs dfs -mkdir -p /aaa/bbb/ccc
但是我们发现 这样创建 只是在本机上创建了文件夹 并没有在分布式文件系统上创建 
如果想在分布式文件系统上创建  
hdfs dfs -mkdir -p hdfs://linux01:8020/aaa/bbb/ccc

由于默认是在本机上操作 我们如果使用指令每次都需要加上 hdfs://linux01:8020
比较麻烦 那么可以进行配置 将默认操作设置为 分布式文件系统上 
修改hadoop的etc/hadoop下的文件 core-site.xml
将下面内容放入到<configuration></configuration>标签中
  <property>
     <name>fs.defaultFS</name>
     <value>hdfs://linux01:8020</value>
  </property>
这样 我们就不需要每次都加上hdfs://linux01:8020了
hdfs dfs -mkdir -p /zs/li/ww


创建文件夹 
hdfs dfs -mkdir -p /abc
创建文件
hdfs dfs -touchz /abc/a.txt
查看
hdfs dfs -ls /

本地创建文件 
echo  "hello" >> b.txt 
将b.txt上传到 分布式文件系统上 /abc下
hdfs dfs  -put    本地文件  目标目录
hdfs dfs  -put   b.txt    /abc/
hdfs dfs  -put   b.txt   /abc/aa.txt   上传并改名

将 /abc/aa.txt 下载到本地根目录下
hdfs  dfs -get  数据源  数据目的
hdfs dfs  -get   /abc/aa.txt  /   
hdfs  dfs -get   /abc/aa.txt   /cc.txt  下载并改名

修改abc文件夹的的权限  
hdfs dfs -chmod  777  /abc

删除abc文件夹的 a.txt
hdfs dfs -rm -r /abc/a.txt
删除abc文件夹下的所有内容
hdfs dfs -rm -r /abc/*

2.5 节点交互原理

我们将JDK压缩上传到文件系统上
tar -zcvf my.tar.gz  jdk1.8.0_191
创建一个文件夹
hdfs dfs -mkdir -p /java/se
上传
hdfs dfs -put my.tar.gz /java/se

在这里插入图片描述

这个文件的总大小为182.87MB 我们说HDFS会进行分块存储 每块大小默认128MB 那么这个文件存储时应该应该分为两块,一个为128MB,一个为54.87MB
文件的存储位置为 
/opt/hdpdata/data/current/BP-2080298671-192.168.133.33-1638430574514/current/finalized/subdir0/subdir0

linux01 linux02 linux03 都是相同位置

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.JAVA客户端

3.1 环境搭建

创建Maven项目,添加Hadoop依赖.

<dependencies>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 添加hadoop的依赖  -->
        <!--添加hdfs的客户端依赖-->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-common</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
            <version>3.1.1</version>
        </dependency>
    </dependencies>

3.2 FileSystem使用

核心类 org.apache.hadoop.fs.FileSystem 文件系统类 抽象类

//静态方法创建对象
public static FileSystem newInstance(URI uri,Configuration conf,String user)
/*
     	  参数一  URI  分布式文件系统 HDFS的资源地址 NN地址 hdfs://linux01:8020
          参数二  Configuration  用户自定义参数设置   副本数 3   物理切块的大小 128M
          		 
          参数三  user  客户端用户名      
 */                        
/*
      org.apache.hadoop.fs.FileSystem  文件系统类  抽象类
         静态方法获取对象(子类对象)
              public static FileSystem newInstance(URI uri,Configuration conf,String user)
                    URI uri: 统一资源标识符   协议://
                                url统一资源定位符   www.baidu.com
                                只要是网络相关的 都是uri包括url
                                迅雷下载 百度网盘 邮件发送 mailto:  jdbc连接
                             分布式文件系统 HDFS的资源地址 NN地址
                             hdfs://linux01:8020
                             构造方法
                                  public URI uri
                   Configuration conf:用户自定义参数设置   副本数 3   物理切块的大小 128M
                                      如果不设置 使用默认设置
                   String user: 用户名  root
 */
public class Demo01_FileSystem {
    public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
        //文件系统的客户端连接对象
        URI  uri = new URI("hdfs://linux01:8020");
        //配置对象 没有进行配置 使用默认配置
        Configuration  con = new Configuration();
        //用户名
        String user = "root";
        //通过静态方法 获取 分布式文件系统对象
        FileSystem fs = FileSystem.newInstance(uri, con, user);
        System.out.println(fs);
    }
}

常用方法

public void copyFromLocalFile(Path src, Path dst)  将本地文件上传到文件系统
public void copyFromLocalFile(boolean delSrc, Path src, Path dst)
public void copyToLocalFile(Path src, Path dst)    将文件系统上的文件下载到本地
public void copyToLocalFile(boolean delSrc, Path src, Path dst)
public FileStatus[] listStatus(Path f) 列出目录下所有的内容 包括文件  文件夹
public RemoteIterator<LocatedFileStatus> listFiles(Path f, boolean recursive)列出目录下所有的文件
public FSDataOutputStream create(Path f, boolean overwrite)  获取字节输出流 向文件中写数据
public FSDataOutputStream append(Path path) 向指定的文件路径中追加写入数据
public FSDataInputStream open(Path f) 获取字节输入流 读取文件中数据
3.2.1 获取文件系统对象工具类
public class HDFSUtils {
    private HDFSUtils(){}

    public static FileSystem  getFileSystem() throws  Exception {
        //文件系统的客户端连接对象
        URI uri = new URI("hdfs://linux01:8020");
        //配置对象 没有进行配置 使用默认配置
        Configuration con = new Configuration();
        //用户名
        String user = "root";
        //通过静态方法 获取 分布式文件系统对象
        FileSystem fs = FileSystem.newInstance(uri, con, user);
        return fs;
    }
}

3.2.2 上传文件
/*
     public void copyFromLocalFile(Path src, Path dst)  将本地文件上传到文件系统
     public void copyFromLocalFile(boolean delSrc, Path src, Path dst)
            boolean delSrc:是否删除源文件  true删除 false则不删除
            Path src:数据源  本地系统上的文件
            Path dst:数据目的 文件系统

     1.创建FileSystem对象
     2.调用方法上传文件到分布式文件系统
     3.关闭资源
 */
public class Demo02_FileSystem {
    public static void main(String[] args) throws Exception {
        //通过工具类获取对象
        FileSystem fileSystem = HDFSUtils.getFileSystem();

        /*
            public void copyFromLocalFile(Path src, Path dst)  将本地文件上传到文件系统
            数据源:本地文件  d:\\mm.jpg
            数据目的:分布式文件系统  /
         */
      //上传并改名
        Path  src = new Path("d:\\mm.jpg");
        Path  dest = new Path("/java/meimei.jpg");

        fileSystem.copyFromLocalFile(true,src,dest);

        fileSystem.close();    
    }
}

3.2.3 下载文件
/*
    将文件系统上的文件下载到本地
    public void copyToLocalFile(Path src, Path dst)
    public void copyToLocalFile(boolean delSrc, Path src, Path dst)

                boolean delSrc: 是否删除数据源  true删除 false不删除
                Path src:数据源 文件系统
                Path dst:数据目的 本地

    注意:由于win和HDFS分布式兼容不好 需要安装Hadoop环境 不安装不能下载文件
        解压 hadoop3.1.1 配置环境变量HADOOP_HOME 加入Path
        需要重启idea  可能需要重启电脑
 */
public class Demo03_FileSystem {
    public static void main(String[] args) throws Exception {
        //通过工具类获取对象
        FileSystem fs = HDFSUtils.getFileSystem();


//        Path  src = new Path("/mm.jpg");
//        Path  dest = new Path("d:\\");
        //将文件系统中的文件 下载到本地  自动生成.crc的检验文件
//        fs.copyToLocalFile(src,dest);

        Path  src = new Path("/mm.jpg");
        Path  dest = new Path("d:\\meimei.jpg");
        下载并改名
      //  fs.copyToLocalFile(src,dest);

        /*
            public void copyToLocalFile(boolean delSrc, Path src, Path dst, boolean useRawLocalFileSystem)
            参数1:是否删除源文件
            参数2:数据源
            参数3:数据目的
            参数4:是否使用本地文件系统 true则不生成.crc文件
         */
        fs.copyToLocalFile(false,src,dest,true);
        fs.close();
    }
}
3.2.3 遍历文件
/*
    public FileStatus[] listStatus(Path f) 列出指定目录下所有的内容 包括文件  文件夹
 */
public class Demo04_FileSystem {
    public static void main(String[] args) throws Exception {
        //获取对象
        FileSystem fs = HDFSUtils.getFileSystem();
        //获取根目录下所有内容 文件 文件夹
        FileStatus[] fileStatusArr = fs.listStatus(new Path("/"));
        //增强for循环遍历
        for(FileStatus file : fileStatusArr){
//            System.out.println(file);
            //判断是否是文件夹
            boolean b = file.isDirectory();
            //判断是否是文件
            boolean b2 = file.isFile();
            //获取文件路径
            Path path = file.getPath();
            if(b){
                System.out.println("文件夹:"+path);
            }else{
                System.out.println("文件:"+path);
            }
        }
    }
}

/*
    public RemoteIterator<LocatedFileStatus> listFiles(Path f, boolean recursive)列出目录下所有的文件 只获取文件
                方法参数
                    Path f:路径
                    boolean recursive:是否递归遍历  true false

                方法返回值
                    RemoteIterator 迭代器
                          hasNext()  判断是否有元素
                          next()  获取元素

              
                    LocatedFileStatus  分布式文件系统上的文件对象 可以获取文件的信息
                                       获取文件大小  副本个数  block块个数 大小等等

                                 Path getPath() 获取路径
                                 long getLen()  获取文件的字节数
                                 short getReplication()获取副本个数
                                 long  getBlockSize() 获取block块的大小
                                 BlockLocation[] getBlockLocations() 获取block块数组

                    BlockLocation  物理切块对象
                           String[]   getHosts()  获取block块在主机的位置
                           String[]   getNames()  获取block块在主机的名称端口
                           long   getLenth()  获取每个切块的大小
                           long   getOffset() 获取偏移量
 */
public class Demo05_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fs = HDFSUtils.getFileSystem();

        //调用方法获取根目录下的所有内容
        RemoteIterator<LocatedFileStatus> it = fs.listFiles(new Path("/"), true);
        while (it.hasNext()) {
            LocatedFileStatus file = it.next();
//            System.out.println(file.getPath());

            //获取路径
            Path path = file.getPath();
            //获取文件的字节数
            long len = file.getLen();
            System.out.println("文件大小:" + len * 1.0 / 1024 / 1024 + "M");
            //获取文件的副本
            short s = file.getReplication();
            System.out.println("副本个数:" + s);
            //获取block大小
            long blockSize = file.getBlockSize();
            System.out.println("block块大小:" + blockSize * 1.0 / 1024 / 1024 + "M");

            //获取block的数组
            BlockLocation[] blockLocations = file.getBlockLocations();
            System.out.println("block块个数:" + blockLocations.length);


            //遍历物理切块数组
            for (BlockLocation b : blockLocations) {
                //获取物理切块的主机地址
                String[] hosts = b.getHosts();
                System.out.println(Arrays.toString(hosts));
                String[] names = b.getNames();
                System.out.println(Arrays.toString(names));
                long length = b.getLength();
                System.out.println("block大小:" + length * 1.0 / 1024 / 1024 + "M");
                long offset = b.getOffset();
                System.out.println("偏移量:" + offset);

            }
            System.out.println("------------------------------");
        }

    }
}
3.2.4 写数据
/*
    public FSDataOutputStream create(Path f, boolean overwrite)  获取字节输出流 向文件中写数据
                boolean overwrite:如果文件存在 是否覆盖  true 覆盖 false不覆盖
 */
public class Demo06_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fs = HDFSUtils.getFileSystem();

        //获取写数据的字节输出流
        FSDataOutputStream out = fs.create(new Path("/1.txt"), true);

        out.write("hello world\r\n".getBytes());

        out.write("hello boys  ".getBytes());
        out.write("hello girls  ".getBytes());

        out.close();
        fs.close();
    }
}

/*
    追加写入数据
      FSDataOutputStream append(Path path) 向指定的文件路径中追加写入数据
 */
public class Demo07_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fs = HDFSUtils.getFileSystem();

        //向文件中追加写入数据
        FSDataOutputStream out = fs.append(new Path("/1.txt"));
        out.write("hello  aaa\r\n".getBytes());
        out.write("hello  bbb\r\n".getBytes());
        out.write("hello  ccc\r\n".getBytes());

        out.close();
        fs.close();

    }
}
3.2.5 读数据
/*
    public FSDataInputStream open(Path f) 获取字节输入流 读取文件中数据
 */
public class Demo08_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fs = HDFSUtils.getFileSystem();

        //获取字节输入流 读取文件中数据
        FSDataInputStream in = fs.open(new Path("/1.txt"));

//        byte[] bytes = new byte[1024];
//        int len =0;
//        while((len = in.read(bytes))!=-1){
//            System.out.println(new String(bytes,0,len));
//
//        }
        //一行一行读取数据
        InputStreamReader isr = new InputStreamReader(in);
        //创建缓冲流
        BufferedReader br = new BufferedReader(isr);
//        String s = br.readLine();
        String line = null;
        while((line = br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
        fs.close();

    }
}
/*
	seek和skip方法补充
 */
public class Demo09_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fs = HDFSUtils.getFileSystem();

        //获取字节输入流 读取文件中数据
        FSDataInputStream in = fs.open(new Path("/1.txt"));

        //跳过几个字节
       // in.skip(1);
	    //指定指针标记读取
        in.seek(0);

        int read = in.read();
        System.out.println(read);

        in.close();

        fs.close();
    }
}
3.2.6 其他方法
//DistributedFileSystem
public class Demo10_FileSystem {
    public static void main(String[] args) throws Exception {
        FileSystem fileSystem = HDFSUtils.getFileSystem();

        fileSystem.delete(new Path("/1.txt"),true);  //删除
        fileSystem.mkdirs(new Path("/aaa/bbb") ); //创建文件夹
        fileSystem.exists(new Path("/aaa/1.txt")); //判断路径是否存在

        //...
      fileSystem.close();
    }
}

3.2.7 小文件合并

由于Hadoop擅长存储大文件,因为大文件的元数据信息比较少,如果Hadoop集群当中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成大文件进行一起处理在我们的HDFS的shell命令模式下,可以通过命令行将很多的hdfs文件合并成一个大文件下载到本地.

 hdfs dfs -getmerge /aaa/* ./abc.txt  

既然可以在下载的时候将这些小文件合并成一个大文件一起下载,那么肯定就可以在上传的时候将小文件合并到一个大文件里面去

     FileSystem fs = HDFSUtils.getFS();

        FSDataOutputStream out = fs.create(new Path("/aaa/big.txt"));

        LocalFileSystem local = FileSystem.getLocal(new Configuration());
        FileStatus[] fileStatuses = local.listStatus(new Path("file:///d:\\input"));

        for (FileStatus fileStatus : fileStatuses) {

            FSDataInputStream in = local.open(fileStatus.getPath());
            IOUtils.copy(in,out);

            IOUtils.closeQuietly(in);
        }

        IOUtils.closeQuietly(out);

3.3 参数配置

使用Configuration类进行配置

       Configuration conf = new Configuration();
        // 修改存储的副本个数  5  name  value
        conf.set( "dfs.replication", "5");
        // 修改物理切块的大小
        conf.set("dfs.blocksize", "64m");

使用配置文件进行配置

在resources下创建配置文件hdfs-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. See accompanying LICENSE file.
-->

<!-- Put site-specific property overrides in this file. -->
<configuration>
        <property>
             <name>dfs.replication</name>
                 <value>5</value>
        </property>
   		<property>
             <name>dfs.blocksize</name>
                 <value>64m</value>
        </property>
</configuration>

3.4 HDFS文件限额配置

在多人共用HDFS的环境下,配置设置非常重要。特别是在Hadoop处理大量资料的环境,如果没有配额管理,很容易把所有的空间用完造成别人无法存取。Hdfs的配额设定是针对目录而不是针对账号,可以让每个账号仅操作某一个目录,然后对目录设置配置。
hdfs文件的限额配置允许我们以文件个数,或者文件大小来限制我们在某个目录下上传的文件数量或者文件内容总量,以便达到我们类似百度网盘网盘等限制每个用户允许上传的最大的文件的量。

hdfs dfs -count -q -h /aaa    #查看配额信息

数量限额

hdfs dfs -mkdir -p /aaa
hdfs dfsadmin -setQuota 2 /aaa #给该文件夹下设置最多上传两个文件 ,发现只能上传一个
hdfs dfsadmin -clrQuota /aaa # 清除限额

空间大小限额

在设置空间配额时,设置空间至少是block_size*3大小

hdfs dfsadmin -setSpaceQuota 4k /aaa #限制空间大小4KB
hdfs dfs -put  a.txt /aaa  #上传失败

生成任意大小文件命令

dd if=/dev/zero of=1.txt bs=1M count=2 #生成任意大小文件  bs*count

清除空间配额限制

hdfs dfsadmin -clrSpaceQuota /aaa

3.5 hdfs的安全模式

安全模式是hadoop的一种保护机制,用于保证集群中的数据块的安全性。当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性。
假设我们设置的副本数(即参数dfs.replication)是3,那么在datanode上就应该有3个副本存在,假设只存在2个副本,那么比例就是2/3=0.666。hdfs默认的副本率0.999。我们的副本率0.666明显小于0.999,因此系统会自动的复制副本到其他dataNode,使得副本率不小于0.999。如果系统中有5个副本,超过我们设定的3个副本,那么系统也会删除多于的2个副本。**在安全模式状态下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。**在,当整个系统达到安全标准时,HDFS自动离开安全模式。

dfs.namenode.safemode.threshold-pct  副本率 默认值 0.999f
dfs.namenode.safemode.extension 默认值30000   检查完成后 30秒后退出安全模式

安全模式操作命令

hdfs dfsadmin -safemode get  #查看安全模式状态
hdfs dfsadmin -safemode enter #进入安全模式
hdfs dfsadmin -safemode leave #离开安全模式

4.原理介绍

4.1 上传数据流程

网络拓扑及机架感知

 网络拓扑
	节点距离:两个节点到达共同父节点的距离和

在这里插入图片描述

机架感知 ( 副本节点的选择 )
	例如:500个节点,上传数据my.tar.gz,副本数为3, 
		  根据机架感知,副本数据存储节点的选择。
	
    
   BlockPlacementPolicyDefault 
   官方注释
   the 1st replica is placed on the local machine, 
   otherwise a random datanode. The 2nd replica is placed on a datanode
   that is on a different rack. The 3rd replica is placed on a datanode
   which is on a different node of the rack as the second replica.

在这里插入图片描述

上传流程

在这里插入图片描述

Block
HDFS中的文件在物理上是分块存储的,即分成Block;
block在Hadoop不同版本大小不同:
Hadoop1.x:64M
Hadoop2.x:128M


Pipeline,中文翻译为管道。这是HDFS在上传文件写数据过程中采用的一种数据传输方式。
客户端将数据块写入第一个数据节点,第一个数据节点保存数据之后再将块复制到第二个数据节点,后者保存后将其复制到第三个数据节点。

为什么datanode之间采用pipeline线性传输,而不是一次给三个datanode拓扑式传输呢?
因为数据以管道的方式,顺序的沿着一个方向传输,这样能够充分利用每个机器的带宽,避免网络瓶颈和高延迟时的连接,最小化推送所有数据的延时。

ACK (Acknowledge character )即是确认字符,在数据通信中,接收方发给发送方的一种传输类控制字符。表示发来的数据已确认接收无误。


Packet
Packet是Client端向Datanode,或者DataNode的PipLine之间传输数据的基本单位,默认64kB.
Chunk
Chunk是最小的Hadoop中最小的单位,是Client向DataNode或DataNode的PipLne之间进行数据校验的基本单位,默认512Byte,因为用作校验(自己校验自己),故每个chunk需要带有4Byte的校验位。
所以世纪每个chunk写入packet的大小为516Byte,真实数据与校验值数据的比值为128:1。

4.2 下载数据流程

在这里插入图片描述

 IO操作过程中难免会出现数据丢失或脏数据,数据传输得量越大出错得几率越高。校验错误最常用得办法就是传输前计算一个校验和,传输后计算一个校验和,两个校验和如果不相同就说明数据存在错误,比较常用得错误校验码是CRC32.
 hdfs写入的时候计算出校验和,然后每次读的时候再计算校验和。要注意的一点是,hdfs每固定长度就会计算一次校验和,这个值由io.bytes.per.checksum指定,默认是512字节。

DataNode节点如何保证数据的完整性

1.当datanode读取Block的时候,会计算CheckSum
2.如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏
3.客户端读取其他DataNode上的Block
4.常见的校验算法crc,md5等 
5.DataNode在其文件创建后周期验证CheckSum(DataNode后台进程(DataBlockScanner))

如果客户端发现有block坏掉呢,会怎么恢复这个坏的块,主要分几步:

 1.客户端在抛出ChecksumException之前会把坏的block和block所在的datanode报告给namenode
 2.namenode把这个block标记为已损坏,这样namenode就不会把客户端指向这个block,也不会复制这个block到其他的datanode。
 3.namenode会把一个好的block复制到另外一个datanode
 4.namenode把坏的block删除掉

4.3 元数据信息管理

4.3.1 什么是元数据信息
元数据:描述数据的数据信息
HDFS中元数据分为两种 
	1.描述文件自身属性的元数据信息
	2.描述文件与block之间映射的元数据信息

在这里插入图片描述

4.3.2 如何管理元数据信息

​ 元数据信息就是虚拟目录的一个映射关系,如何保存这些信息.按存储形式分为内存元数据元数据文件两种,分别存在内存磁盘上。

内存元数据

为了保证用户操作元数据交互高效,延迟低,NameNode把所有的元数据都存储在内存中,我们叫做内存元数据。内存中的元数据是最完整的,包括文件自身属性信息文件块位置映射信息。但是内存的致命问题是,断点数据丢失,数据不会持久化。因此NameNode又辅佐了元数据文件来保证元数据的安全完整。

元数据文件

有两种:fsimage镜像文件,edit编辑日志

fsimage镜像文件:是内存元数据的一个持久化的检查点。但是fsimage中仅包含Hadoop文件系统中文件自身属性相关的元数据信息,但不包含文件块位置的信息。文件块位置信息只存储在内存中,是由datanode启动加入集群的时候,向namenode进行数据块的汇报得到的,并且后续间断指定时间进行数据块报告。

持久化的动作是数据从内存到磁盘的IO过程,并且fsimae文件会很大(GB级别的很常见)。如果所有的更新操作都往fsimage文件中添加,这样会导致系统运行的十分缓慢会.对namenode正常服务造成一定的影响,不能频繁的进行持久化。

edit编辑日志:为了避免两次持久化之间数据丢失的问题,又设计了Edits log编辑日志文件。文件中记录的是HDPS所有更改操作(文件创建,删除或修改)的日志,文件系统客户端执行的更改操作首先会被记录到edits文件中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuKHcENE-1683629112695)(img\23.png)]

当HDFS集群运行一段时间后,就会出现下面一些问题:

1. edits logs因操作记录过多会变的很大,
2. fsimage因间隔时间长将会变得很旧;
3. namenode重启会花费很长时间,因为有很多改动要
   从edits log合并到fsimage文件上;
4. 如果频繁进行fsimage持久化,又会影响NN正常服
   务,毕竟I0操作是-种内存到磁盘的耗精力操作.

因此为了克服上述问题,需要一个易于管理的机制来帮助我们减小edit logs文件的大小和得到一个最新的fsimage文件,这样也会减小在NameNode,上的压力。

checkpoint机制

通过Secondary Namenode,每隔一段时间将Name Node的元数据更新并备份,然后返回fsimage给Name Node,供其下次启动时读取.

当集群启动时,NN和SNN都会启动,NN启动后会读取最新的fsimage文件,读到较新的元数据信息,同时还会读取最新的日志信息,根据日志信息的内容“回滚”上一次开机时的操作信息,这样即可保证当前的元数据信息是完整正确的

​ SNN会隔一段时间就去NN下载其fsimage文件和众多edits文件,下载到SNN的本机上,然后将fsimage反序列化到内存中,同时“回放”众多日志文件中的操作信息,更新补全元数据,元数据更新完毕后,SNN就会将该元数据对象序列化到本地磁盘中,然后再将该元数据对象发送给NN,供其下一次开机读取

checkpoint过程

在这里插入图片描述

checkpint相关参数设置(了解)

core-site.xml
dfs.namenode.checkpoint.period
--两次检查点创建之间的固定时间间隔,默认3600,即1小时。

dfs.namenode.checkpoint.txns
--未检查的事务数量。若没检查事务数达到这个值,也触发一次checkpoint,1,000,000。

dfs.namenode.checkpoint.check.period
--standby namenode检查是否满足建立checkpoint的条件的检查周期。默认60,即每1min检查一次。

dfs.namenode.num.checkpoints.retained
--在namenode上保存的fsimage的数目,超出的会被删除。默认保存2个。

dfs.namenode.num.checkpoints.retained
--最多能保存的edits文件个数,默认为1,000,000.

dfs.ha.tail-edits.period
--standby namenode每隔多长时间去检测新的Edits文件。只检测完成了的Edits, 不检测inprogress的文件。

查看FSImage和edit文件

cd /opt/hdpdata/name/current
hdfs oiv -p 文件类型  -i镜像文件  -o转换后的文件输出路径
hdfs oiv -p XML -i fsimage_0000000000000000434 -o /opt/fsimage.xml
hdfs oev -p XML -i edits_inprogress_0000000000000000695 -o /opt/edit.xml

HDFS缺点

1.不适合低延时数据访问,比如毫秒级的存储数据.
2.无法高效的对大量小文件进行存储
	  1.存储大量小文件的话,它会占用namenode大量内存来存储文件目录和块信息,而namenode的内存是有限的.
	  	一个block的需要存储的信息 150个字节
	  2.小文件存储的寻址时间会超过读取文件的时间,违反HDFS的设计目标
3.不支持并发写入,文件随机修改
	  1.一个文件只能有一个写,不允许多线程同时写
	  2.仅支持数据append,不支持文件的随机修改
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值