前言
Hadoop是一个专为离线和大规模数据分析而设计的,并不适合那种对几个记录随机读写的在线事务处理模式。
Hadoop=HDFS(文件系统,数据存储技术相关)+ Mapreduce(数据处理),Hadoop的数据来源可以是任何形式,在处理半结构化和非结构化数据上与关系型数据库相比有更好的性能,具有更灵活的处理能力,不管任何数据形式最终会转化为key/value,key/value是基本数据单元。
安装教程
有独立(本地)运行模式和伪分布式模式,完全分布式模式。
独立(本地)运行模式:无需任何守护进程,所有的程序都运行在同一个JVM上执行。在独立模式下调试MR程序非常高效方便。所以一般该模式主要是在学习或者开发阶段调试使用 。
伪分布式模式: Hadoop守护进程运行在本地机器上,模拟一个小规模的集群,换句话说,可以配置一台机器的Hadoop集群,伪分布式是完全分布式的一个特例。
完全分布式模式:Hadoop守护进程运行在一个集群上。参考 https://blog.csdn.net/lyhkmm/article/details/88037565
HDFS
Block数据
基本存储单位,一般大小为64M配置大的块主要是因为:
1、减少搜寻时间,一般硬盘传输速率比寻道时间要快,大的块可以减少寻道时间;
2、减少管理块的数据开销,每个块都需要在NameNode上有对应的记录;
3、对数据块进行读写,减少建立网络的连接成本减少管理块的数据开销,每个块都需要在NameNode上有对应的记录;
一个大文件会被拆分成一个个的块,然后存储于不同的机器。如果一个文件少于Block大小,那么实际占用的空间为其文件的大小。基本的读写类似于磁盘的页,每次都是读写一个块每个块都会被复制到多台机器,默认复制3份
NameNode
相当于一个领导者,负责调度。如存储文件的metadata(元数据),比如命名空间信息,块信息等。但是这些信息也可以持久化到磁盘上。运行时所有数据都保存到内存,整个HDFS可存储的文件数受限于NameNode的内存大小当。
一个Block在NameNode中对应一条记录(一般一个block占用150字节),如果是大量的小文件,会消耗大量内存。同时map task的数量是由splits来决定的,所以用MapReduce处理大量的小文件时,就会产生过多的map task,线程管理开销将会增加作业时间。处理大量小文件的速度远远小于处理同等大小的大文件的速度。因此Hadoop建议存储大文件
数据会定时保存到本地磁盘,但不保存block的位置信息,而是由DataNode注册时上报和运行时维护(NameNode中与DataNode相关的信息并不保存到NameNode的文件系统中,而是NameNode每次重启后,动态重建)。NameNode失效则整个HDFS都失效了,所以要保证NameNode的可用性。
Secondary NameNode
定时与NameNode进行同步(定期合并文件系统镜像和编辑日志;,然后把合并后的传给NameNode,替换其镜像,并清空编辑日志,类似于CheckPoint机制),需要注意的是NameNode失效后仍需要手工将其设置成主机。
DataNode
保存具体的block数据,负责数据的读写操作和复制操作。DataNode启动时会向NameNode报告当前存储的数据块信息,后续也会定时报告修改信息。DataNode之间会进行通信,复制数据块,保证数据的冗余性。
写文件
1、客户端将文件写入本地磁盘文件中时先写到临时文件中;
2、当临时文件大小达到一个block大小时(2.x之后是128M,之前是64M),HDFS client通知NameNode,申请写入文件;
3、NameNode在HDFS的文件系统中创建一个文件,并把该block id和要写入的DataNode的列表返回给客户端;
4、客户端收到这些信息后,将临时文件写入DataNodes:
4.1 客户端将文件内容写入第一个DataNode(一般以4kb为单位进行传输);
4.2 第一个DataNode接收后,将数据写入本地磁盘,同时也传输给第二个DataNode;
4.3 依此类推到最后一个DataNode,数据在DataNode之间是通过pipeline的方式进行复制的;
4.4 后面的DataNode接收完数据后,都会发送一个确认给前一个DataNode,最终第一个DataNode返回确认给客户端;
4.5 当客户端接收到整个block的确认后,会向NameNode发送一个最终的确认信息;
4.6 如果写入某个DataNode失败,数据会继续写入其他的DataNode。然后NameNode会找另外一个好的DataNode继续复制,以保证冗余性;
4.7 每个block都会有一个校验码,并存放到独立的文件中,以便读的时候来验证其完整性;
5.文件写完后(客户端关闭),NameNode提交文件(这时文件才可见,如果提交前,NameNode垮掉,那文件也就丢失了。fsync:只保证数据的信息写到NameNode上,但并不保证数据已经被写到DataNode中)
HDFS的文件写入原理,主要包括以下几个步骤:
1、客户端通过调用 DistributedFileSystem 的create方法,创建一个新的文件。
2、DistributedFileSystem 通过 RPC(远程过程调用)调用 NameNode,去创建一个没有blocks关联的新文件。创建前,NameNode 会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,NameNode 就会记录下新文件,否则就会抛出IO异常。
3、前两步结束后会返回 FSDataOutputStream 的对象,和读文件的时候相似,FSDataOutputStream 被封装成 DFSOutputStream,DFSOutputStream 可以协调 NameNode和 DataNode。客户端开始写数据到DFSOutputStream,DFSOutputStream会把数据切成一个个小packet,然后排成队列 data queue。
4、DataStreamer 会去处理接受 data queue,它先问询 NameNode 这个新的 block 最适合存储的在哪几个DataNode里,比如重复数是3,那么就找到3个最适合的 DataNode,把它们排成一个 pipeline。DataStreamer 把 packet 按队列输出到管道的第一个 DataNode 中,第一个 DataNode又把 packet 输出到第二个 DataNode 中,以此类推。
5、DFSOutputStream 还有一个队列叫 ack queue,也是由 packet 组成,等待DataNode的收到响应,当pipeline中的所有DataNode都表示已经收到的时候,这时akc queue才会把对应的packet包移除掉。
客户端完成写数据后,调用close方法关闭写入流。
6、DataStreamer 把剩余的包都刷到 pipeline 里,然后等待 ack 信息,收到最后一个 ack 后,通知 DataNode 把文件标示为已完成。
读文件
1、客户端向NameNode发送读取请求;
2、NameNode读回文件的所有block和这些block所在的DataNodes(包括复制节点);
3、客户端直接从DataNode中读取数据,如果该DataNode读取失败(DataNode失效或校验码不对),则从复制节点中读取(如果读取的数据就在本机,则直接读取,否则通过网络读取)。
具体文件读取原理,主要包括以下几个步骤:
1、首先调用FileSystem对象的open方法,其实获取的是一个DistributedFileSystem的实例。
2、DistributedFileSystem通过RPC(远程过程调用)获得文件的第一批block的locations,同一block按照重复数会返回多个locations,这些locations按照hadoop拓扑结构排序,距离客户端近的排在前面。
3、前两步会返回一个FSDataInputStream对象,该对象会被封装成 DFSInputStream对象,DFSInputStream可以方便的管理datanode和namenode数据流。客户端调用read方法,DFSInputStream就会找出离客户端最近的datanode并连接datanode。
4、数据从datanode源源不断的流向客户端。如果第一个block块的数据读完了,就会关闭指向第一个block块的datanode连接,接着读取下一个block块。这些操作对客户端来说是透明的,从客户端的角度来看只是读一个持续不断的流。
5、如果第一批block都读完了,DFSInputStream就会去namenode拿下一批blocks的location,然后继续读,如果所有的block块都读完,这时就会关闭掉所有的流。
可靠性
DataNode可以失效,DataNode会定时发送心跳到NameNode。如果在一段时间内NameNode没有收到DataNode的心跳消息,则认为其失效。此时NameNode就会将该节点的数据(从该节点的复制节点中获取)复制到另外的DataNode中。
数据可以毁坏,无论是写入时还是硬盘本身的问题,只要数据有问题(读取时通过校验码来检测),都可以通过其他的复制节点读取,同时还会再复制一份到健康的节点中。
NameNode不可靠。