0.HDFS分布式文件系统

HDFS分布式文件系统

1. Hadoop是什么(20分钟)

1.1 Hadoop架构

HDFS (Hadoop Distributed File System)文件系统:存文件读文件的一个系统

Hadoop由三个模块组成:分布式存储HDFS、分布式计算MapReduce、资源调度引擎Yarn

1.2 从生活中寻找灵感

1.2.1 存储书籍

分馆书架

举例:国家图书馆从无到有开始创建,图书逐渐增多,日常需要统计书籍情况

1、初期一个分馆、规划很多列(一列放置多个书架)、一个书架

2、持续增加藏书,增加书架

3、增加第二列书架

4、增加n列书架

5、再增加一个分馆

1.2.2 分配人员、统计书籍

现需要统计所有书籍中,书名包含"Hadoop"关键字的书有多少本?

馆长(yarn):分配普通工作人员干活

普通工作人员(资源cpu,内存,io):图书馆的工作人员

(逻辑):统计包含Hadoop关键字的书

1.2.3 存储书的电子版

数据中心机架服务器大文件

数据中心、机架、服务器、服务器存储电子书 -> HDFS

以一个数据中心为例

1.2.4 分配资源、统计书籍

现需要MapReduce程序统计所有书籍中,书名包含"Hadoop"关键字的书有多少本?

Yarn(馆长):分配“CPU/内存/IO资源”干活

CPU/内存/IO资源(普通工作人员):服务器资源

MapReduce程序(事):统计包含Hadoop关键字的书

****

**map阶段主要做什么?reduce阶段呢?**map阶段主要是统计,把文本拆分成一个个单词,统计起来。reduce阶段对统计起来的数进行计算。

1.3 分布式集群区别

分布式:利用一批通过网络连接的、廉价普通的机器,完成单个机器无法完成的存储、计算任务

**分布式&集群区别?**分布式各个节点不同工作,集群所有的节点做相同的工作。

小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。

后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,两个厨师的关系是集群。

为了让厨师专心炒菜,把菜做到极致,再请了个配菜师负责切菜,备菜,备料 ...  厨师和配菜师的关系是分布式。

 

一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群。

一个配菜师因故请假了,但是其余的配菜师还是该啥就干啥,只是没请假的配菜师任务均匀的加量了,但他们的任务和职责是不变的,这是集群。
1.4 HDFS是什么

Hadoop分布式文件系统

==适合存储大文件,不适合存储小文件?==无论是大文件还是小文件都要有150字节的元数据,都要占用内存,而nn管理元数据,其内存空间是有限的,如果存储小文件是很吃亏的。

大量文件分布式存储在不同的服务器上。

单个文件100T,单块磁盘肯定存储不下。必须把文件分块进行存储在不同的服务器上

**hdfs中是谁对文件进行的分块?又是怎么进行的分块,单纯的把数据按照128M进行分块么?**hdfs client把文件进行切分,规则就是按照128M进行切割。之后nn根据元数据信息进行指派不同文件分配到不同dn。

1.5 为什么使用HDFS

高可用、容错、可扩展

**高可用和容错的区别?可用和容错不都是在说数据没了后的备份么?**高可用是在说hdfs崩溃后还有备选方案恢复。容错是在说数据丢失后可以恢复。

​ 高可用:nn挂掉了还有secondNN,还有HA策略。主节点挂了还能用

​ 容错:网络连接出错了,数据出错,还可以从其他地方获取。通过校验和得知后,找备份的block读取。

​ 可扩展:很方便的插入新的服务器,进行容量的扩展。

1.6 Hadoop历史

  • Hadoop作者Doug Cutting

  • Apache Lucene是一个文本搜索系统库

  • Apache Nutch作为前者的一部分,主要包括web爬虫、全文检索;2003年“谷歌分布式文件系统GFS”论文,2004年开源版本NDFS

  • 2004年“谷歌MapReduce”论文,2005年Nutch开源版MapReduce

2. HDFS初体验(10分钟)

操作hdfs的两种方式:hdfs命令,java编程

2.1 HDFS命令

linux shell命令风格

参考课件《Hadoop fs命令》

2.2 HDFS编程

java 编程的方式

参考10.HDFS编程

2.3 WEB UI界面

3. 核心概念block(20分钟)

3.1 数据块block

是谁对文件进行切分的? HDFS2.x上的文件,按照128M为单位,hdfs client将文件切分成一个个block,分散的存储在集群的不同数据节点datanode上

**HDFS中一个44M大小的块会不会占据128M的空间?**不会,还是占用44M

为啥就定了128M?(面试题)

磁盘寻址时间数据传输时间决定的

原理:

文件块越大,寻址时间越短,但磁盘传输时间越长。

文件块越小,寻址时间越长,但磁盘传输时间越短。

一、为什么HDFS中块(block)不能设置太大,也不能设置太小?

  1. 如果块设置过大,

    从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时–mapreduce中的map任务通常一次只处理一个块中的数据,如果块过大运行速度也会很慢。

  2. 如果块设置过小,

    一方面文件块过小,寻址时间增大,导致程序一直在找block的开始位置。

    另一方面存放大量小文件会占用NameNode中大量内存来存储元数据,而NameNode的内存是有限的,不可取;

    因而,块适当设置大一些,减少寻址时间,那么传输一个由多个块组成的文件的时间主要取决于磁盘的传输速率。

二、 HDFS中块(block)的大小为什么设置为128M?

  1. HDFS中平均寻址时间大概为10ms;

  2. 经过前人的大量测试发现,寻址时间为传输时间的1%时,为最佳状态

    所以最佳传输时间为10ms/0.01=1000ms=1s

  3. 目前磁盘的传输速率普遍为100MB/s;

    计算出最佳block大小:100MB/s x 1s = 100MB

    而我们磁盘block块的大小都是2^n倍,所以最合适的大小就是128MB.

ps:实际在工业生产中,磁盘传输速率为200MB/s时,一般设定block大小为256MB

   磁盘传输速率为400MB/s时,一般设定block大小为512MB

扩展:

hdfs 寻址时间1:读取时间100

机械硬盘

扇区512字节,window读取的流程:一个文件分块存在磁盘面上,每次读取文件都要先寻址到对应的扇区,之后进行读的操作,之后找下一块。寻址是很耗时的

3.2 block副本

**这样存储有没有问题?**商用服务器上一旦发生cpu,io的故障,一个数据节点挂掉,就没办法读回来数据了。为此hadoop,为每个块做了一个副本

保正数据的可用及容错**(在大数据中我们所说的副本数都包括自身)**

  • replication = 3

  • hdfs-site.xml

    <property>
    	<name>dfs.replication</name>
    	<value>3</value>
    </property>
    

3.2.1 机架感知策略

实际机房中,会有机架,每个机架上若干服务器

服务器2u,3u

机架36u,可以放18个2u的服务器,并且机架中有交换机让服务器间通信

​ 机架

2个机架里各放了三个服务器。

备份的block存储到另外一个机架,机架断电了也不怕。

机架感知策略:

第一个block在机架1上

**为什么第二个block存储到另外一个机架上?**这样哪怕机架1断电,也能从另外一个机架上拿数据。

**为什么第三个block存储在同机架的另一个服务器上?**因为同时挂掉两个机架的可能性很小,并且机架间的通信走的网络很远,机房中的带宽是很宝贵的,3个block各在一个机架上,并发较高的话很容易把机房带宽打满,整个集群的响应速度很慢,所以出于带宽安全的考虑第三个block存储在同机架1上。

网络带宽是指在单位时间(一般指的是1秒钟)内能传输的数据量

跨机架通信:服务器要先找到当前机架的交换机,当前机架的交换机要找到总机架的交换机,在通过总交换机去找另外一个机架的交换机,在去找具体的服务器。跨机架通信很占用带宽的。

这整个流程是hdfs自动控制的。

3.3 block的一些操作
  • **设置文件副本数,有什么用?**分担读的负载,可能一个数据有很多的客户端要读。多做些副本方便我们去读。
hadoop fs -setrep -R 4 /path

R–recursive递归

  • 查看文件的块信息?
hdfs fsck /02-041-0029.mp4 -files -blocks -locations

4. HDFS体系架构(20分钟)

1558073557041

HDFS是主从架构Master/Slave、管理节点/工作节点

hdfs client先链接nn获取元数据信息,之后根据元数据信息去链接dn,获取每个服务器上的块信息

每个节点间要进行通信必须通过网络。(借助RPC进行通信)

**hdfs怎么读数据?**hdfs client必须先知道文件的元数据信息(文件的每个block存在哪些数据节点dn上,备份在哪里,block大小等等。。。),元数据信息存储在nn的内存当中

4.1 NameNode
  • fs文件系统,用来存储、读取数据

  • 读文件 -> 找到文件 -> 在哪 + 叫啥?

  • window的NTFS文件系统 -> 虚拟机说明.txt

HDFS也是fs,它也有metadata;metadate由NameNode存储在其内存

  1. nn负责管理文件系统和命名空间,内存中存放了HDFS的元数据;

    • **命名空间是什么?**进行区分的一个命名规范。北京杨振江,山西杨振江,c盘word.txt和h盘word.txt

    • 元数据文件系统树、所有的文件和目录、每个文件的块列表、块所在的datanode等;

      • 位置:/opt/bigdata/hadoop-2.7.3/hadoop/dfs/name/current

        edits_0000000000000097822-0000000000000097823  edits_inprogress_0000000000000244400
        edits_0000000000000097824-0000000000000097825  fsimage_0000000000000244300
        edits_0000000000000097826-0000000000000097827  fsimage_0000000000000244300.md5
        edits_0000000000000097828-0000000000000097829  fsimage_0000000000000244399
        edits_0000000000000097830-0000000000000098168  fsimage_0000000000000244399.md5
        edits_0000000000000098169-0000000000000098169  seen_txid
        edits_0000000000000098170-0000000000000098180  VERSION
        
      • fsimage和edit log文件都会按照事务ID来分段。

      • 当前正在写入的edit log文件名会带有"inprogress"标识

      • seen_txid文件保存的就是当前正在写入的edit log文件的起始事务ID。

      • 最近的fsimage和edit log文件的内容加起来就是全量元数据。NN在启动时,就会将最近的fsimage文件加载到内存,并重放它之后记录的edit log文件,恢复元数据的现场。

    • 文件、block、目录占用150Byte字节的元数据;所以**HDFS适合存储大文件,不适合存储小文件(元数据信息放在hdfs中nn的内存中,内存是有上限的**,小文件要占150byte的内存,大文件还是占150byte的字节,我们当然是选择存大文件了)

    • ==为什么元数据存储在NameNode内存中?==内存读写快

      • **这样做有什么问题?**NN一旦宕机内存中的元数据会丢
      • **怎么解决?**将元数据写磁盘。hdfs中用的是双缓冲异步写的策略,当第一个内存写满后,交换两个内存,在第二个内存中进行写操作。同时第一个内存的数据进行摞磁盘的操作,当第二个内存也写满后,此时第一个内存就又有空间了,继续进行写。(这里的摞磁盘就是直接写到了我们说的NN的fsimage和edits log中)
  2. 为了保证元数据的安全,会把内存中的元数据保存到磁盘中。元数据信息在磁盘上以命名空间镜像文件fsimage编辑日志edits log的方式保存

    • fsimage:每次checkpoint的时候生成一个fsimage。fsimage是文件系统的目录树Namespace。Namespace保存了目录树及每个目录/文件节点的属性。

      • 一开始是没有fsimage的,一开始是满足条件后edit log发送过去,sbnn将内存中当前的状态保存为fsimage
    • edits log:对hdfs进行读写操作就会在edit log中记录, 把HDFS 的修改信息(创建,修改,删除等)写入到本地目录,每一个操作以一条数据的形式存放。edits文件默认每2分钟产生一个。正在写入的Edits文件以 edits_inprogress_* 格式存在。

    • https://tech.meituan.com/2017/03/17/namenode-restart-optimization.html

    • https://hexiaoqiao.github.io/blog/2016/07/06/namenode-memory-overview/

    • HDFS的整个目录树被完整建立,但是并没有掌握数据块Block与DataNode之间的对应关系BlocksMap,甚至对DataNode的情况都不掌握,所以需要等待DataNode注册,并完成对从DataNode汇报上来的数据块汇总。

    • 除Namespace外,NameNode还管理非常重要的元数据BlocksMap,描述数据块Block与DataNode节点之间的对应关系。NameNode并没有对这部分元数据同样操作持久化,原因是每个DataNode已经持有属于自己管理的Block集合,将所有DataNode的Block集合汇总后即可构造出完整BlocksMap。

    • **hadoop集群中的元数据都存储的nn的内存中(X)**元数据=edits+fsimage。当内存写满后会进行写磁盘的操作,并不是都在内存中,有部分在磁盘上。

4.2 DataNode

存储block,以及block元数据,包括数据块的长度、块数据的校验和、时间戳

4.3 SeconddaryNameNode

secondaryNameNode一般部署在另外一台节点上,因为它需要占用大量的CPU时间,并需要与namenode一样多的内存,来执行合并操作

完整流程:从磁盘把fsimage加载到内存,重启nn,hdfs client写数据时会产生元数据,元数据会写入内存,但是内存中保存元数据不安全,所以采用双缓存异步写,写到磁盘生成fsimage和editlog。每隔1h或者写入了100W的事务就会进行一次checkpoint,在sn中合并fsimage和editlog,在发给nn一个合并后的fsimage。

  • 最近的fsimage和edit log文件的内容加起来就是全量元数据。NN在启动时,就会将最近的fsimage文件加载到内存,并重放它之后记录的edit log文件,恢复元数据的现场。

    • fsimage每次在checkpoint后才会生成。而checkpoint要么在1h后要么事务达到100W
    • 双缓存异步写
      • 每当hdfs client写操作的时候,会生成元数据,元数据会写入内存中,但内存保存元数据不安全。使用双缓存异步写的方式把元数据写入磁盘。
      • 缓存1号去接收元数据,缓存2号去写磁盘。当缓存1号写满元数据后,缓存1号和2号互换位置,缓存1号进行写磁盘操作(写入nn磁盘中的edits log和fsimage),缓存2号进行接收元数据。缓存2号通过写磁盘释放了一部分空间,又可以继续接收元数据了。
  • SecondaryNameNode的check point备份元数据信息,提升nn从故障中恢复速度

    • 集群启动1h后(或事务达到100W)会进行一次checkPoint检查点,对重要数据进行备份,把edits log和fsimage通过网络http get请求拉取到secondaryNameNode中,并进行合并生成一个新的文件fsimage,之后回传给nn,并重命名为fsimage。

      dfs.namenode.checkpoint.period:触发checkpoint的周期长度,默认为1小时。
      dfs.namenode.checkpoint.txns:两次checkpoint之间最大允许进行的事务数(即edit log的增量条数),默认为100万。
      
      • 由于NN的负担已经比较重,再让它来进行I/O密集型的文件合并操作就不太科学了,所以Hadoop引入了SNN负责这件事。也就是说,SNN是辅助NN进行checkpoint操作的角色
    • 当此时还有客户端向hdfs写数据,会滚动生成一个新的edits文件

    • 当nn挂掉,我们要进行数据恢复的时候,我们只需要执行少部分的new edits的操作以及把sn的新fsimage读入内存就够了。如果没checkpoint的话,可能fsimage之后又很多edit log文件,我们得把这些edit log的数据操作全执行一次,相当耗时。

https://lihuimintu.github.io/2019/09/20/HDFS-HA-checkpoint/

  • 怎么提高nn的启动速度的?

没有sn的话,随着写操作的进行,edits记录会越来越多,一旦节点挂掉,重启后要恢复元数据,要把所有的edit log执行一遍,才能恢复。

有sn,会进行checkpoint生成fsimage,合并fsimage和edits,这样下次重启就只需要执行新的edit log就可以。会加快集群启动后,nn的重启速度。

如果nn挂了,sn可以充当新的nn。把合并后的fsimage.ckpt加载进内存。启动sn,对外提供服务。

sn策略不够优雅,nn挂了后重启过程中,对外提供服务一定会有一个中断的过程(metadata要从磁盘加载到内存)。更优雅的方式是HA。

如果开启了NN高可用呢?
上面说的都是集群只有一个NN的情况。如果有两个NN并且开启了HA的话,SNN就没用了——checkpoint过程会直接交给Standby NN来负责。Active NN会将edit log文件同时写到本地与共享存储(QJM方案就是JournalNode集群)上去,Standby NN从JournalNode集群拉取edit log文件进行合并,并保持fsimage文件与Active NN的同步。

HA with QJM架构下,NameNode的整个重启过程中始终以SBN(StandbyNameNode)角色完成。与前述流程对应,启动过程分以下几个阶段:

  1. 加载FSImage;
  2. 回放EditLog;
  3. 执行CheckPoint(非必须步骤,结合实际情况和参数确定,后续详述);
  4. 收集所有DataNode的注册和数据块汇报block report。

扩展:

当nn挂掉,还有一部分edits在nn上,现在从sn恢复后是不是就没这部分数据了?不会的,集群中有一个叫nfs(network fileSystem)的服务器,每次我们客户端有写操作的时候,我们的nn服务器上有一个nfs client,每次的操作不仅会在edits中记录,也会通过nfs client传给nfs server。所以当nn挂掉,我们可以从nfs server 上去拿。

6. HDFS机制

6.1 心跳机制

集群通过网络通信形成一个整体。主要靠心跳机制。dn向nn发心跳,nn会反馈一些指令给dn

  • **传达什么指令?**当一个block副本replica数不够,就让dn进行block复制

工作原理:

  1. master启动的时候,会开一个ipc server作为服务端。

    ​ **ipc server有什么用?**Inter-Process Communication,进程间通信 (RPC),跨进程通信

  2. slave启动,通过ipc连接到master,每隔3秒钟向master发送一个“心跳”,携带状态信息;

    • **什么状态信息?dn有哪些状态?**主要就是告诉nn,dn还活着
  • 要是收不到心跳呢?

    • 如果超过10min都没收到心跳,nn就会认定dn死了。那么客户端进行读写操作都不会在这个死了的dn上进行。并且会把死了的dn上存储的block进行再次的备份。(因为客户端和dn操作前是和nn操作,通过元数据获取dn上block的存储情况,如果nn认定dn死了,客户端也就不可能对dn有任何操作了。)
  • block report(报告)

  • report 需要收集datanode 上的block情况报告给nn,会造成读和写的阻塞。(该机的block达到上百万级别)

    • DataNode通过block report告诉NameNode,dn上所有的block的信息以及block当前状态–正在构建 or 已经完成构建
  • dn刚开始启动后会进行一次block report,每隔1h还会进行一次。(源码中有写)

心跳机制作用:

  1. Namenode全权管理数据块的复制,它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。接收到心跳信号意味着该Datanode节点工作正常。Block report意味着nn掌握了所有的dn的block信息

  2. DataNode启动后向NameNode注册(注册就是dn告知nn自己的ip,port,所属的blockpool,告知的方法就是往nn的数据结构中加入block的数据)

  3. hadoop集群刚开始启动时,会进入安全模式(99.9%),就用到了block report机制

    • 安全模式,当 block当前数(去重)/总数<99.9% 时会进入安全模式。当不断有dn向nn汇报,nn得知的block越来越多>99.9,客户端就可以对hdfs进行操作了。要保证存储数据的完整性。安全模式只能读不能写

    • **怎么知道的总数?**有元数据、因为一启动集群,就会把nn中的fsimage读入内存,edits log在操作一遍获取元数据。元数据信息中就有总的block数。

    **为什么要进入安全模式?安全模式有什么用?不进入会怎么样?**不进入会发现很多块都没有启动,对块操作的时候会有一堆莫名其妙的错误。有安全模式,NameNode就不会过早地开始复制数据块。

6.2 负载均衡(每个磁盘/服务器的访问量就是负载)

**什么原因会有可能造成不均衡?**加入新的磁盘,新的服务器,他们的磁盘利用率和老磁盘的磁盘利用率差别大。会造成资源不均衡。主要是两方面,存储和性能的不均衡。

**为什么需要均衡?**不均衡资源没有得到充分的利用。某个block所在的服务器的访问频率高,新的服务器和磁盘访问率低,没有充分的利用所有的服务器和磁盘,这样就不好。

**如何均衡?**当出现不均衡的情况,hdfs的自动均衡策略,通过网络方式把磁盘中使用率高的block进行迁移到使用率低的新磁盘或者新服务器上

$HADOOP_HOME/sbin/start-balancer.sh -t 5%

使用该命令,nn掌握着每个block的使用情况,一旦磁盘利用率的差值大于5%,就会触发负载均衡。存储和读写均衡,能提高效率。

7. HDFS读写流程(15分钟)

7.1 数据读流程

7.1.1 基本流程

  1. hdfs客户端会调用Distributed FileSystem对象的open方法
  2. 之后通过RPC的方式从nn上获取一批块的元数据信息(块的所有冗余备份的位置信息(block的3个备份):DataNode的列表),根据client到block所在dn的远近进行一个排序。
  3. 之后生成一个FSDataInputStream
  4. 获取文件位置信息后,去最近的dn上读数据。
    • 如果期间网络中断,inputStream会从距离第二近的dn上读数据,并告诉nn,block网络中断的消息,nn会返回一个删除block的命令,并让其他的dn在复制一份block保证达到副本数。
    • 在block上不仅会记录数据,还会记录一个校验和,如果返回的数据出错,也就是返回的数据计算的新的校验和无法和原来的校验和比对上的话,告知nn,nn会返回一个删除block的命令,并让其他的dn在复制一份block保证达到副本数。
  5. 获取返回数据后在关闭流。

7.1.2 容错

  • **如果client从datanode读取block时,网络中断怎么办?**会从下一个距离相对较近的dn上读数据,并且会在nn上记录当前的dn有问题。

  • **client如何保证读取的数据是完整的?**block上不仅会存储数据,也会存校验和–checksum(CRC32),当我们读回来的时候,会再次计算一次校验和,如果比对不上,说明数据丢失了。能比上说明数据没错。

    • **checksum本身出错呢?校验和32byte,在巨大的块中,出现错误概率很小?为什么小就出错概率小了?**数学概率问题。
  • 如果client读取的数据不完整,怎么办?

    不完整去其他块上读,并且告诉nn出错的block,block在返回指令中告诉dn删除错误的block,之后让其他dn上复制block达到副本数。

7.2 数据写流程

1557999856839

create时nn要做4件事:

  1. 判断nn是否正常运行
  • 怎么知道nn是否正常?nn不正常运行hdfs就无法操作
  1. 判断client是否有权限
  2. 判断要写的文件是否存在。
  3. 向edits.log写日志

写流程

  1. hdfs client将文件按照128M进行切割,调用DistributedFileSystem对象的create方法
  2. 通过RPC访问nn,发送一个addBlock命令给nn,nn会根据已知的元数据信息,合理的(根据client到dn的距离)返回给我们要在哪些dn上进行write操作。
  3. 创建FSDataOutputStream对象
  4. 进行写操作
    • 先写chunk。chunk数据块516byte:一个byte一个byte写数据,当写满512byte的chunk后计算一个校验值4byte。
    • chunk写完后会都放在packge中,packge数据包54KB
    • packge写满后会把一个个packge放入data queue数据队列。
    • 数据队列中的packge会通过OutputStream向dn中写数据(根据nn提供的元数据信息)。同时会向ack queue队列写数据。向dn写完后删除data queue中的packge。
  5. 当向最后一个dn写完最后一个packge后(3个副本),逆着pipeline对数据进行校验,packge中的每个chunk的校验值相加生成最终的校验和
    • 在通过dn把校验和返回给我们的ack queue对比两个校验和
    • 如果校验成功就删除ack queue上的packge,生成一个ack packet返回给hdfs client。
    • 如果校验失败就把ack queue上的packge重新传输给data queue。在进行一次写操作
  6. 当把所有的block写完后会close关闭outputStream
  7. 之后client告诉nn已经写完了–调用nn上的complete方法。
  • 异常处理:dn间传输packge时出现网络中断
    • 首先会关闭掉当前的pipeline(管道)
    • 在其他dn间生成新的pipeline,在其他dn的block上生成一个新的verison和id,传给nn。(比如dn2断了,dn1和dn3间会建立新的pipeline写packge)
    • 当网络恢复后,原先的dn会向nn发心跳(nn会对比这个block和其他block副本的数据信息),这时发现自己的block的id,version是老的。nn就会给他发送一个删除的指令。
    • 当dn因为网络中断,会告知nn,nn检测到一些block少了一个副本,nn就会发送指令,让其他的dn进行block的备份。

支持多用户同时写文件么?为了保证数据的一致性,一个文件只能有一个写,不允许并发写入–多个线程同时写。不支持文件的随机修改。仅支持数据的append。

如果HDFS支持随机修改的话,在文件头写一点东西,必须向所有的副本都写一遍,涉及到机架感知,服务器带宽很容易打满

数据一致性是指什么?

在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。

并发写入是指什么?

hdfs不能并发写指的是不能同时上传同位置同名文件

两个同名文件,一个文件成功上传,再用另一个客户端上传同名文件:会提示 File exists

**如果pipeline一个节点写失败,则会重新申请dn节点在写(x)**首先会关闭当前pipeline,剩下的dn组成一个新的pipeline

扩展:

packge 54*1024/516不是整数,究竟是怎么存储的?

packge中包含3部分:packge的header,chunk的data,checksum校验和。

组成packge的chunk并不是刚刚够整除,剩余部分用header填补(data和checksum的长度存储到header中)

8. Hadoop HA高可用(High Availability)

**zk主要作用是什么?**分布式协调框架,做HA高可用,nn挂了还能用。sn的机制太消耗时间了,企业中会有很大的损失。

在HA中SN的checkpoint动作交给了StandbyNN来做。

高可用架构:

  1. NN Active会把自己最近的edits log和fsimage写入共享服务器JournalNode,NN Standby会实时读取共享服务器中的edits和fsimage。这样就相当于做了一个备份的操作。
  2. 同时dn在注册和block report的时候会向两个nn都发送,这样nn standby就完全和nn active一样了,不仅有edits和fsimage还知道了每个dn上存储的完整block信息。

Image201906101009

ZooKeeperFailoverController:zk网络容错控制器–作为一个ZK集群的客户端,用来监控NN的状态信息。zkfc会周期性的向它监控的namenode,发生健康探测命令,从而鉴定某个namenode是否处于正常工作状态,如果机器宕机,心跳失败,那么zkfc就会标记它处于不健康的状态;

HA流程:

  1. 两个nn进程抢着注册,先注册的在zk上创建临时节点,状态变为ActiveNN。未抢注成功的在zk上创建监听器,状态变为standbyNN。
  2. 监听器轮询的访问临时节点,一旦zkfc进程和zk连接中断,临时节点将随着连接的session断开而消失。
    • 监听器监听到临时节点不存在,通知standbyNN触发回调函数
  3. zkfc触发回调函数去访问ActiveNN的zkfc检查nn的当前状态
    • 若Active的ZKFC状态标记为不健康,修改ActiveNN的状态变为standbyNN,修改自身的standby变为Active。

9. Hadoop联邦

9.1 为什么需要联邦

分布式文件系统存在的问题:

  • 集群的元数据保存在namenode内存中。每个文件、目录、block的元数据占用内存150字节。对于一个拥有大量文件的超大集群来说,nn的内存将成为限制系统横向扩展的瓶颈。

为什么要有联邦?

  • 联邦的出现就是要打破这中内存上限的。
9.2 联邦

1558003666943

  • 联邦特点:扩展性,吞吐量,隔离性
    • 扩展性:每个nn下有对应的block信息(逻辑概念block pool)。有了联邦后有多少ns(一个nn一个name space命名空间),nn存储的容量就能扩大多少倍。
      • ns(nameSpace)命名空间。每个文件系统有很多目录,目录下有很多文件,目录结构,以及有哪些文件整体就叫做ns
      • 这里nn的块pool是一个逻辑上的概念,实际的block存储的dn上。每个dn上存储来自不同block pool上的块,实现了dn的扩容
    • 吞吐量:client读写数据都要和nn进行交互,有多个nn就能并发的和多个nn交互。提高吞吐量
    • 隔离性:client连接不同的nn,哪怕其中一个nn内存写满了,也不影响在其他nn上的client操作

10. HDFS编程

步骤:

1.创建文件系统

2.调用读写方法open读,create写

3.IO读写

10.1 代码

resource下引入配置文件

为什么要引入?不引入也能完成读写?那是因为node1上有hadoop,如果在本地机器上就必须引入

core-site.xml

hdfs-site.xml

log4j.properties

mapred-site.xml

yarn-site.xml

关键有一个问题,这4个是怎么联动到代码的?

Configuration的静态代码块中有addDefaultResource(“core-site.xml”)

static {
    // Add default resources
    addDefaultResource("core-default.xml");
    addDefaultResource("core-site.xml");
    。。。
    }
  • 写数据

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

import java.io.*;
import java.net.URI;

/**
 * 将本地文件系统的文件通过java-API写入到HDFS文件
 */
public class Write {

    public static void main(String[] args){
//      TODO  修改代码的系统变量HADOOP_USER_NAME的内容为root
        System.setProperty("HADOOP_USER_NAME","root");
		
        String source="H:\\BigData\\02.Hdfs\\课堂笔记.txt";

        //HDFS的路径,先确保/aaa目录存在
        String fileSystem="hdfs://node1:9000";
        String destination="hdfs://node1:9000/aaa";

        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(source));
            //HDFS读写的配置文件
            Configuration conf = new Configuration();
            
          	//创建hdfs文件系统
            FileSystem fs = FileSystem.get(URI.create(fileSystem),conf);

            //调用Filesystem的create方法返回的是FSDataOutputStream对象
            //该对象不允许在文件中定位,因为HDFS只允许一个已打开的文件顺序写入或追加
            OutputStream out = fs.create(new Path(destination));

            //true写完后自动关闭输入输出流
            IOUtils.copyBytes(in, out, 4096, true);

        } catch (FileNotFoundException e) {
            System.out.println("exception");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("exception1");
            e.printStackTrace();
        }
    }
}

写文件需要修改权限,否则会报错

org.apache.hadoop.security.AccessControlException: Permission denied: user=85094, access=WRITE, inode="/aaa2":root:supergroup:-rw-r--r--
hdfs目录树
-rw-r--r--	root	supergroup	1.69 KB	2020/4/11 下午7:25:17	3	128 MB	aaa
  u  g	o
当前用户有读写的权限,其他用户只有读的权限

修改权限

//      TODO  修改代码的系统变量HADOOP_USER_NAME的内容为root
        System.setProperty("HADOOP_USER_NAME","root");

hdfs的create()写操作默认是覆盖,我重新写的时候文件确实是覆盖的操作

  /**
   * Create an FSDataOutputStream at the indicated Path.
   * Files are overwritten by default.
   * @param f the file to create
   * @throws IOException IO failure
   */
  public FSDataOutputStream create(Path f) throws IOException {
    return create(f, true);
  }
  • 读数据

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;

/**
 * 从HDFS读取文件
 * 打包运行jar包 [bruce@node-01 Desktop]$ hadoop jar com.kaikeba.hadoop-1.0-SNAPSHOT.jar  com.kaikeba.hadoop.hdfs.FileReadFromHdfs
 */
public class Read {

    public static void main(String[] args) {
        try {
            String fileSystem="hdfs://node1:9000";
            String srcFile = "hdfs://node1:9000/aaa";
            Configuration conf = new Configuration();

            FileSystem fs = FileSystem.get(URI.create(fileSystem),conf);
            FSDataInputStream hdfsInStream = fs.open(new Path(srcFile));

            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("H:\\aaa.txt"));

            //true执行结束后自动关闭流
            IOUtils.copyBytes(hdfsInStream, outputStream, 4096, true);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
10.2 打包运行

打jar包方式:

mvn clean package -DskipTests

打包运行jar包

[bruce@node-01 Desktop]$ hadoop jar com.kaikeba.hadoop-1.0-SNAPSHOT.jar  com.kaikeba.hadoop.hdfs.FileReadFromHdfs 参数1 参数2

11. 存储大量小文件

11.1 有没有问题
  • NameNode存储着文件系统的元数据,每个文件、目录、块大概有150字节的元数据;
  • 因此文件数量的限制也由NN内存大小决定,如果小文件过多则会造成NN的压力过大
11.2 如何解决
    1. HAR文件方案(本质启动mr程序,所以需要启动yarn)start-yarn.sh
  • MR把小文件合并为大文件,前提是要启动yarn

​ 合并小文件,通过HAR把小文件合并

1558004541101

# 创建archive文件--底层启动mr进行har
# 原文件还存在,需手动删除
[root@node1 ~]# hadoop archive -archiveName test2.har -p /data/hdfs_data/archive/ -r 3 a.txt b.txt c.txt /data/hdfs_data/outhar

-archiveName 打包后的名字 -p 要打包的路径 -r 副本数 打包的相对路径 打包后保存的路径

har://协议
# 查看archive文件
[root@node1 ~]# hdfs dfs -ls -R har:///data/hdfs_data/outhar/test2.har
-rw-r--r--   3 root supergroup          2 2021-07-28 09:57 har:///data/hdfs_data/outhar/test2.har/a.txt
-rw-r--r--   3 root supergroup          2 2021-07-28 09:57 har:///data/hdfs_data/outhar/test2.har/b.txt
-rw-r--r--   3 root supergroup          2 2021-07-28 09:57 har:///data/hdfs_data/outhar/test2.har/c.txt


# 解压archive文件
# 顺序
[root@node1 ~]# hdfs dfs -cp har:///data/hdfs_data/outhar/test2.har/a.txt hdfs:/data/hdfs_data/unarchivef

hdfs dfs -cp har:///指定解压的文件 hdfs:/解压后输出的路径

unarchivef里面的内容就是a.txt的内容
[root@node1 ~]# hdfs dfs -cat /data/hdfs_data/unarchivef
a

hadoop distcp har:///outhar/test.har/th1 hdfs:/unarchivef2 # 并行解压,启动MR
    1. Sequence Files方案(存储的时候,MapReduce的时候会讲)

其核心是以文件名为key,文件内容为value组织小文件

比如:10000个100KB的小文件

可以编写程序将这些文件放到一个SequenceFile文件,然后就以数据流的方式处理这些文件。也可以使用MapReduce进行处理。

一个SequenceFile是可分割的,所以MapReduce可将文件切分成块,每一块独立操作。

SequenceFile支持压缩,不像HAR。在大多数情况下,以block为单位进行压缩是最好的选择,因为一个block包含多条记录,压缩作用在block之上,比reduce压缩方式(一条一条记录进行压缩)的压缩比高。

把已有的数据文件转存为SequenceFile比较慢。比起先写小文件,再将小文件写入SequenceFile,一个更好的选择是直接将数据写入一个SequenceFile文件,省去小文件作为中间媒介。

普通的:一条记录一条的进行压缩

高效的:以block为单位进行压缩

syn(同步点)作用? 我们要定位record,但是每个record长度不一样,这时我们可以借助syn进行定位。

序列化:把对象变成一个字节数组通过网络传输出去

  • 向SequenceFile写入数据
package sequenceFile;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;

import java.io.IOException;


public class Write2SequenceFile {
    public static void main(String[] args) {
        IntWritable key=new IntWritable();
        Text value=new Text();
        String[] DATA = {
                "One, two, buckle my shoe",
                "Three, four, shut the door",
                "Five, six, pick up sticks",
                "Seven, eight, lay them straight",
                "Nine, ten, a big fat hen"
        };

        Configuration conf=new Configuration();

        //设置文件路径
        //hdfs:///data/hdfs_data/SequenceFile/test1
        SequenceFile.Writer.Option pathOption=SequenceFile.Writer.file(new Path(args[0]));
        //设置k-v
        SequenceFile.Writer.Option keyOption=SequenceFile.Writer.keyClass(IntWritable.class);
        SequenceFile.Writer.Option valueOption=SequenceFile.Writer.valueClass(Text.class);
        //设置压缩格式
        SequenceFile.Writer.Option compressionOption=SequenceFile.Writer.compression(SequenceFile.CompressionType.BLOCK);


        SequenceFile.Writer writer = null;
        try {
            writer = SequenceFile.createWriter(conf, pathOption, keyOption, valueOption, compressionOption);
			//模拟小文件
            for (int i=0;i<100;i++){
                //key是小文件的名
                key.set(i);
                //value是小文件的内容
                value.set(DATA[i%DATA.length]);
                //%s将后面的值插入这个位置
                System.out.printf("[%s]\t%s\t%s\n", writer.getLength(), key, value);
                //向SequenceFile写入数据
                writer.append(key,value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中
            IOUtils.closeStream(writer);
        }

    }

}
  • 读取SequenceFile文件

    • 小文件不是组成sequenceFile大文件了么,为什么读的时候还用sequenceFile代码进行读,不能正常的hdfs读么?生成的sequenceFile是字节码文件

      test1

      5345 5106 206f 7267 2e61 7061 6368 652e
      6861 646f 6f70 2e69 6f2e 496e 7457 7269
      7461 626c 6519 6f72 672e 6170 6163 6865
      2e68 6164 6f6f 702e 696f 2e54 6578 7401
      012a 6f72 672e 6170 6163 6865 2e68 6164
      6f6f 702e 696f 2e63 6f6d 7072 6573 732e
      4465 6661 756c 7443 6f64 6563 0000 0000
      2fde 5886 d3e4 ca0f 3526 3ef2 a14b 73ef
      
package sequenceFIle;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.ReflectionUtils;

import java.io.IOException;

public class ReadFromSequenceFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration();
		//args[0]指向SequenceFile文件位置
        SequenceFile.Reader.Option fileOption=SequenceFile.Reader.file(new Path(args[0]));
        SequenceFile.Reader reader=null;
        try {
            reader=new SequenceFile.Reader(conf,fileOption);
            //反射的创建kv
            Writable key= (Writable) ReflectionUtils.newInstance(reader.getKeyClass(),conf);
            Writable value= (Writable) ReflectionUtils.newInstance(reader.getValueClass(),conf);
            //获取当前位置
            long position=reader.getPosition();

            while (reader.next(key,value)){
                //同步点读*
                String synSeen=reader.syncSeen() ? "*" : "";
                System.out.printf("[%s%s]\t%s\t%s\n",position,synSeen,key,value);
                // beginning of next record
                //更新当前位置
                position=reader.getPosition();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            IOUtils.closeStream(reader);
        }
    }
}

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值