Hadoop学习-概述与mapreduce
Hadoop
概述
什么是Hadoop
Hadoop是一个适用于海量数据的分布式存储和分布式计算的平台。
Hadoop三大基本组件
YARN、MapReduce、HDFS
分布式文件系统
FS、GFS(谷歌的分布式文件系统)、HDFS(Hadoop专有的分布式文件系统)
为什么需要使用分布式文件系统
-
文件存放在一个磁盘上效率肯定是最低的
读取效率低
如果文件特别大会超出单机的存储范围
文件在磁盘真实存储文件的抽象概念
数组可以进行拆分和组装,源文件不会收到影响
3. 切分数据
对字节数组进行切分
4. 拼接数据
按照数组的偏移量将数据连接到一起,将字节数组连接到一起
5. 偏移量
当前数据在数组中的相对位置,可以理解为下标
数组都有对应的索引,可以快速定位数据
6. 数据存储的原理
不管文件的大小,所有的文件都是由字节数组构成
如果我们要切分文件,就是将一个字节数组分成多份
我们将切分后的数据拼接到一起,数据还可以继续使用
我们需要根据数据的偏移量将他们重新拼接到一起
Block块
什么是Block块
-
是磁盘进行数据 读/写的最小单位,数据被切分后的一个整体被称之为块
-
.在Hadoop 1默认大小为64M,在Hadoop 2及其之后默认大小为128M块,这么大是为了最小化寻址开销
-
同一个文件中,每个数据块的大小要一致除了最后一个节点外不同文件中,块的大小可以不一致,文件大小不同可以设置不同的块的数量,HDFS中小于一个块的大小的文件不会占据整个块的空间
-
真实情况下,会根据文件大小和集群节点的数量综合考虑块的大小
-
数据块的个数=Ceil(文件大小/每个块的大小)
注意事项
-
只要有任意一个块丢失,整个数据文件被损坏
-
HDFS中一旦文件被存储,数据不允许被修改,修改会影响偏移量,导致数据倾斜(单节点数据量过多)、蝴蝶效应
-
但是可以被追加(一般不推荐)追加设置需要手动打开
-
一般HDFS存储的都是历史数据.所以将来Map Reduce都用来进行离线数据的处理
-
块的大小一旦文件上传之后就不允许被修改 128M-512M
拆分的数据块需要等大(面试)
-
数据计算的时候简化问题的复杂度(否则进行分布式算法设计的时候会因为数据量不一很难设计)
-
数据拉取的时候时间相对一致
-
通过偏移量就知道这个块的位置
-
相同文件分成的数据块大小应该相等
端口号
HDFS:50070
YARN:8088
进程管理
HDFS:NameNode、DataNode、SecondaryNameNode(NN、DN、SNN)
NameNode
功能:
1、接受客户端的读/写服务
因为NameNode知道文件与DataNode的对应关系
2、保存文件的时候会保存文件的元数据信息
a. 文件的归属
b. 文件的权限
c. 文件的大小,时间
d. Block信息,但是block的位置信息不会持久化,需要每次开启集群的时候DN向NN汇报。(带同学们画图讲解,引出这4个点)
3、收集Block的位置信息
3.1 系统启动
a. NN关机的时候是不会存储任意的Block与DataNode的映射信息的
b. DN启动的时候会自动将自己节点上存储的Block信息汇报给NN
c. NN接收请求之后会重新生成映射关系
File ----> Block
Block---> DN
d. 如果数据块的副本数小于设置数,那么NN会将这个副本拷贝到其他节点
3.2 集群运行中
a. NN与DN保持心跳机制,三秒钟发送一次
b. 如果客户端需要读取或者上传数据的时候,NN可以知道DN的健康情况
c. 可以让客户端读取存活的DN节点
d. 如果NN与DN三秒没有心跳则认为DN出现异常,此时不会让新的数据写到这个异常的DN中,客户端访问的时候不提供异常DN节点地址
e. 如果超过十分钟没有心跳,那么NN会将当前DN节点存储的数据转移到其他的节点
4、NameNode为了效率,将所有的操作都在内存中进行
a. 执行速度快
b. NameNode不会和磁盘进行任何的数据交换
但是会存在两个问题:
1)数据的持久化
2)数据保存在内存中,断电丢失
DataNode
1、存放的是文件的数据信息,以及验证文件完整性的校验信息
2、数据会存放在硬盘上
a. 1m=1条元数据
b. 1G=1条元数据
c. NameNode非常排斥存储小文件(能存,但是不推荐!!)
一般小文件在存储之前需要进行压缩
3、汇报
1)启动时
汇报之前会验证Block文件是否被损坏
向NN汇报当前DN上block的信息
2)运行中
向NN保持心跳机制
4、当客户端读写数据的时候,首先会先去NN查询file与block与DN的映射,然后直接与DN建立连接,然后读写数据
SecondaryNameNode
1、传统的那日村持久化方案
1)日志机制
a. 做任何操作之前先记录日志
b. 在数据改变之前先记录对应的日志,当NN停止的时候
c. 当我下次启动的时候,只需要重新按照以前的日志“重做一遍”即可
缺点:
a. log日志文件的大小不可控,随着时间的发展,集群启动的时间会越来越长
b. 有可能日志中存在大量的无效日志
优点:
a. 绝对不会丢失数据
2)拍摄快照
a. 我们可以将内存中的数据写出到硬盘上(序列化)
b. 启动时还可以将硬盘上的数据写回到内存中(反序列化)
缺点:
a. 关机时间过长
b. 如果是异常关机,数据还在内存中,没法写入到硬盘
c. 如果写出的频率过高,导致内存使用效率低
优点:
启动时间较短
2、SNN的解决方案
1)解决思路
a. 让日志大小可控
b. 快照需要定时保存
c. 日志+快照
2)解决方案
a. 当我们启动一个集群的时候,会产生4个文件 ..../name/current/
b. 我们每次操作都会记录日志
主从节点的进程
master:NameNode、SecondaryNameNode、ResourceManager
node1、2:DataNode、NodeManager
MapReduce
什么是MapReduce
map--->映射
reduce--->归纳
mapreduce必须构建在hdfs之上的一种大数据离线计算框架
在线:实时数据处理
离线:数据处理时效性没有在线那么强,但是相对也需要很快得到结果
mapreduce不会马上得到结果,他会有一定的延时(磁盘IO)
如果数据量小,使用mapreduce反而不合适
杀鸡焉用宰牛刀
原始数据-->map(Key,Value)-->Reduce
分布式i计算
将大的数据切分成多个小数据,交给更多的节点参与运算
计算向数据靠拢
将计算传递给有数据的节点上进行工作
MapReduce流程
原始数据被切分
数据被切分成块存放在HDFS上,每一个块有128M大小
切片Split
目的:动态地控制计算单元的数量
切片是一个逻辑概念
在不改变现在数据存储的情况下,可以控制参与计算的节点数目
通过切片大小可以达到控制计算节点数量的目的
有多少个切片就会执行多少个Map任务
一般切片大小为Block的整数倍(2 1/2)
防止多余创建和很多的数据连接
如果Split大小 > Block大小 ,计算节点少了
如果Split大小 < Block大小 ,计算节点多了
默认情况下,Split切片的大小等于Block的大小 ,默认128M,如果读取到最后一个block块的时候,与前一个blokc块组合起来的大小小于128M*1.1的话,他们结合生一个split切片,生成一个map任务
一个切片对应一个MapTask
MapTask
map默认从所属切片读取数据,每次读取一行(默认读取器)到内存中(map种的逻辑作用在每一行上)
我们可以根据自己书写的分词逻辑(空格,逗号等分隔),计算每个单词出现的次数(wordcount)
这时会产生(Map<String,Integer>)临时数据,存放到内存中
但是内存的大小是有限的,如果每个任务随机的去占用内存,会导致内存不可控。多个任务同时执行有可能内存溢出(OOM)
如果把数据都直接放到硬盘,效率太低
所以想个方案,内存和硬盘结合,我们要做的就是在OOM和效率低之间提供一个有效方案,可以先往内存中写入一部分数据,然后写出到硬盘
环形缓冲区(KV-Buffer)
可以循环利用这块内存区域,减少数据溢写时map的停止时间
每一个Map可以独享的一个内存区域
在内存中构建一个环形数据缓冲区(kvBuffer),默认大小为100M
设置缓冲区的阈值为80%(设置阈值的目的是为了同时写入和写出),当缓冲区的数据达到80M开始向外溢写到硬盘
溢写的时候还有20M的空间可以被使用效率并不会被减缓
而且将数据循环写到硬盘,不用担心OOM问题
分区
根据Key直接计算出对应的Reduce
分区的数量和Reduce的数量是相等的
hash(key) % partation(reduce的数量) = num
默认分区的算法是Hash然后取余
Object的hashCode()—equals()
如果两个对象equals,那么两个对象的hashcode一定相等
如果两个对象的hashcode相等,但是对象不一定equlas
排序
对要溢写的数据进行排序(QuickSort)
按照先Partation后Key的顺序排序–>相同分区在一起,相同Key的在一起
我们将来溢写出的小文件也都是有序的
溢写
将内存中的数据循环写到硬盘,不用担心OOM问题
每次会产生一个80M的文件
如果本次Map产生的数据较多,可能会溢写多个文件
合并
因为溢写会产生很多有序(分区 key)的小文件,而且小文件的数目不确定
后面向reduce传递数据带来很大的问题
所以将小文件合并成一个大文件,将来拉取的数据直接从大文件拉取即可
合并小文件的时候同样进行排序(归并 排序),最终产生一个有序的大文件
组合器
-
集群的带宽限制了mapreduce作业的数量,因此应该尽量避免map和reduce任务之间的数据传输,hadoop允许用户对map的输出数据进行处理,用户可自定义combiner函数(如同map函数和reduce函数一般),其逻辑一般和reduce函数一样,combiner的输入是map的输出,combiner的输出作为reduce的输入,很多情况下可以i直接将reduce函数作为conbiner函数来试用(job.setCombinerClass(FlowCountReducer.class))。
-
combiner属于优化方案,所以无法确定combiner函数会调用多少次,可以在环形缓存区溢出文件时调用combiner函数,也可以在溢出的小文件合并成大文件时调用combiner,但是要保证不管调用多少次,combiner函数都不影响最终的结果,所以不是所有处理逻辑都可以i使用combiner组件,有些逻辑如果试用了conbiner函数会改变最后reduce的输出结果(如求几个数的平均值,就不能先用conbiner求一次各个map输出结果的平均值,再求这些平均值的平均值,那样会导致结果的错误)。
-
combiner的意义就是对每一个maptask的输出进行局部汇总,以减小网络传输量:
原先传给reduce的数据时a1 a1 a1 a1 a1
第一次combiner组合后变成a(1,1,1,1,1)
第二次combiner后传给reduce的数据变为a(5,5,6,7,23,...)
拉取
我们需要将Map的临时结果拉取到Reduce节点
相同的Key必须拉取到同一个Reduce节点
但是一个Reduce节点可以有多个Key
未排序前拉取数据的时候必须对Map产生的最终的合并文件做全序遍历
而且每一个reduce都要做一个全序遍历
如果map产生的大文件是有序的,每一个reduce只需要从文件中读取自己所需的即可
合并
因为reduce拉取的时候,会从多个map拉取数据
那么每个map都会产生一个小文件,这些小文件(文件与文件之间无序,文件内部有序)
为了方便计算(没必要读取N个小文件),需要合并文件
归并算法合并成2个(qishishilia)
相同的key都在一起
归并
将文件中的数据读取到内存中
一次性将相同的key全部读取到内存中
直接将相同的key得到结果–>最终结果
写出
每个reduce将自己计算的最终结果都会存放到HDFS上
总结
1.每个reduce将自己计算的最终结果都会存放到HDFS上
2、按照我们定义的split逻辑对block进行切分(不是真正意义上的切分,而是逻辑上的切分,读取到定义的大小,就会产生—map任务,hadoop默认split大小是128M,但是如果最后一个block块与前一个block块数据真实大小总和小于128*1.1的话,会生一个map任务
3、map任务的逻辑是作用在每一行会按照我们定义的分隔符进行切分,得到一个Key-value的格式数据
4、map任务产生的数据会进入到一个叫作环形缓冲区的地方,这个环形缓冲区(内存,每一个map任务都会有一个这样的环形缓冲区)的大小默认是100M,当数据写入到默认80%的时候,开始往磁盘溢写,产生一个文件。
5、在溢写之前,也就是在环形缓冲区的时候,会先将数据分区(哈希值%reduce),排序(快速排序)。reduce去拉取的时候,每一个map产生的小文件会先进行一次合并(归并排序),同-个分区的数据被拉取到同一个reduce中。这时候,一个reduce中就计算出每一个map任务的结果reduce再将每一个map任务的结果做合并(归并排序),聚合得到结果最终的结果写出到HDFS中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具