大数据的概述
大数据5V特征
- 数据体量大(体积大):VOLUME
- 数据的种类和来源多:VARIETY
- 种类:结构化、半结构化、非结构化
- 数据的增长速度越来越快:VELOCITY
- 数据的价值密度越来越低:VALUE
- 数据的真实性:VERACITY
Hadoop
Hadoop简介
- 由Yahoo!开发的后来贡献给Apache的一套开源的、可靠的、可伸缩的(可扩展)的分布式存储和计算的系统。
- 版本有Hadoop1.0、Hadoop2.0(常用)、Hadoop3.0
Hadoop模块
- Hadoop Common:公共模块
- Hadoop Distribute File System(HDFS):分布式文件系统
- Hadoop YARN:任务分配和资源调配、任务调度和资源管理
- Hadoop MapReduce:完成分布式计算
- Hadoop Ozone:对象的存储
发展的版本
- Hadoop1.0包含Common、HDFS、MapReduce
- Hadoop2.0包含Common、HDFS、MapReduce、YARN,2.7版本中开始支持Ozone。Hadoop1.0和Hadoop2.0不兼容
- Hadoop3.0包含Common、HDFS、MapReduce、YARN、Ozone,Hadoop2.0和Hadoop3.0部分版本兼容
三种安装形式
- 单机:只能启动MapReduce模块
- hadoop默认安装方式,当首次解压安装时,hadoop无法了解硬件的安装环境,要保守的选择了最小化安装,在这种安装方式下是最小的配置,在这种方式下XML配置文件的内容均为空,当配置为空时,hadoop会完全地运行在本地,意味着hadoop只会和本机进行交互,因为不需要和其他节点(服务器)进行交互。单机模式下不会使用HDFS,也不加载hadoop的任何守护线程
- 伪分布式:启动Hadoop的大部分功能模块
- 守护线程运行在本地服务器上,模拟一个小规模的集群环境,可以使用HDFS和MapReduce
- 完全分布式:启动Hadoop的所有功能模块
- 守护线程运行在一个集群中,启动所有的守护线程,具有Hadoop的完整功能,可以使用HDFS、MapReduce和Yarn,并且这些守护线程运行在集群中,可以真正的利用集群提供高性能的任务(完全分布式是实际开发中使用的模式)
Hadoop的起源
- 在2002年,Doug和Mike仿照Google设计了Nutch项目(开源)
- 2003年,Google发表了论文《The Google File System》谷歌的文件存储系统,简称GFS,阐述了Google的分布式文件系统存储的原理,但是Google并没有公开GFS的使用以及原理
- 2004年,Doug根据GFS设计了NDFS(Nutch Distributed File System),NDFS是为Nutch而设计的分布式文件系统,解决了数据存储的问题
- 2004年,Google发表了论文《The Google MapReduce》阐述了分布式计算的思想,同样没有公开源代码
- Doug根据这篇论文设计MapReduce
- 从Nutch0.8版本开始,Doug将NDFS和MapReduce独立出来,成为Hadoop1.0版本,并且将NDFS重名为HDFS
- 在2008年,Doug从原公司离职带着Hadoop去了Yahoo!
- 在Yahoo!期间,将Hadoop进行了开源,并且设计实现了Hive、HBase、Pig等框架
HDFS
概述
- 是Hadoop的一个组件,用来进行文件的存储
- 是根据Google的论文《The Google File System》设计实现的
- 本身也是一个可扩展、可靠的、便于操作的存储系统
细节
- 在HDFS中,存在一个名称节点NameNode和数据节点DataNode
- 在HDFS中,NameNode是核心节点(主节点/管理节点)
- 当往HDFS中存放数据时,数据往往会进行切块拆分成一个个的数据块-Block
- 为了防止由于节点宕机而导致的数据丢失,需要对Block进行备份(全分布式的复本数量默认是3个,伪分布式只能是1个)——分布式存储,复本的数量通过dfs.replication进行设置
- HDFS中要记录元数据
- 元数据:文件的存放路径、文件所对应的切块、切块所存储的节点、切块复本对应的备份
Block
- 本身是一个数据块,是HDFS中数据存储的基本单位
- 在Hadoop2.0中,每一个Block默认是128M,而在Hadoop1.0中每一个Block为64M
- 如果一个数据块不足128M,那么这个数据的本身大小就是数据块的大小
- 切块的意义:
- 能够存储超大的文件
- 便于进行快速的备份,有效地提高效率
NameNode
- 是HDFS的核心节点(主节点)
- 在Hadoop1.0的完全分布式以及Hadoop2.0的伪分布式中,NameNode有且仅有一个,意味着存在单点故障问题
- 在Hadoop2.0的完全分布式下可以对NameNode进行备份
- 到目前为止,集群中能且仅能最多存在两个NameNode,意味着放弃了SecondaryNameNode。
- NameNode的作用是记录元数据:
- 文件的存储位置
- 文件块的复本数量
- 文件的切块数量
- 切块的存储位置
- 元数据存储在磁盘和内存中:
- 元数据存储在内存中的意义是为了能够进行快速的查询
- 元数据存储在磁盘中的意义是为了永久保存从而保证崩溃恢复
- 元数据在磁盘中的存储位置是由hadoop.temp.dir决定的,在指定的目录的子目录dfs/name下存储文件
- fsimage:记录元数据,fsimage里面的元数据并不是实时的元数据,记录的是历史的元数据
- edits:记录用户请求的操作,存放实时的元数据
- 当客户端发起请求到NameNode时,NameNode会将这个请求先写到edits文件中,当edits文件写入成功时,会更改内存中的元数据。如果内存中的元数据修改成功,内存就会给客户端返回成功。(先写edits(硬盘)再写内存)。
- 内存的元数据=edits操作+fsimage元数据
- 每来一个请求都要记录到edits文件中,由于edits文件也有容量的限制,意味着edits文件到达一定大小的时候必须把这些操作更新到fsimage。因为edits文件在磁盘中存放,所以文件的大小也就有要求而并不是无限大,当到达一个限度时就要往fsimage中更新数据。
- edits文件的操作更新到fsimage中,这个过程在hadoop2.0版本的伪分布式,需要SecondaryNameNode的支持,但是在完全分布式下,由于设置了两个NameNode,所有舍弃了SecondaryNameNode,因为在完全分布式中,更新操作是在NameNode中发生的。
- 如果是在一个NameNode和一个SecondaryNameNode,那么更新操作是在SecondaryNameNode中
- 如果是两个NameNode,那么更新操作只能发生在NameNode中。
- 元数据在内存中也在磁盘中,在内存中是实时更新的,在磁盘中先记录后边的操作再进行合并更新。
- 在HDFS中,每一次NameNode启动都会触发一次edits和fsimage的合并操作
- NameNode除了记录元数据外,还要管理DataNode,通过RPC通信进行心跳检测机制来管理DataNode
- 心跳机制是指DataNode每间隔固定时间主动向NameNode发送一次信号
- 心跳的信息包括:
- 当前节点的状态(例如:当前节点是否存活和剩余的存储空间)
- 当前节点存储的数据(例如:存储的是哪些数据块)
- 心跳的信息包括:
- 当NameNode重新启动时,会将edits和fsimage进行合并操作,合并之后把元数据更新到fsimage中,然后再将元数据加载到内存中,以便让内存中存放的也是最新的数据。之后会等待DataNode的心跳。
- 等待DataNode的心跳的作用是:
- NameNode检查集群中是否还有个别的DataNode处于无法正常启动的状态
- 保证数据不会丢失并且保证复本的数量
- 等待DataNode的心跳的作用是:
- 在安全模式下HDFS只能读不能写。hadoop启动时检查复本块数,就会进入safemode
- 在安全模式下会对数据进行校验(数据的完整性和数据的复本数量是否足够)
- 如果长期处于安全模式下,那么以为着数据可能发生了不可逆的损坏,需要手动强制退出安全模式
- 命令:hadoop dfsadmin -safemode leave
- 在伪分布式下,如果将复本数量设置为3,那么它必定会导致HDFS一直处于safemode而无法自动退出,所以必须将复本的数量设置为1
- edits和fsimage的合并条件:
- 时间:通过fs.checkpoint.period属性来决定这两个文件的合并时间,这个属性默认单位是秒,则默认的时间是3600s。
- 空间:通过fs.checkpoint.size属性来决定edits文件的大小,默认是64M,当edits文件达到这个大小时就和触发合并操作
- 手动合并:通过hadoop.dfsadmin -rollEdits命令
- NameNode重启:这种合并方式在实际生产环境中用的比较少
- 每一条元数据大概是150B左右
- NameNode端口号50070;DataNode端口号50075;SecondaryNameNode端口号50090
复本放置策略
在HDFS中保证数据的完整性采用的是多复本策略,其中上传的数据往往称之为第一复本。
- 第一复本:
- 如果是从集群内部上传数据,即:DataNode作为客户端来进行上传,从某一个DataNode中上传,那么从哪个DataNode上传,第一复本就放置在哪一个DataNode上
- 如果是从集群外部的客户端上传的话,NameNode选择一个相对来说比较空闲的DataNode来存储这个复本,从而保证每个服务器节点上存储的数据块可以达到均衡的效果
- 第二复本:
- 放置在和第一复本不同机架的节点上
- 第三复本:
- 放置在和第二复本相同机架的节点上(考虑到数据传输速率的问题)
- 更多复本:如果有更多的复本,这些复本都是随机放置在相对空闲的节点上的
机架感知策略
概述
-
机架感知策略就是告诉Hadoop集群中某一个机器属于哪一个机架
-
所谓的机架感知策略实际上是通过映射来建立逻辑机架,这种情况意味着可以将不同物理机架上的节点放置在同一个逻辑机架中。在实际开发中,往往是要将同一个物理机架上的节点放置在同一个逻辑机架中。
-
Hadoop的机架感知策略的功能默认是未启用的,如果要启用,需要在NameNode所在机器上的core-site.xml文件中配置
-
如果将不同物理机架上的节点放置在同一个逻辑机架上,那么会导致在实际开发中的数据管理很麻烦,所以往往一个物理机架对应一个逻辑机架。但是一个逻辑机架并不一定对应一个物理机架,可以将2~3个物理机架放置在同一个逻辑机架中。
SecondaryNameNode
- SecondaryNameNode本身不是NameNode的热备份(实时备份),只是辅助NameNode进行元数据的合并操作,但是SecondaryNameNode也能起到一定的备份作用
- 在有SecondaryNameNode的情况下,fsimage和edits的合并过程是发生在SecondaryNameNode上的:
- 合并过程:
- SecondaryNameNode通过Http的get请求获取到NameNode的fsimage和edits文件,同时在NameNode中产生了一个edits.new文件,在合并过程中的后续操作是写在edits.new这个文件中的。
- 在SecondaryNameNode需要将fsimage加载到内存中,然后将edits中的记录的操作也更新到内存中,然后将内存中的数据写到新文件fsimage.ckpt中。
- 通过http请求将fsimage.ckpt拷贝到NameNode中,然后NameNode中将fsimage.ckpt文件重命名为fsimage,并且将edits.new文件重命名为edits。合并过程完成。
- SecondaryNameNode虽然可以起到一定的备份作用,但是还是会产生数据的丢失
- 在Hadoop2.0的完全分布式下,舍弃了SecondaryNameNode,而是采用了2个NameNode机制,实现NameNode 的热备份,这也就以为着在完全分布式下,合并的过程就只能发生在NameNode上。
- 如果NameNode和SecondaryNameNode共存,那么这两个节点应该是分布在两个不同的主机上,如果在同一台主机上,两者会争抢内存,势必会导致争抢失败的一方效率降低。
HDFS常用命令
- 在HDFS的根目录下创建一个test目录:hadoop fs -mkdir /test
- 上传当前目录下的test.txt到分布式文件系统的根:hadoop fs -put test.txt /
- 删除文件:hadoop fs -rm /test.txt
- 删除目录:hadoop fs -rmdir /test
- 创建多级目录:hadoop fs -mkdir -p /a/b/c/d/e
- 删除多级目录:hadoop fs -rmr /a
- 下载文件:hadoop fs -get /test.txt
- 下载文件到指定位置并修改名称:hadoop fs -get /test.txt /home/a.txt
- 展示目录内容:hadoop fs -ls /
- 查看文件的最后1000个B:hadoop fs -tail /test.txt,此命令适用于文件较大的场合
- 创建一个空文件:hadoop fs -touchz /test.txt,文件的大小为0
- 文件合并:hadoop fs -getmerge /a.txt /b.txt /c.txt,将hadoop上的a和b合并到本地的c(而不是HDFS),如果指定的是一个目录,则合并目录下的所有文件
- 查看文件的基本信息:hadoop fsck /1.txt -files -blocks -locations -racks:
- -files:表示该文件所在的目录
- -blocks:表示该文件所对应的文件数据块
- -locations:表示该文件存放的位置
- -racks:表示该文件所处的机架
DataNode
- 是用来存储数据的,其中数据是以Block形式存在的
- Block存放的路径:/home/software/hadoop-2.7.1/tmp/dfs/data/current/BP-70731579-192.168.197.128-1596529170963/current/finalized/subdir0/subdir0
- DataNode通过心跳机制来向NameNode发送信号
- DataNode每隔3秒向NameNode发送一次信号
- 信号包含的信息有:
- 当前节点的状态
- 存储的数据信息
- 信号包含的信息有:
- 如果NameNode等待超过10min没有收到DataNode的心跳,那么就会认为这个DataNode已经lost
- DataNode自身引起的宕机
- 网络波动
- 如果NameNode发现某一个DataNode丢失,那么NameNode就会将这个DataNode上的Block再次进行备份以保证复本的数量
垃圾回收机制
-
在HDFS中,垃圾回收机制是默认不开启的
-
需要手动在core-site.xml配置文件中进行配置:
fs.trash.interval 1440 -
当配置完成后,删除文件会发现提示信息:
Moved: ‘hdfs://hadoop01:9000/1.txt’ to trash at: hdfs://hadoop01:9000/user/root/.Trash/Current
表明删除的文件已经移动到了/user目录下
-
可以通过hadoop fs -lsr /user查看发现之前删除的文件会在Current目录下,通过浏览器无法访问user目录。如果想还原hadoop fs -mv /user/root/.Trash/Current/1.txt /now.txt
tmp/dfs目录
- 用于存储HDFS中的数据:
- 元数据
- 数据块
- 在执行格式化之后会在指定的目录下(core-site.xml中指定的)产生dfs目录
- dfs目录中有name、data、namesecondary三个目录,但是在完全分布式下,这三个目录实际上是在不同的服务器节点下
- 当NameNode每格式化一次,就会随机产生一个clusterId,这个clusterId的值在version文件中,当HDFS集群启动的时候,NameNode就会将他的clusterId发送给每一个DataNode
- in_use.lock表示当前节点已经启动,为了防止在同一个主机上重复启动节点,关闭节点,in_use.lock文件即消失。如果再次启动,就会寻找是否存在该文件,如果有的话意味着该节点已经启动了
- 在HDFS中,会对每一次写操作分配一个事物ID——txid
- 上传文件的事物操作:
- OP_ADD:创建空文件
- OP_ALLOCATE_BLOCK_ID:分配Block ID
- OP_SET_GENSTAMP_V2:分配时间戳
- OP_ADD_BLOCK:添加数据
- OP_CLOSE:关流
- OP_RENAME_OLD:重命名
- HDFS在第一次启动1min之后会进行一次edits和fsimage的自动合并,第一次合并之后就会按照配置的时间进行合并操作
- 查看edits文件:hdfs oev -i edits_inprogress_000000000000000000001 -o a.xml
- -i:表示输入
- -o:表示输出
- 查看fsimage文件:hdfs oif -i fsimage_0000000000000000000000 -o b.xml -p XML
- -p:表示再次指定格式为XML
执行流程
-
写入流程
- 客户端访问HDFS通过RPC请求访问NameNode
- NameNode在收到请求之后,校验客户端的权限,校验指定路径下是否有同名的文件,如果校验成功,首先计算文件的大小,计算文件切块的数量,为每一个Block分配对应的存储节点,产生可对应的元数据
- NameNode会将Block所分配的地址放入一个队列,然后返回给客户端
- 客户端会根据切块的数量将文件切块,会将每一个块封装成为一个packet对象,准备发送
- 客户端会从节点中选择一个较近的节点,将packet对象发送过去,这个节点通过通过pipeline(管道)将数据复制到其他节点上(底层是NIO的channel)
- 如果都写成功了,第一个节点返回一个ack给客户端表示成功
-
读取流程
- 客户端通过RPC请求访问NameNode
- NameNode在接收到请求之后会查询元数据,通过元数据获取到这个文件对应的切块数量和切块地址
- NameNode会将这个文件所对应的的全部或者部分Block的地址放入一个队列中返回给客户端
- 客户端从队列中取出第一个Block对应的地址,然后从三个地址(备份完总共三份,每一个Block有三个地址)中选取一个网络相对而言较近的节点进行读取操作
- 读取完一个Block之后,会对这个Block进行一次校验checksum校验这个Block的完整性,如果校验成功,则获取下一个Block的地址并且读取下一个Block中的数据;如果校验失败,客户端会先发送信息给NameNode报告损坏的Block信息,然后再从另一个节点上读取Block,然后NameNode重新备份一份以确保备份数量。
- 当客户端将这一次所有的Block都读取完成,会向NameNode要下一批Block的地址
- 当客户端全部读取完成之后,通过NameNode关闭文件(关流)
-
删除流程:
- 客户端通过RPC向NameNode发送请求
- NameNode接收到请求之后会将这个操作记录到edits,记录完成后会修改内存中的元数据,修改完成之后会给客户端返回一个ack信号表示删除成功,但是此时只是这个文件所对应的的元数据被删除了,这个文件本身还是依然存在在DataNode上
- 当DataNode发起下一次心跳的时候,NameNode就会检查这个节点上的数据信息,如果发现元数据中的信息和DataNode发送的信息不一致就会给DataNode对应的指令要求删除对应的Block,此时这个文件才是真正的被移除了
HDFS的优缺点
优点:
- 支持超大文件
- 检测和快速应对硬件故障
- 流式数据访问
- 简化的一致性模型
- 高容错性
- 可以构建在廉价的机器上
- 纵向:在不改变数量的前提下来提高集群的性能
- 横向:在增加数量的前提下来提高集群的性能
缺点:
- 做不到低延迟的数据访问
- 不建议存储大量的小文件(每存储一个文件都要生成150B的元数据)
- 不支持多用户写入文件、修改文件
- 不支持事务
Maven依赖管理
Maven中实现项目的管理,使用一些插件、Jar包等内容,都是作为资源需要保存在一个库(repository)。例如:项目开发SSM框架使用的那些jar包来进行管理和存储,Maven的库分为两种:远程库和本地库
Maven默认使用的远程库叫做Maven的中央库(集合了全世界最大的jar包资源),由Maven社区取维护的,我们可以使用这种远程库不能进行资源的编辑、新增、修改、只能是读取。Maven社区将全世界常用的一些资源加入到中央库中,供程序开发人员使用
只要客户端(Maven的运行终端)需要网络连接远程服务器获取服务器中的资源,这个远程服务器中的资源就是远程库。
远程库
远程库分为两种:中央库(不能写、不能改、只能读)、远程私服(可以按照权限进行资源的修改,并且可以利用远程私服代理访问中间库)
本地库:
如果每次Maven运行都要占用网络带宽实现资源下载,这样会浪费时间,开发效率并不靠,Maven准备了本地库,作为资源的缓存使用,一旦通过远程库下载库资源,将会保存在Maven的本地,供后续多次使用
配置Maven的库
如果网络连接的是Maven的中央库,外国的服务器访问的速度非常慢,可以通过配置国内的镜像,解决该问题,实现代理访问中央库,所以国内网速可以迅速下载资源。
MapReduce
简介
- MapReduce是基于Yarn的分布式计算系统
- MapReduce是Doug仿照Google MapReduce设计实现的
- MapReduce会将计算过程拆分为2个阶段:Map(映射)阶段和Reduce(规约)阶段
windows下Hadoop的配置
HADOOP_HOME(安装路径)、HADOOP_USER_NAME(root)、Path中添加HADOOP_HOME\bin
运行项目如果出现null\bin\winutils解决方案:
-
先检查环境变量是否配置正确
-
如果环境变量配置正确,那么在Driver中添加
System.setProperty(“hadoop.home.dor”,“dadoop的实际安装路径”);
序列化:Writable
- 在MarReduce中要求被传输的数据能够被序列化
- MarReduce中的序列化默认使用的时AVRO,但是在AVRO的基础之上进行了封装提供了更加简单的序列化形式:writable
- 在实际生产过程中,需要根据开发文档或者产品文档来将必须属性来写出,而非必要属性可以不写
- 由于AVRO的限制,要求被序列化的对象的属性值不能为null
分区——Partitioner
- 作用:用于对数据进行分类的
- 如果要自定义分区,需要定义一个类继承Partitioner类,覆盖其中的getPartition()方法来制定分区逻辑
- 在MapReduce中要进行分区,默认对分区进行编号的,默认从0开始编号
- 在MapReduce中,默认有一个分区,ReduceTask的数量默认就只有一个,ReduceTask的数量是由分区数量决定的
- 每一个分区都要对应一个ReduceTask,每一个ReduceTask都会产生一个结果文件。
排序
- MapReduce会默认对键进行自然排序,也因为要求放在键的位置上的元素对应的类必须实现一个接口Comparable
- 考虑到键既需要被序列化有需要能够排序,所以事先WritableComparable接口
- 如果两个键的CompareTo结果为0,那么Reduce阶段会将这两个键看做是同一个键,然后将对应的值分到一组
- 如果针对多字段排序,称之为二次排序
合并——Combiner
- Combiner的特点:减少数据的体量但是比改变计算结果
- 默认情况下,ReduceTask只有一个,这就导致ReduceTask的计算压力相对较大,甚至ReduceTask的计算效率会成为整个MapReduce程序的瓶颈
- Combiner和Reducer的逻辑实现是相同的
- 设置Combiner
- Combiner能够有效地提高MapReduce程序的效率,但是不是所有的场景都适合于使用Combiner,例如:求和、求最值、去重等可传递运算的场景都是可以使用Combiner。例如:求平均值等不可传递的场景不能使用Combiner
InputFormat:输入格式
- InputFormat在Mapper之前来执行,用于读取HDFS上的数据,然后将读取到的数据给Mapper,所有InputFormat读出来的数据是什么格式,那么Mapper接收到的数据就是什么格式
- InputFormat提供了两个抽象方法:
- getSplits:计算切片
- createRecordReader:提供输入流来读取数据
- 在MapReduce中,如果不指定则默认使用的输入格式类是TextInputFormat,默认情况下切片依靠FileInputFormat,
- 自定义输入格式:首先定义一个类继承InputFormat,这个类在考虑切片过程相对而言较为复杂,所以一般在实际开发中会继承FileInputFormat
- 多源输入:在MapReduce中,允许指定多个输入路径,如果多个输入路径之间的文件内容没有关联,那么就可以使用多源输入
数据本地化策略
-
当ResourceManager接受请求后,会向NameNode发送请求用于获取文件的元数据,NameNode会给ResourceManager返回文件的元数据信息
-
ResourceManager在划分切片之后,每一个切片会对应地产生MapTask来处理,JobTracker(ResourceManager)会将MapTask分配个TaskManager(NodeManager)
-
Split(切片)是一种逻辑切分而不是物理切分,实际上就是在划分任务量,根据任务量来确定产生的线程数(MapTask)
-
Block是一种物理切分,真正的将数据切开进行存储
-
数据本地化策略:
- 在实际生产过程中,会考虑将DataNode和TaskTracker部署在相同的节点上,这样做的目的是为了减少跨集群的数据传输
- 在分配任务时,考虑尽量将任务分配到有对应数据的DataNode上,这样做就保证了数据时从本读取,减少了集群中节点之间的网络消耗
- 切片问题:
-
如果是空文件,则整个文件作为一个切片来进行处理
-
文件存在可切和不可切两种,例如:绝大部分的压缩文件是不可切的。如果文件不可切,则整个文件作为一个切片来进行处理
-
默认情况下,Split和Block大小一致
-
如果要调小SplitSize,那么需要调小maxSize;如果需要调大SplitSize,那么需要调大minSize
-
FileInputFormat.setMaxInputSplitSize(); FileInputFormat.setMinInputSplitSize();
-
在切片的过程中,存在切片与之SPLIT_SLOP=1.1,如果剩余文件大小/spliSize>1.1才会继续切,不然剩下的数据就会作为一个切片来处理
- job的执行流程:
- 提交阶段
- 检查输入输出的路径
- 计算切片
- 如果有必要,可以设置分布式的缓存存根账户信息
- 将任务的jar包和配置信息存储到HDFS上
- 将任务提交JobTracker,并且可以选择监控任务的执行
- 执行阶段
- 拆分任务:JobTracker在收到任务之后,会先将任务拆分成子任务(MapTask和ReduceTask),其中MapTask的数量由切片数量决定的,ReduceTask的数量是由分区数量决定的
- 分配任务:JobTracker在拆分任务完成之后,等待TaskTracker的心跳,收到心跳之后,会给TaskTracker来分配任务,在分配任务的时候,MapTask要尽量考虑数据本地化策略,ReduceTask无法考虑数据本地化,所以在实际开发中,一般将ReduceTask分配到相对空闲的节点上
- 下载Jar:TaskTracker通过心跳机制领取到了任务,注意:可能领取到的任务不止一个。TaskTracker会发送请求到对应节点来下载Jar包(包含的是执行逻辑)——体现:逻辑移动、数据固定
- 执行任务:TaskTracker在本节点(服务器)上开启一个JVM子进程来执行任务,默认情况下,每一个任务都会开启和关闭一次/JVM子进程,这就导致TaskTracker在频繁的开关JVM子进程,造成资源的浪费。
Shuffle过程
-
Map端的Shuffle
- map方法在产生数据之后,并不是直接把数据发送到Reduce端,而是先将数据临时地存储在临时缓冲区中(memory buffer)(本质上是一个环形的字节数组)
- 数据在缓冲区会进行分区(Partition)、排序(Sort),如果指定了Combiner还会进行Combiner操作,采用的时快速排序
- 每一个MapTask都会自带一个缓冲区,缓冲区维系在内存中,默认大小是100M
- 当缓冲区使用到指定的阈值(0.8)的时候,会产生一次溢写(spill),将缓冲区中的数据溢写到硬盘形成溢写文件。单个溢写文件中的数据是分区且排好序的
- 溢写之后,新来的数据依然写到缓冲区中,如果再次达到阈值还会产生溢写,每一次溢写都会产生一个新的溢写文件,所有的溢写文件之间的数据是整体无序局部有序的
- 当MapTask处理完所有的数据之后,会将所有的溢写文件合并(Merge-不改变数据量)到一个文件中,形成结果文件(final out)
- 在merge过程中,会对数据再次进行分区排序,所以最后产生的final out是整体分区且排序的,如果指定了Combiner且溢写文件个数≥3个,那么在merge过程中会再次进行Combiner,采用的是归并排序
注意事项
- 缓冲区本质是一个环形字节数组,减少了重复的寻址过程
- 阈值的作用是为了减少阻塞
- 溢写过程不一定发生
- 原始数据大小不能决定溢写次数,取决于map方法处理完成之后的数据量
- 溢写文件的大小要考虑序列化因素
Reduce端的Shuffle
- 当达到启动阈值(默认是0.05,即当有5%的MapTask结束)的时候,ReduceTask就去启动区MapTask抓取数据
- ReduceTask启动fetch线程通过http的get请求(携带分区号)的方式来抓取数据,在抓取数据时之抓取当前要处理的分区的数据
- ReduceTask会将抓取来的每一段数据临时存储在本地磁盘上,当抓取完成之后,会将这些临时文件merge成一个大文件。在Merge过程中,会对数据再次进行排序,这些排序使用的是归并排序
- merge完成之后,将这个文件中相同的键对应的值分到一组区,形成一个迭代器,这个过程成为分组(group)
- 形成迭代器之后,每一个键调用一次reduce方法
注意事项
- 启动阈值设置的目的是为了提高执行效率用于节省时间
- 每一个ReduceTask最多只能去5个MapTask中取抓取数据(最多只能启动5个fetch线程)
- Merge factor(合并因子)默认是10,即每10个小文件合并一次,最终合并成一个大文件
Shuffle的优化
- 增大缓冲区,实际开发中缓冲区大小一般是250M~400M
- 增大溢写阈值(默认0.8),可能导致阻塞的风险增加(不建议)
- 考虑增加Conbiner过程,减少数据总量但不改变计算结果
- 考虑将final out压缩之后再传输,但是解压和压缩过程需要耗费时间和CPU,这种方案是网络资源和压缩时间的一个取舍
- 调节ReduceTask的启动阈值(默认0.05)(不建议)
- 适当地增加fetch线程的数量,一般而言在实际开发中这个值会在5000~10000
- 考虑增大merge因子(不建议)
关于切片的注意事项
如果不是压缩文件,就直接切
如果是可切的压缩文件(只有bzip2)那么直接切
如果不是可切的压缩文件(zip、rar等),先解压后再切
Join
- 如果两个或者多个文件之间相互关联,那么此时就需要使用Join操作
- 分析需求确定好键值之后,就可以确定哪个文件作为主处理文件,其他的关联文件需要放在缓存中,在Mapper处理的时候,先将缓存中的文件取出来先处理,然后再利用Map方法来处理主文件,在处理主文件的过程中,如果需要使用到缓存中的内容,那么就可以直接获取
小文件
- 在大数据的环境中,小文件存在的问题:
- 存储:大量的小文件,就会产生大量的元数据,导致内存被大量占用,降低查询效率
- 计算:大量的小文件,就会产生大量的切片,大致产生大量的线程(MapTask),增加服务器(集群)的并发压力
- 市面上针对小文件的处理手段只有两种:合并和打包
- Hadoop提供了原生的打包方式:hadoop archive
Hadoop完全分布式搭建
DataNode和NodeManager存在于同一主机
-
先将Hadoop的伪分布式保留下来
mv haoop-2.7.1 hadoop-alone
-
三台虚拟机关闭防火墙
service iptables stop
chkconfig iptables off
-
三台虚拟机修改主机名称
vim /etc/sysconfig/network
修改hostname属性,第一台修改为hadoop01,第二台修改为hadoop02,第三台修改为hadoop03
-
三台虚拟机免密互通
- ssh-keygen
- ssh-copy-id root@hadoop01
- ssh-copy-id root@hadoop02
- ssh-copy-id root@hadoop03
-
测试免密是否成功
- ssh hadoop01 登录时不需要密码则logout
- ssh hadoop02 登录时不需要密码则logout
- ssh hadoop03 登录时不需要密码则logout
-
在第一台虚拟机上解压hadoop的安装包
- tar -xvf hadoop-2.7.1_64big.tar.gz
-
进入hadoop的配置文件目录hadoop-env.sh文件进行JDK和hadoop的配置文件目录,配置完成后需要让该文件重新生效
-
配置过程略
-
将解压目录远程拷贝到其他两台主机上
- scp -r hadoop-2.7.1 root@hadoop02:/home/software
- scp -r hadoop-2.7.1 root@hadoop03:/home/software
-
配置三台主机的环境变量/etc/profile,保存退出,然后重新生效