大数据入门学习笔记(叁)- 布式文件系统HDFS

文章目录

  • HDFS概述及设计目标
    • 什么是HDFS
    • HDFS前提和设计目标
  • HDFS 构成及工作原理解析
    • 基本构成
    • 数据读写过程
    • 集群架构
    • namenode深入
    • HDFS HA
    • HDFS文件类型-列式与行式存储
    • HDFS文件类型-常用文件类型
    • HDFS文件类型-如何使用?
  • HDFS副本机制
    • 副本存放策略![在这里插入图片描述](https://img-blog.csdnimg.cn/20181109091413763.png)
  • HDFS环境搭建
  • HDFS关键设置及常见问题
    • 常用配置
    • 常见问题
      • 小文件问题
      • Namenode管理
      • 数据迁移
      • 数据平衡
        • hdfs balancer
  • 如何提高数据存取效率
    • 数据压缩-作用
      • 数据压缩-评价指标
      • 数据压缩-类型
      • 数据压缩-使用场景?
      • 使用建议
    • 纠删码(hadoop3.0之后的功能)
      • 相关命令
      • 使用建议
    • 异构存储
      • 相关命令
      • 使用建议
  • 代码演示
  • HDFS shell
    • HDFS shell常用命令的使用
      • Is
      • get
      • mkdir
      • rm
      • put
  • Java API操作
  • HDFS文件读写流程
    • 文件写流程图解
    • 文件读流程图解
  • HDFS错误处理机制
  • HDFS优缺点

HDFS概述及设计目标

如果让我们自己来设计一个分布式文件系统,咋办?
下图是普通分布式文件系统
大数据入门学习笔记(叁)- 布式文件系统HDFS_第1张图片

什么是HDFS

  • Hadoop实现了一个分布式文件系统( Hadoop Distributed File System) ,简称HDFS
  • 源自Google的GFS论文
  • 发表于2003年,HDFS是GFS的克隆版
  • 基于JAVA实现的一个分布式文件系统
  • 基于unix/linux
  • 是Hadoop最重要的核心组件
  • 支持顺序写入,而非随机定位读写

HDFS前提和设计目标

  • 存储超大文件
    HDFS适合存储大文件,单个文件大小通常在百MB以上
    HDFS适合存储海量文件,总存储量可达PB,EB级
  • 硬件容错
    基于普通机器搭建,硬件错误是常态而不是异常,因此错误检测和快速、自
    动的恢复是HDFS最核心的架构目标
  • 流式数据访问
    为数据批处理而设计,关注数据访问的高吞吐量
  • 简单的一致性模型
    一次写入,多次读取
    一个文件经过创建、写入和关闭之后就不需要改变
  • 本地计算
    将计算移动到数据附近

HDFS 构成及工作原理解析

基本构成

  • 数据块
    • 文件以块为单位进行切分存储,块通常设置的比较大(最小6M,默认
      128M)
    • 块越大,寻址越快,读取效率越高,但同时由于MapReduce任务也是以块为最小单位来处理,所以太大的块不利于于对数据的并行处理
    • 一个文件至少占用一个块(逻辑概念)
  • Namenode与Datanode
    • namenode 负责维护整个文件系统的信息,包括:整个文件树,文件的块分布信息,文件系统的元数据,数据复制策略等
    • datanode 存储文件内容,负责文件实际的读写操作,保持与namenode的通信,同步文件块信息
一个文件有多少个块:文件大小/块的大小
   950m的文件,100m的块大小
   总共块的个数:10
   最后一个块的占用空间:50m,其他占用的空间为100m

快的大小在创建文件的时候可以指定,不指定则使用系统默认的设置,在Hadoop2.x里一个块对应的磁盘上的空间:128m

数据读写过程

  1. 创建一个文件。
  2. 检查是否可以创建文件。
  3. 切分block(可以指定两个参数;不知道则使用默认);然后给block分配写入的datanode。
  4. 返回写入datanode的队列。
  5. 以pipeline方式写入;以packet(里面还有chunk数据单元(chunk里又check sum))形式写入;ack机制往上一层一层汇报写完一个chunk(以chunk为单元进行校验)。
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第2张图片

集群架构


大数据入门学习笔记(叁)- 布式文件系统HDFS_第3张图片
HDFS有主从架构。HDFS集群由一个NameNode组成,它是一个主服务器,管理文件系统名称空间并管理客户机对文件的访问。此外,还有许多datanode,通常每个节点一个,管理连接到它们运行的节点的存储。HDFS公开一个文件系统名称空间,并允许用户数据存储在文件中。在内部,文件被分割成一个或多个块,这些块存储在一组数据节点(datanode)中。NameNode执行文件系统操作,如打开、关闭和重命名文件和目录。它还确定块到datanode的映射。datanode负责服务来自文件系统客户端的读写请求。根据NameNode的指令,datanode还执行块的创建、删除和复制。
大数据入门学习笔记(叁)- 布式文件系统HDFS_第4张图片
上图中

  • 1 个Master(NameNode/NN) 带 N个Slaves(DataNode/DN)
    HDFS/YARN/HBase其实都是一样的
  • 1个文件会被拆分成多个Block理解为:
    blocksize:128M
    130M ==> 2个Block: 128M 和 2M

NN:
1)负责客户端请求的响应
2)负责元数据(文件的名称、副本系数、Block存放的DN)的管理

DN:
1)存储用户的文件对应的数据块(Block)
2)要定期向NN发送心跳信息,汇报本身及其所有的block信息,健康状况

一个典型的部署是一台机器运行一个namenade,集群中的其他机器都运行一个DataNode。该体系结构不排除在同一台机器上运行多个DataNode
但在实际部署中却很少出现这种情况。

namenode深入

作用:

  • Namespace管理:负责管理文件系统中的树状目录结构以及文件与数据块的映射关系
  • 块信息管理:负责管理文件系统中文件的物理块与实际存储位置的映射关系BlocksMap
  • 集群信息管理:机架信息,datanode信息
  • 集中式缓存管理:从Hadoop2.3 开始,支持datanode将文件缓存到内存中,这部分缓存通过NN集中管理

存储结构:

  • 内存: Namespace数据,BlocksMap数据,其他信息
  • 文件:
    • 已持久化的namespace数据:FsImage
    • 未持久化的namespace操作:Edits

启动过程:

  1. 开启安全模式:不能执行数据修改操作
  2. 加载fsimage
  3. 逐个执行所有Edits文件中的每一条操作将操作合并到fsimage,
    完成后生成一个空的edits文件
  4. 接收datanode发送来的心跳消息和块信息
  5. 根据以上信息确定文件系统状态
  6. 退出安全模式
  • 安全模式:文件系统只接受读数据请求,而不接受删除、修改等变更请求
  • 什么情况下进入:NameNode主节点启动时,HDFS进入安全模式
  • 什么时候时候退出:系统达到安全标准时,HDFS退出安全模式
  • dfs.namenode.safemode.min.datanodes: 最小可用datanode数量
  • dfs.namenode.safemode.threshold-pct: 副本数达到最小要求的block占系统总block数的百分比
  • dfs.namenode.safemode.extension: 稳定时间
  • 相关命令:
  • hdfs dfsadmin -safemode get:查看当前状态
  • hdfs dfsadmin -safemode enter:进入安全模式
  • hdfs dfsadmin -safemode leave:强制离开安全模式
  • hdfs dfsadmin -safemode wait:一直等待直到安全模式结束

HDFS HA

  • Datanode: 通过数据冗余保证数据的可用性
  • Namenode: 在2.0以前存在SPOF风险,从2.0之后:
  1. 把name.dir指向NFS(Network File System)
  2. QJM 方案

大数据入门学习笔记(叁)- 布式文件系统HDFS_第5张图片

HDFS文件类型-列式与行式存储

HDFS 支持任意文件格式主要分为列式与行式
大数据入门学习笔记(叁)- 布式文件系统HDFS_第6张图片

HDFS文件类型-常用文件类型

大数据入门学习笔记(叁)- 布式文件系统HDFS_第7张图片

HDFS文件类型-如何使用?

创建的时候指定文件格式
ALTER TABLE table_name SET FILEFORMAT PARQUET;
CREATE TABLE table_name (x INT, y STRING) STORED AS PARQUET;

单独指定默认的文件格式
SET hive.default.fileformat=Orc

HDFS副本机制

HDFS支持传统的分层文件组织。用户或应用程序可以在这些目录中创建目录并存储文件。文件系统名称空间层次结构与大多数现有文件系统相似;可以创建和删除文件,将文件从一个目录移动到另一个目录,或者重命名文件。
NameNode维护文件系统名称空间。对文件系统名称空间或其属性的任何更改都由NameNode记录。应用程序可以指定由HDFS维护的文件的副本数量。一个文件的副本数量称为该文件的副本因子。这些信息由NameNode存储。
大数据入门学习笔记(叁)- 布式文件系统HDFS_第8张图片
数据副本
HDFS被设计为在大型集群中的机器之间可靠地存储非常大的文件。它以块序列的形式存储每个文件。为了容错,复制文件的块。每个文件的块大小和复制因子都是可配置的。

除了最后一个块之外,文件中的所有块大小都相同。

应用程序可以指定文件的副本数量。副本因子可以在文件创建时指定,以后可以更改。HDFS中的文件只写一次(除了追加和截断之外),并且在任何时候都有一个写入者

NameNode就块的复制做出所有决定。它定期从集群中的每个datanode接收心跳和数据块报告。接收到心跳表示DataNode正常工作。块报表包含datanode上所有块的列表。

副本存放策略大数据入门学习笔记(叁)- 布式文件系统HDFS_第9张图片

上图代表数据中心,两个机架,黄色代表客户端所在的节点(默认三个副本)

第一个副本存放在同client的节点上面;
第二个副本存放在不同第一个副本机架的随意一个节点;
第三个副本存放在与第二个副本相同机架的另一个节点上;
如果只有一个机架,则在不同节点存储;如果高于三个副本则高于三的随意挑选机架和节点。

HDFS环境搭建

官网安装文档
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

  1. 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

  2. 安装ssh
    sudo yum install ssh
    ssh-keygen -t rsa
    cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys

  3. 下载并解压hadoop
    下载:直接去cdh网站下载
    解压:tar -zxvf hadoop-2.6.0-cdh5.7.0.tar.gz -C ~/app

  4. 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>
  1. 启动hdfs
    格式化文件系统(仅第一次执行即可,不要重复执行):hdfs/hadoop namenode -format
    启动hdfs: sbin/start-dfs.sh
    验证是否启动成功:
    jps
     DataNode
     SecondaryNameNode
     NameNode
    浏览器访问方式 http://hadoop000:50070
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第10张图片
  2. 停止hdfs
    sbin/stop-dfs.sh

HDFS关键设置及常见问题

常用配置

配置文件路径:$HADOOP_HOME$/etc/hadoop
主要配置文件:
•hdfs-site.xml
•core-site.xml

大数据入门学习笔记(叁)- 布式文件系统HDFS_第11张图片

常见问题

小文件问题

  • 定义:大量大小小于块大小的文件
  • 实际场景:网页,Hive动态分区插入数据等
  • 背景:每个文件的元数据对象约占150byte,所以如果有1千万个小文件,每个文件占用一个block,则NameNode大约需要2G空间。如果存储1亿个文件,则NameNode需要20G空间;数据以块为单位进行处理。
  • 影响:占用资源,降低处理效率
  • 解决方案:
    • 从源头减少小文件
    • 使用archive打包
    • 使用其他存储方式,如Hbase,ES等

Namenode管理

大数据量下的namenode问题:

  1. 启动时间变长
  2. 性能开始下降
  3. NameNode JVM FGC风险较高

解决方案:

  1. 根据数据增长情况,预估namenode内存需求,提前做好预案
  2. 使用HDFS Federation,扩展NameNode分散单点负载
  3. 引入外部系统支持NameNode内存数据
  4. 合并小文件(有相关的命令:hadoop archive)
  5. 调整合适的BlockSize

内存结构
大数据入门学习笔记(叁)- 布式文件系统HDFS_第12张图片
大数据入门学习笔记(叁)- 布式文件系统HDFS_第13张图片
内存预估
文件元数据对象约占200byte,block元数据约占180byte:
总内存 =198 * num(Directory + Files) + 176 * num(blocks) + 2% * 总内存

数据迁移

场景:

  • 冷热数据迁移
  • 集群升级、维护

方案:

  • hadoop distcp 命令

数据平衡

原因:长期运行的集群增删节点,节点增删磁盘等

影响:

  • 跨节点拷贝数据
  • task会存在任务失败的风险
  • 磁盘利用不均

方案:

  • 集群节点间:hdfs balancer 命令
  • 单节点磁盘间:hdfs diskbalancer 命令

hdfs balancer

参数:

  • -threshold 30 :判断集群是否平衡的目标参数,每一个 datanode
    存储使用率和集群总存储使用率的差值的绝对值都应该小于这个阀
整体空间占用:30%
threshold: 10
balance之后每个dn的空间占用:20-40%

dn1:60% ;dn2:10%;dn3:30%
整体:
dn1:40% ;dn2:30%;dn3:30%
  • -include :执行balance的DN列表
  • dfs.balance.bandwidthPerSec 300MB :balance工具在运行中所能占用的带宽,设置的过大会影响其他任务

建议:

  • 对于一些大型的HDFS集群(随时可能扩容或下架服务器),balance脚本建议作为后台常驻进程
  • 根据官方建议,脚本需要部署在相对空闲的服务器上
  • 停止脚本通过kill进程实现

其他管理命令

  • hdfs dfsadmin
  • hdfs fsck

如何提高数据存取效率

数据压缩-作用

  • 节省数据占用的磁盘空间
  • 加快数据在磁盘和网络中的传输速度,从而提高系统的
    处理速度

数据压缩-评价指标

  • 压缩比:压缩比越高,压缩后文件越小,所以压缩比越高越好
  • 压缩时间:越快越好
  • 已经压缩的格式文件是否可以再分割:可以分割的格式允许单一
    文件由多个Mapper程序处理,可以更好的并行化

数据压缩-类型

大数据入门学习笔记(叁)- 布式文件系统HDFS_第14张图片

数据压缩-使用场景?

  • HDFS命令行写入:将数据压缩后写入
  • Flume写入:写入时指定hdfs.codeC参数
  • Sqoop写入:写入时指定参数
    –compression-codec org.apache.hadoop.io.compress.SnappyCodec
  • HBase 数据存储: 创建表时指定
    create ‘xx_table’, {NAME => ‘xx_cf’, COMPRESSION => ‘GZ’}
  • Mapreduce中间结果和最终结果:hadoop jar xxx “-Dmapred.compress.map.output=true”
    “-Dmapred.map.output.compression.codec=xxx”
    “-Dmapred.output.compress=true” “-Dmapred.output.compression.codec=xxx”
  • Hive中间结果和最终结果:
    set hive.exec.compress.intermediate=true
    set mapred.map.output.compression.codec=xxx
    set mapred.map.output.compression.codec=xxx
    set hive.exec.compress.output=true
    set mapred.output.compression.codec=xxx
  • Spark(RDD分区、广播变量、shuffle输出):
    • rdd:spark.rdd.compress,是否压缩已序列化的rdd,默认关闭
    • broadcast:spark.broadcast.compress,是否压缩broadcast数据,默认打开
    • 结果存储:saveAsTextFile(path,codec)
    • 压缩算法:spark.io.compression.codec,默认为snappy

使用建议

  • 选择何种压缩格式:
    • 考虑是否支持切分
    • 压缩率vs压缩速度
  • 什么时候使用:
    • 存储:磁盘空间紧张
    • 计算:性能调优(内存空间占用,IO传输)

纠删码(hadoop3.0之后的功能)

复制策略: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 ]

使用建议

将冷门数据以纠删码格式转存,减少空间占用:

  1. 指定某个目录为纠删码模式:
    hdfs ec -setPolicy -path [path] -policy [policy]
  2. 通过distcp命令将原有数据转存

异构存储

大数据入门学习笔记(叁)- 布式文件系统HDFS_第15张图片

  1. 配置dn存储路径时指定存储格式:
    [SSD]file:///path,[ARCHIVE]file:///path
  2. dn通过心跳汇报自身数据存储目录的StorageType给nn
  3. nn汇总并更新集群内各个节点的存储类型情况
  4. 客户端写入时根据设定的存储策略向nn请求响应的dn作为候
    选节点

相关命令

• hdfs storagepolicies [COMMAND]
	[-listPolicies]
	[-setStoragePolicy -path  -policy ]
	[-getStoragePolicy -path ]
	[-unsetStoragePolicy -path ]
	[-help ]
• hdfs mover [-p  | -f ]

使用建议

  • 一般使用默认策略(HOT,磁盘)即可
  • ARCHIVE:计算能力较弱,存储密度高,存储冷数据
  • SSD:土豪专用

代码演示

https://blog.csdn.net/bingdianone/article/details/83863115#t15

HDFS shell

HDFS shell常用命令的使用

官网文档参考
http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html#dfs
hdfs dfs等于hadoop fs

Is

[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

get

[root@hadoop data]# hdfs dfs -get /hell.txt

mkdir

[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

rm

[root@hadoop data]# hdfs dfs -rm -R  /text/a/b

put

[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

Java API操作

  • IDEA+Maven创建Java工程
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第16张图片
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第17张图片
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第18张图片
    大数据入门学习笔记(叁)- 布式文件系统HDFS_第19张图片
  • 添加HDFS相关依赖
<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>
  • 开发Java API操作HDFS文件
    注意两点:
  1. hdfs://hadoop000:8020更改访问ip和端口(在hadoop_home/etc/hadoop/core-site.xml)
  2. FileSystem.get(new URI(HDFS_PATH), configuration, “hadoop”)的用户hadoop需要更改为具有读写权限的用户
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");
    }

}

HDFS文件读写流程

大数据入门学习笔记(叁)- 布式文件系统HDFS_第20张图片
首先看看出场的角色,第一个是client客户端,用来发起读写请求,读取HDFS上的文件或往HDFS中写文件;第二个是Namenode,唯一的一个,会协调所有客户端发起的请求;第三个是DataNode,负责数据存储,跟Namenode不一样,DataNode有很多个,有时候能达到数以千计。

文件写流程图解


大数据入门学习笔记(叁)- 布式文件系统HDFS_第21张图片
往HDFS中写数据的流程如下:

  • 第1幅图:我们跟客户端说,你帮我写一个200M的数据吧,客户端说没问题啊,但是…
  • 第2幅图:客户端不知道我们对数据有没有其他的要求啊,问我们是不是忘了什么东西呢?我们想起来我们还是有要求的,第一我们要把数据分成若干块,并且每块的大小是128M,第二,每个数据块应该复制3份。其实这就是我们说的HDFS的文件分块和多副本,如果你不说的话客户端怎么知道到底怎么分,复制多少份呢?
  • 第3幅图:由上面的对话我们发现,如果对于每个文件客户端都要这么问一下,是不是太麻烦了?所以说一个好的客户端应该是,用户就算不说你也要知道有这两个属性:块的大小,一个文件应该按照怎样的大小切分(通常是64M或128M);复制因子,每个块应该复制多少份(通常是3份),也就是说如果用户不主动提供这些属性,那么就按照默认的来。
  • 第4幅图:现在客户端已经知道了每个块的大小了,那么把200M的文件分成128M和72M两个块,一个长一个短。
  • 第5幅图:切分后客户端就开始工作了,既然有两个块,那先上传第一个块,于是客户端请求Namenode帮它写一个128M的块,并且要复制3份。
  • 第6幅图:Namenode接受到客户端的请求后,既然需要3个副本,那么就需要找到3个DataNode,Namenode就会想怎么去找到这3个DataNode呢?我该告诉客户端哪些信息呢?于是它就去它管理的DataNode中找一些满足要求的空闲节点。
  • 第7幅图:Namenode找到了3个节点,现在把找到的节点发给客户端,表示:兄弟,你不是要我帮你写数据嘛,我给你找到了这3个合适的DataNode,并且已经按距离远近给你排过序了,第一个是最近的,你把数据给他们让他们帮你写吧。
  • 第8幅图:客户端收到3个DataNode地址后,直接把数据发送到第一个节点(DataNode1)上,然后DataNode1开始把数据写到他的硬盘中。
  • 第9、10、11幅图:DataNode1在接受数据的同时,会把刚刚收到的数据发送到第二个DataNode2上,同理DataNode2也是,接收的同时把数据立马发给DataNode3,到了DataNode3已经是最后一个DataNode了。整个过程跟流水线一样,接收一点就发一点。(个人感觉跟计算机网络中令牌环网的工作原理有些类似)
    第12幅图:Namenode是所有DataNode的老大,所以DataNode在存完数据后要跟老大汇报,告诉他说,我第一个块的数据已经写完了。
  • 第13幅图:3个DataNode都报告完成后,好,这样第一个数据块就写完了,下面对第二个块重复这个步骤。
  • 第14幅图:所有的块都写完了之后,客户端关闭跟Namenode的连接。这时Namenode已经存储了文件的元数据,也就是文件被拆成了几块,复制了几份,每块分别存储在哪个DataNode上。
  • 最后一幅图说明了每个角色在写数据过程中的作用:
    Client:切分文件成数据块。
    Namenode:对于每个数据块,找到存储的DataNode地址。
    DataNode:多副本方式存储数据。

文件读流程图解

大数据入门学习笔记(叁)- 布式文件系统HDFS_第22张图片

  • 第1幅图:写文件已经搞定了,那么怎么读文件呢?我们先跟客户端说,嘿兄弟!帮我读个文件呗!
  • 第2幅图:客户端跟Namenode发了个请求,把文件名发送给Namenode,表示我想要这个这个文件的信息。
  • 第3幅图:Namenode找了找,然后找到了一个结果,结果包含这个文件被拆成了多少块,每个块存储在哪些DataNode上的信息,并且DataNode同样是按照距离排序的。然后把这个结果发送给客户端,说,嘿兄弟!你要的文件在这些DataNode上,你去找吧。
  • 第4幅图:现在客户端知道了文件的存储情况,所以就一个个去DataNode上访问就好了。
  • 最后提出了一个问题:如果这个过程中DataNode挂了,或者数据在传输中出了问题怎么办?事实上HDFS对于这些问题都是能够完美解决的。

HDFS错误处理机制



HDFS优缺点

优点:

  • 数据冗余、硬件容错
  • 处理流式的数据访问
  • 适合存储大文件
  • 可构建在廉价机器上

缺点:

  • 低延迟的数据访问
  • 不适合小文件存储

你可能感兴趣的:(Hadoop学习笔记)