java基础巩固-宇宙第一AiYWM:为了维持生计,大数据Hadoop之yarn【MapReduce的基本概念、Yarn的架构(中的角色、各角色的工作流程)MapTask与ReduceTask】~整起


目录


一、HADOOP之MapReduce

1.MapReduce基本概念

  • 【本质就是在分组统计计算】数据以一条记录为单位通过map映射为K-V键值对,相同的key为一组,这一组数据调用一次reduce方法,在方法内迭代计算着一组数据
    在这里插入图片描述
    在这里插入图片描述
    • 可以在代码中关闭Reduce(),可以先输出Map()计算的结果,然后再输出Reduce()的结果集
  • Map:以一条记录为单位做映射,各个记录之间计算过程中没啥依赖关系
    • split切片中会有记录,切片split中格式化出一条一条记录,以记录为单位调用一次map,map方法是以记录为单位进行调用的,map对这些一条一条的记录进行映射、变换、过滤
      • 输入数据集集进来,经过map做映射、变换、过滤等操作(也就是抽取数据相同特征后),形成一种数据格式【也就是键值对】,为后面的reduce做准备,map计算输出中间数据集时在中间数据集中已经按照键值对的键类型将数据分好组了
        在这里插入图片描述
      • split有四个属性
        • file
        • offset:来自于一个文件,文件切开后,每个切片都有自己的面向文件的偏移量
        • length
        • hosts:这个属性支撑计算向数据【在哪里的数据】移动
    • map和reduce都有并行度的概念
      • split数量决定了map的并行度,split可以说是由block决定,一个block默认对应一个split【HDFS中会对文件进行切割为多个块,比如3个块,1split约等于1块【不直接用block而引入split只是为了引入中间人进行解耦,引入split就是为了控制粒度】】
        • 计算分为CPU密集型【文件中有多个行,一行可能由于有大循环可能使得CPU忙半天,性能损耗到CPU上】和IO密集型【性能都损耗在IO上了】,默认情况下一个block大一点适合CPU密集型而对IO密集型不友好,没办法给出一个完美大小,所以给出一个灵活的block大小,以满足未来不同的类型的计算【感觉就跟MAC地址与IP地址一样,MAC变不了我为了控制粒度,想要变化,搞出一个IP地址,不就可以拥抱变化了嘛】
        • split其实跟block的影子一样【split不管为了迎合不同的类型的计算,CPU密集型还是IO密集型,都可以得到block的offset以及副本数等属性】,可以得到block的offset偏移量以及副本数量等,所以此时基于split的map计算就可以决定计算发生于哪台机器上(因为副本可能处于不同机器上),不就正好符合计算向数据移动嘛
    • 计算向数据移动,也就是说等map启动后,split从block那里就可以告诉map,你只能从文件的这里处理到那里,你就可以进行移动了,也就是计算向数据移动【由于block或者split的偏移量以及副本等信息】
  • Reduce:以一组键值对为单位进行计算,可以启动多个reduce同时对多组键值对进行并行计算
    • map那里分好组之后,相同的key为一组调用reduce方法,这组数据不能被破坏,自己拉去对应分区中对应分组进行计算【相同一组数据不能被切割开】,然后由Reduce将键值对分解、缩小、归纳,输出【键值对中的键的类型有很多,就可以启动多个reduce,并行计算【因为多个计算间没啥依赖关系】,直接输出结果】
    • reduce的并行度由开发人员决定【一个reduce处理几组键值对,提高并行度】
    • 相同的key的hash值一定相同,相同的哈希值模reduce集群中reduce节点数量时计算出来的分区号一定相同
  • 换个角度再看MapReduce的原理
    在这里插入图片描述
    • 1.split切片会格式化出记录,然后以记录为单位调用map方法进行处理
      • MapReduce框架默认的输入
      • MapReduce框架默认的输入格式化类
        在这里插入图片描述
    • 2.map的输出映射为KV,K、V都会序列化变成字节数组,然后序列化完成之后KV会参与分区计算,拿着key算出P这个分区号,最后就可以得到K、V、P三元组
      • k、v也会做一次序列化,K、V都会序列化变成字节数组,这些键值对字节数组会放到缓冲区这个大的字节数组中,如果你不表明哪个键值对字节数组存的时候是从哪开始从哪结束,你到时候取得时候就很麻烦,所以搞了一个16Byte的索引index,
        • index索引中包含分区信息【分区信息就是个整数,占4个字节】、keyStart【占4个字节】、ValueStart【占4个字节,并且 valueStart - 1就是keyEnd,就可以通过value把key取出来】、ValueLength【占4个字节,valueStart结合valueLength就可以取出value】。那咱们此时想取哪个键哪个值不都很方便嘛
        • 为了防止键值对正好索引浪费以及索引正好键值对浪费这种问题,map每输出一个键值对,将键值对序列化放到缓冲区里之后,在缓冲区中放个16Byte索引,再放一个键值对再追加一个16Byte索引
        • map输出使用的缓冲区是一个环形缓冲区:以中间赤道为起点向两端堆数据
          在这里插入图片描述
          • buffer缓冲区本质上还是一个线性的字节数组
          • map输出会向缓冲区不断的填键值对数据和索引数据,当缓冲区放的数据和索引超过80%那个源码中的设定或者规定的阈值80%之后,此时会启动线程,线程会进行排序【比较key或者整个数据,看有必要交换位置吗?】和溢写来整理空间【就跟垃圾回收机制差不多】,然后多余的空间咱们map输出的线程继续向剩余的空间写数据,要不就是继续放数据【键值对】要不就是继续放索引,但是要是同向按照原来的键值对数据和索引存放方向继续放可能会撞车会冲突,比如最后剩2K了键值对数据和索引都想放那谁放呀对不对,所以,开辟一条赤道,其实最开始键值对数据和索引数据的分界线不就是一条赤道嘛
            在这里插入图片描述
      • 调优:combiner,用来压缩数据,其实就是一个map里面的reduce,用来对map进行按组统计计算,对内存数据进行压缩,
        • 算是一个map里面的reduce,用来对map里面的数据进行统计计算,用来比如块里面有10万行k=1键值对,把这10万行压缩为一行,存为十万行k=1,就可以提升速度
          在这里插入图片描述
        • 发生的时间点有两个【或者说combine有两种被执行的可能性】:
          • combiner发生的时间点如果是内存溢写数据之前【内存还没有向磁盘写数据,没发生IO操作,就可以使得溢写的IO变少】之前排序之后
          • combine发生的时间点如果是最终的map输出结果之后,过程中buffer溢写出多个内部有序的小文件,那么如果溢写的小文件数量大于等于3,即minSpillForCombine=3【这个可以调】,map最终会把溢写出来的小文件合并为一个大文件【合并就跟咱们平时给谁发东西,你把所有东西打个压缩包发过去肯定稍微比原来快点】,目的就是为了避免小文件的碎片化对未来reduce拉取数据造成的随机读写带来的性能损耗【为了读小文件,磁头得飘来飘去,这个机械运动很慢,毫秒级的,】,尽量成就线性顺序读写,也会触发combine
            • kafka和ES搜索引擎这些为什么读写磁盘数据快,就是因为避免了这一个磁道上一个小文件,那一个磁道上一个小文件,磁头为了读数据得来回跑,本来就慢还来回跑,这肯定更慢了,所以kafka和ES这些是在磁盘上线性按照磁道顺序存的数据,进而使得读写数据速度很快
            • 咱们mapReduce是这样的,比如map输出好几个小文件A、B、C…,A、B、C中都有0号分区的、1号分区的…,那么带来的恶果就是0号Reduce去进行三次寻址,从A、B、C三个中拿走0号分区的数据,1号Reduce去进行三次寻址从A、B、C三个中拿走1号分区的数据…这种交叉寻址多慢呀对不对
            • 但是如果你在0号1号…Reduce在拉取对应的x号数据之前把0号数据放在一起,把一号数据放在一起…构成线性存储,比交叉寻址快多了吧
        • combine必须是幂等的,注意求平均数容易不幂等,所以每次达到80%先求和,最后结合个数再求最终的一次平均数,就可以避免求平均的不幂等
    • 3.map task的输出是一个文件,存在本地的文件系统中
      • 但是此时涉及到了一个问题,啤酒理论,我要拿一瓶啤酒上楼喝完下楼再拿再上楼再喝还是,一次性用箱子把啤酒全拿上楼然后回家慢慢喝,操作系统也不傻,人家你一次性格式化出一条记录然后经过map计算出K、V、P,然后本地化刷磁盘存储,拿一条记录烦操作系统一次,拿一条记录烦我操作系统一次,所以操作系统受不了了,搞了一个buffer in memory这个大箱子,进行批量处理,你map先输出,我用缓存先接收存着,buffer默认是100M,可以调大小,buffer攒满了后再进行调系统IO,然后写磁盘
      • 因为涉及到调用系统调用,甭管是IO或者其他啥系统调用,都要进行用户态到内核态的切换,操作系统很累的
    • 4.有了buffer之后,很多对<K、V、P>在buffer里面是乱的,每个文件拉自己对应的<K、V、P>,这样一来IO成本很高【每个文件为了找到属于自己的东西不得不进行全量查找,他来得全部翻一遍,你来找又得全部翻一遍】,为了解决这个问题,在产生IO系统调用之前做一次排序【用P来进行分区有序】,并紧接着 在按照分区P排序之后进行按照键值对的键K排序【做一个二次排序,分区有序,并且分区内key有序,保证未来相同的一组key会相邻的排列在一起】
      • 排序会让reduce拉取数据以及计算的复杂度降低
        • 相同的key的hash值一定相同,相同的哈希值模reduce集群中reduce节点数量时计算出来的分区号一定相同
        • reduce的归并排序可以和reduce方法的计算同时发生,目的是尽量减少IO系统调用,原理就是有迭代器模式的支持【迭代器模式是批量计算中非常优秀的实现形式之一】
      • 排序是二次排序【索引里有分区这个整数P,排序先比较索引的分区这个整数P决定顺序,然后在比较相同分区整数P中的key的顺序】,分区有序【最后reduce拉取是按照分区的】、分区内key有序【因为reduce计算是按照分组计算,分组指的是相同的key排在了一起】
        • 但是你超过80%进行排序后,有可能A交换过来放不进B原来的位置,为了解决这个问题,虽然比较的是key进行的排序,但是最后数据不动只交换A、B他俩的索引进行排序,排完索引,等到溢写时只要按照排序的索引,卸载下来的数据就是有序的,换句话说就可以保证文件里面的键值对数据是有序的
      • 分区
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述

2.MapReduce的数量约定

  • split可以多于block也可以少于block也可以约等于block
    • split是逻辑的,block是物理的,block身上有自己offset偏移量、locations【block的副本都在哪些机器上】等信息,split和block是有映射关系的
  • split和map是1:1的关系
    在这里插入图片描述
    在这里插入图片描述
  • 用于计算的Name Manager【Node Manager未来要产生容器Container,容器中跑咱们的计算】和存储数据的DataNode应该是1:1
    • 计算和数据,在集群当中应该是1:1的关系
  • map和reduce可以是N:1、N:N、1:1、1:N
  • group和partition可以是N:1、N:N、N:1、1:1

3.MapReduce的迭代器模式

  • 用迭代器模式来避免大数据计算内存溢出的风险,因为有可能有时候一组数据会很大,所以没有个保护措施早内存溢出了
    • 迭代器是对象,但是没有数据【迭代器是不存储数据的,数据可以存在任何地方】,这个对象单独维护一个数据标识,就相当于利用迭代器可以并发批量处理数据,大数据这里很多经常用迭代器,来避免内存溢出,即便一个数据1000T,只要你磁盘中能放,内存中进行处理的方法中传的就是迭代器对象,迭代器有自己的指针,最差的情况就是readLine一行一行把数据向内存中扔过去处理
    • mapReduce中真假迭代器两层嵌套:
      在这里插入图片描述
      • Spark中有多层迭代器嵌套,多个迭代器组成pipeline,数据从磁盘拿出来,经过多个迭代器组成的pipeline管道,pipeline管道中的迭代器都会对数据进行加工处理

4.mapreduce的架构、架构中的角色(都有谁)、各个角色之间如何进行协作,如何进行更好的读写?

  • mapreduce的计算模型:分为两阶段【map阶段和reduce阶段】,属于批量计算【先进行map计算,等map计算完之后产生结果,reduce才能在map计算结果的基础上再进行计算,这就是批量处理的特点,也可以说批量计算,或者说map和reduce是一种阻塞关系,因为只有map算完了才能把计算结果交给reduce继续进行处理】
    • map阶段:单条记录加工和处理
    • reduce阶段:多条记录加工处理
  • MapReduce计算框架:hdfs会暴露数据的位置,然后咱们计算向数据移动如何实现,主要就是要做哪些事、用到哪些资源?【不就是资源管理和任务调度嘛】
    • 用到 JobTracker+TaskTracker 帮咱们管理资源和任务调度
      • MapReduce1.0的架构
        在这里插入图片描述
      • MapReduce2.0架构如下:MapReduce2.0架构是在YARN架构的基础上运行的
        在这里插入图片描述
    • JobTracker:主从架构中的主
      • 来了个 JobTracker搞定了【哪些节点可以过去呢?(需要对资源有整体的把控)】+ 【确定了节点后对方怎么知道呢(任务调度),并且比如有一个节点失败了,应该重新在哪个节点上retry】这两件事
        • 资源管理:哪些节点可以过去呢?(需要对资源有整体的把控)
        • 任务调度:确定了节点后对方怎么知道呢(任务调度),并且比如有一个节点失败了,应该重新在哪个节点上retry
      • JobTracker工作流程:JobTracker收到启动程序之后
        • 1.首先,从hdfs中取回或者说下载split切片清单这个文件
          在这里插入图片描述
        • 2.其次,根据自己的TaskTracker汇报的资源,最终确定每一个split对应的map应该去到哪一个节点【确定清单】。
        • 3.TaskTracker在心跳的时候会拉取回JobTracker分配给自己的任务信息,隔一个心跳拉一次
      • JobTracker的三个问题:
        • 1.单点故障
        • 2.压力过大
        • 3.集成了【资源管理和任务调度】,两者耦合,导致未来的新的计算框架不能复用资源管理
          • 重复造轮子。各自实现资源管理,但是他们部署在同一批硬件上,因为隔离所以不能感知对方的使用,可能会导致资源争抢
      • 为了解决JobTracker的三个问题,Hadoop2.0出现了yarn
    • TaskTracker负责:主从架构中的从们
      • 任务管理
      • 通过心跳机制实现资源汇报
      • TaskTracker的工作流程
        • 1.在心跳取回任务之后
        • 2.从hdfs中下载jar、XML配置文件等到本机;
        • 3.最终启动任务描述中的MapTask/ReduceTask。最终,代码在某一个节点上被启动,通过client上下,TaskTracker下载

5.MapReduce的客户端client

  • MapReduce的客户端client:做规划,推动计算向数据移动的实现【你数据在哪里呀,知道你数据在哪里后我就知道去哪里计算了哟,不就相当于计算向数据移动嘛】,写的程序搞成一个jar包,启动这个SpringBoot写成的jar包就相当于启动了一个client
    • MapReduce的客户端client干的第一件事:client中会根据每次的计算数据【数据在HDFS中放着】,咨询NameNode,取回NameNode的元数据【元数据中block等信息】,然后MapReduce的客户端client根据这些元数据计算出自己的split切片【虽然split默认约等于block,但是可以根据CPU密集型还是IO密集型等不同类型调整split大小。】,得到一个切片的清单【这次计算的数据总共切了多少个切片split以及每个切片要移动到哪里去】,然后map的数量就可以知道了【因为split和map是1:1的关系】,这个清单中包含split的偏移量以及split对应的map任务应该移动到哪些节点上【因为split是逻辑的,block是物理的,block身上有自己offset偏移量、locations【block的副本都在哪些机器上】等信息,split和block是有映射关系的。】,
      • 相当于client给出了一个移动的范围
      • 调整split的大小:
        在这里插入图片描述
        在这里插入图片描述
    • MapReduce的客户端client干的第二件事:MapReduce的客户端client还会生成计算程序未来运行时的相关配置的文件,比如相关的程序对应的XML配置文件
    • MapReduce的客户端client干的第三件事:保证生成的移动应该相对可靠,也就是 client会将资源们【jar包以及jar包中包含的split清单、程序对应的XML配置文件】上传到比较可靠的hdfs的目录中【因为hdfs中有journalNode、Zookeeper集群、zookeeperFailoverController等角色帮咱们维持高可用性或者分布式架构中的可靠性】
      在这里插入图片描述
      • 上传的数据副本数量为10,是可以配置的
    • MapReduce的客户端client干的第四件事:client会调用JobTracker,通知要启动一个计算程序啦,并且告知文件都放在了HDFS的哪些地方

6.yarn的架构(中的角色、各角色的工作流程)与实操

6.1 架构:来解决JobTracker的三个问题,将资源管理独立出来,在MapReduce之上运行,进行资源管理【不负责任务调度】,yarn中一切皆资源

在这里插入图片描述

  • yarn的模型、架构:
    • yarn架构中的角色:
      在这里插入图片描述
      • Resource Manager,yarn中的主
        • yarn也是一个主从架构,yarn中的Resource Manager也有单点故障和压力过大,这俩主从架构通用的问题。所以用主备方案来解决这个问题,用zookeeper解决呗
      • Node Manager,yarn中的从
        • 主从中肯定有心跳机制存在,从向主汇报心跳,提交自己的资源情况
        • 但是如何Node Manager一个挂了不就有影响了嘛,所以此时在Task级别上有一种retry,一个Node Manager挂了,里面的Container用不了了,那么App Master重新拿着任务去找Resource Manager让他帮咱们在存活的节点中重新找重新分配
      • Container:容器,用来标识在yarn中代表资源的使用情况,(不是docker那个容器),有两个特点
        • 容器Container第一个特点就是个对象,可以是虚的,跟个对象一样,有属性【属性定义了内存、IO等资源的量,并且属性中定义了容器Container归属于哪个Name Manager】
          • 在逻辑上可以视作是一个对象,这个对象中有一些属性定义了未来在这个Node Manager中未来要消耗多少内存、多大CPU、多大带宽
        • 容器Container第二个特点是可以容器Container视为JVM进程,操作系统进程,对进程资源的监控有两种实现方式:【逻辑上把你Container看作一个对象,但是 物理上你Container是一个进程,你有对应的堆等信息的大小】
          • Node Manager未来会有线程去监控Container资源使用情况,有没有超标,超额后由Node Manager直接kill掉JVM进程,这种方式太暴力了
          • 另一种方式就是coregroup内核级技术,在启动jvm进程时由内核约束死未来操作系统级的JVM进程能使用多少寻址空间,那么线程就不用监控了从而暴力杀死
      • App Master也是一个Container:
        • App Master相当于一个阉割版的JobTracker,那App Master如何实现人家JobTracker原来的功能呢:
          • 1.首先,从hdfs中取回或者说下载split切片清单这个文件
            在这里插入图片描述
          • 2.App Master拿着split切片清单去找Resource Manager,说,大哥,我这有个清单,你看啊,这个清单中有多少个Map要跑,这些个Map分别要移动向哪些节点,你Resource Manager根据你掌握的Node Manager集群的资源情况,帮我分配一下Map的去向
      • MapReduce的客户端client,不管是红的还是蓝的或者是其他的没画出来的没表示出来的MapReduce客户端,我每个MapReduce客户端client都通过Resource Manager有自己的App Master,再到Container再执行下面完整的工作流程
        • 此时就解决了下面JobTracker的三个问题:
          • 单点故障,你这个App Master挂掉了,我其他的App Master该咋工作就咋工作,你看看你原来的JobTracker单点故障差成啥了
            • yarn中每一个计算程序有一个自己的App Master调度程序,不是全局的,这个挂了其他的不受影响
            • yarn支持App Master还支持失败重试
          • 压力过大,原来一个JobTracker进行资源管理与任务调度,现在每一个计算程序有自己的App Mater和Container那一条工作流程,压力大大减少了
            • yarn中每个计算程序自有App Master,每个App Master只负责自己计算程序的任务调度,轻量了
            • App Masters是在不同的节点中启动
          • 解耦和了,每一个计算框架要有自己的App Master和Container配合的那一条流程,不就不需要重复造轮子了, 每一个计算框架都在一个资源层上跑起来了,由Resource Manager统一管理
            • yarn只是资源管理,不负责具体的任务调度【任务调度有自己的App Master】,只要计算框架继承yarn的App Master,那么这些个计算框架就可以使用一个统一视图的资源层
        • 随便拿一个客户端来看,首先MapReduce的客户端client前三件事照干不误,第四件事因为JobTracker有三个问题,所以就不找JobTracker玩啦转而去找Resource Manager玩这个Resource Manager会帮client在Node Manager集群中挑选一个不是很忙的并且有资源的节点,然后当有计算发生时帮client创建出一个App Master(这个App Master相当于一个阉割版的JobTracker,只留下了JobTracker的任务调度功能,因为这样就能避免JobTracker的三个问题,曾经的长连接服务变成了现在的按需服务【2.x之后把之前的JobTracker和TaskTracker这些曾经是MapReduce的常服务,取消了这些常服务,相对的MapReduce的客户端调度、任务等变成临时服务了】,还不够解耦和嘛?还不够灵活嘛?)
          • Resource Manager通过心跳机制收集到了所有Node Manager的资源使用情况
            • Resource Manager像一个大管家一样,咳咳,大家静一静,根据我手中掌握的集群中的资源使用情况,做决策如下,你这个Node Manager1未来可以跑一个任务,你这个Node Manager2可以未来跑两个任务,你这个Node Manager3未来可以跑三个任务…,这样一来就决策了计算向哪里移动
            • 资源的用Container(是一个进程)来量化,Resource Manager按照Container来衡量你每个Node Manager有多少槽位可以共计算去移动去使用。然后App Master做一个决策,将MapTask/ReduceTask等任务发到指定的含有Container的Node Manager上【Node Manager通过发送消息来启动Container,Container启动之后会反向注册到App Master,反向注册成功之后AppMaster就知道了你每个Node Manager有多少槽位或者说有多少Container可以共计算去移动去使用】,然后由这个Name Manager从hdfs中下载jar、XML配置文件等到本机,然后在这个已经启动的Container进程中,由这个已经启动的Container进程【逻辑上把你Container看作一个对象,但是物理上你Container是一个进程,你有对应的堆等信息的大小】通过反射取出MapTask这个类,并实例化MapTask为对象,然后调用MapTask对象中的方法来运行程序逻辑
    • 流程:MapReduce on yarn,MapReduce资源层之上运行
      • 1.(只有这个第一步需要人参与,给出相关配置信息,其余几步都是自动的)MapReduce的客户端启动之后,做自己该做的事情【切片清单、配置、jar包上传到HDFS】、之后访问Resource Manager申请自己计算程序对应的调度程序App Master
        在这里插入图片描述
      • 2.由Resource Manager选择一个不忙的节点通知Name Manager启动一个Container,在里面反射一个MapResource的App Master这个实现类,启动计算程序对应的调度程序
      • 3.然后紧接着启动MapReduce的App Master,从hdfs下载切片清单,向Resource Manager申请资源
      • 4.Resource Manager根据自己掌握的资源情况按照切片清单进行匹配,然后得到一个确定的清单,然后通知Node Manager启动Container
      • 5.Container启动后会反向注册到已经启动的App Master进程,App Master此时才会知道集群中有多少Container可以被自己调度
      • 6.Map Reduce的App Master作为一个阉割版的不带资源管理的JobTracker最终将任务Task发送给Container,以消息的形式发送
      • 7.container会反射相应的Task类为对象,调用方法执行,其结果就是我们的业务逻辑代码的执行
      • 8.集群框架都有任务Task任务重试的机制,咱们可以定义重试次数

6.2 yarn的实操:

  • 最终咱们要开发Map Reduce的计算程序,2.x后出现了yarn资源管理,导致没有了常服务,搞一个container里面运行咱们的AppMaster,从而执行map/reduce Task,然后形成MapReduce on yarn
  • 模拟的集群中的布局:
    在这里插入图片描述
    • yarn和hdfs是两个独立的关系,俩概念,两个的实操是不冲突的【可以不停掉hdfs去搞yarn】
  • YARN on a Single Node,单机安装
    在这里插入图片描述
    • mapred-site.xml中value配置的yarn,指的就是mapReduce on yarn,你要把value换成local,那就mapReduce跟yarn没啥关系
  • High Availability of YARN’s ResourceManager
    在这里插入图片描述
    • zookeeper是个可以复用的技术,很多人都可以用
    • 启动流程:
      在这里插入图片描述
      • 可以用jps查询某个机器上启动的都有谁:
      • 在每个机子上:http://node0x:8088,看下web监控页面
        在这里插入图片描述
        在这里插入图片描述
    • 官方给了一个wordcount的计算程序,一个demo,可以试着跑一下看看效果,提前得在node01上准备一个装满word的文本文件然后上传到hdfs中去,上传目录可以不为空但是输出路径必须为空,因为害怕输出覆盖其他项目模块的数据等结果,同名不就有可能覆盖了嘛
      在这里插入图片描述
      在这里插入图片描述
      • 比如咱们自己写的data.txt通过hdfs dfs -D dfs.blocksize=1048576 -put data.txt /data/wordcount/input这个hdfs页面中【可以到hdfs页面那里点点看】,上传后文件会被切割为2个block,但是计算完人家数据是对的,这是人家源码中定义好的就像我买的一个完整东西,你要给我运过来,不管你中途咋拆开,到我手里必须是一个完整的东西,是一个道理,mapReduce很好的做到了这一点
    • IDEA中开发HDFS、MapReduce
      • 导入依赖:版本和hadoop集群版本一致
        在这里插入图片描述
      • 具体编程:可以点进Job、Mapper等类的源代码中,里给了example,咱们按照example把里面的代码改为自己想要的,就知道咋写了
        • Configuration这个类可以获取咱们resource底下的配置文件中的配置信息,就是说可以得到一个Configuration对象,通过这个对象可以得到resource底下的配置文件中的配置信息
          • 配置文件
            在这里插入图片描述
        • 一般比如咱们那个例子,就是三个类
          • MyMapper:对应的是Map过程
            在这里插入图片描述
            • 在集群中运行【集群中运行流程就是client找Resource Manager,Resource Manager去进一步找App Master
          • MyRedue:对应的是Reduce过程
            • 在集群中运行
          • MyWordCount:对应实体的序列化等过程
        • 提交方式:
          • 方式一:开发项目通过MAVEN打成jar包,将jar包上传到集群中的某一个节点上,然后命令行启动:hadoop jar xxxx.jar xxxx in路径 out路径
            在这里插入图片描述
            • 属于集群方式
              在这里插入图片描述
          • 方式二:嵌入到Linux或Window的IDEA或者其他东西中,把客户端跑起来
            在这里插入图片描述
            • 算是非hadoop的集群方式
            • 推送jar包到hdfs
              在这里插入图片描述
            • 只要有代码跑在window上,window上的话需要改变化境适应linux
              在这里插入图片描述
          • 方式三:单机本地,先测试一下程序有啥问题没
            在这里插入图片描述
            在这里插入图片描述
            • 只要有代码跑在window上,window上的话需要改变化境适应linux
              在这里插入图片描述
    • 两个案例:
      • TopN
        • 常见两种思路:
          • 指定一个时间范围(窗口)针对当前窗口中的数据进行TopN【借助窗口函数,全量函数】,不对原始数据进行处理
          • 指定一个时间范围(窗口),首先需要窗口1对原始数据进行处理【用增量或者全量函数都可以】,然后再把处理后的数据做TopN
            在这里插入图片描述
      • 分组取TopN:从所给数据中找出每个月气温最高的两天,第一高和第二高
        在这里插入图片描述
      • 好友推荐:
        在这里插入图片描述

7.MapTask与ReduceTask

在这里插入图片描述

  • MapTask:input->map->outpiut,咱们写代码时client的代码,MapTask基本上要注意的点就是这么几个
    • input:
      在这里插入图片描述
      • 输入环节:input等于split+format,输入input来自于我们的 输入格式化类 给我们实际返回的记录读取器对象:输入TextInputFormat->返回LineRecordReader。
        在这里插入图片描述
        ...
        //输入输出代码
        TextInputFormat.addInputPath(job, new Path(other[0]));
        Path outPath = new Path(other[1]);
        if(outPath.getFileSystem(conf).exist(outPath)){
            outPath.getFileSystem(conf).delete(outPath, true);
        }
        TextOutputFormat.setOutputPath(jon, outPath);
        
      • 以温度最高的两天距离的TopN的实现为例:
        在这里插入图片描述
        • 不管是大数据这块的还是JavaWeb开发的,框架方面,一般都是,用户配置了就用用户的【SpringBoot那里不就是可以用ConditionalOn等让默认的失效嘛】,用户没配置就用系统默认的。一般比如IDEA这种也可以在配置那里-D xxxxx去进行配置的更改,不就相当于咱们DIY,没用默认的用自己的嘛【Configuration conf = new Configuration(true);String[] other = new GenericOptionsParser(conf, args).getRemainingArgs():启动MapReduce客户端时,把参数都带出来,然后把参数里面的-D修改的配置拿出来赋值给conf,然后除了-D配置修改的参数通过字符串的一些方法切割出来成为字符串数组,就是这个other,然后就可以用了】
          在这里插入图片描述
          • 参数灵活化,后期可以通过这个Configuration配置参数,填充参数:
            在这里插入图片描述
            在这里插入图片描述
      • 你要是想要DIY,比如之前是一行一行读,然后里面有个变量key表示每一行相对于文件的偏移量,然后读的时候以换行符为结束,扔掉换行符后面的现在咱们想要两行两行读,或者三行三行读,就可以继承TextInputFromat,然后重写nextKeyValue、getCurrentKey、getCurrentValue,通过这几个方法拿到数据
      • split:file,offset,length
      • init:
        在这里插入图片描述
        • in = fs.open(file).seek(offset),除了第一个切片对应的map,之后的map都在init环节,从切片包含的数据中,让出第一行,并把切片的起始更新为切片的第二行。换句话说,前一个map会多读取一行,来弥补hdfs把数据切割的不完整的问题【不然你觉得把数据切割的不完整了,后面计算咋对着呢,不就是靠这里的弥补措施嘛】
          • 其中有个seek()方法,有了这个seek()方法之后,每个map之后就可以从各自的切片初始位置开始读,就不需要所有的map都从文件流的第一行开始读,这不就产生重复读了嘛,省事
            在这里插入图片描述
          • 其实,读的时候先面向文件拿到IO,或者说都是字节流,然后map拿的时候会先伸手一拿,利用seek转到对应切片或者块的偏移量位置【因为集群分布式中不同的块有可能分布在不同的主机上,而且文件都是IO流,不用seek的话很容易都从一个开始位置读,不就把开始那些重复读了嘛,所以一般seek一下到块的偏移量位置】,然后以换行符为结束标识,把换行符后面的丢弃,然后前面的记录参与运算就行
      • nextKeyValue():
        • 1.读取数据中的一条记录对key,value赋值
        • 2.返回布尔值
      • getCurrentKey()、getCurrentValue():直接取值,将值给map传参
    • map:
      ...
      job.setMapperClass(TMapper.class); //自己定义Mapper类,TMapper主要的东西就是这个map类,然后记得一个重要点就是把mkey、mval放在map方法外面,减少gc
      job.setMapOutputKeyClass(Tkey.class); //对Map的输出进行反序列化反序列化,其实就是实现WritableComaprable接口重写接口中三个方法即可,
      job.setMapOutputValueClass(IntWritable.class);
      
      //具体的TKey根据自己的项目需求进行编写,写一个需要的类不会吗?
      
      在这里插入图片描述
      • 程序中的map方法【map方法肯定是在Mapper类中呀】需要咱们自定义或者说自己设置Key实体类、write方法【write中会对key、value进行序列化,所以一般把key、value定义在方法外面,】
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
        • 在map方法中对mkey、mva进行处理之后用context.write(mkey、mval)把key、value写出去;
          在这里插入图片描述
    • output输出环节
      • NewOutputCollector中有分区器partitioner、缓冲区collector,缓冲区中有个关键的东西就是MapOutputBuffer。
      • MapOutputBuffer:
        • init():
          在这里插入图片描述
          在这里插入图片描述
        • 比较器:比较有两种类型:字典序和数值序,默认是快排【溢写是80%】,比如咱们在大数据这里比较key的话,那咱们就可以自己定义一个IKey.java实体类,让这个实体类实现WritableComparable接口,重写这个接口中的compareTo方法、序列化反序列化的方法write、readFields等三方法即可
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          • 序列化、反序列化俩方法
            在这里插入图片描述
        • combiner:算是一个map里面的reduce,用来对map里面的数据进行统计计算,用来比如块里面有10万行k=1键值对,把这10万行压缩为一行,存为十万行k=1,就可以提升速度
          在这里插入图片描述
        • 分区器:用来规划具体怎么进行分区【保证相同的key获得相同的分区号这个整数就可以相同的key就可以放到一起,不就实现咱们的目的了嘛】,因为系统默认的分区方式不一定是咱们想要的
          ...
          //按照年月进行分区,一个分区里面有很多组,
          job.setPartitionerClass(TPartitioner.class);
          
          • 以温度最高的两天距离的TopN的实现
            在这里插入图片描述
        • 排序比较器:
          ...
          //按照年-月-温度排序,且温度倒序
          job.setSortComparatorClass(TSortComparator.class);
          
          • 以温度最高的两天距离的TopN的实现
            在这里插入图片描述
            在这里插入图片描述
  • ReduceTask:input->reduce->output,咱们写代码时client的代码,ReduceTask基本上要注意的点就是这么几个
    在这里插入图片描述
    • 设置分组比较器
      ...
      job.setGroupingComparatorClass(TGroupingComparator.class);
      
    • reduce方法
      ...
      job.waitForCompletion(verbose:true);
      
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      • reduce主要有三个阶段:
        • 1.shuffle:拉取数据,相同的key被拉取到一个分区
        • 2.sort:整个MapReduce框架中只有map端是无序到有序的过程,用的是快速排序,reduce这里的排序其实就是一个对着map排好序的一堆小文件做归并排序【reduce没改变数据顺序,只是归并合并了map的两次排序结果】
        • 3.reduce:reduce方法被调用时并没有把一组数据真的加载到内存,而是传递一个迭代器作为形参values传进来,在reduce方法中使用这个迭代器时,hashNext方法中判断nextKeyIsSame,也就是判断下一条是不是还是一组;然后用next方法负责调取nextKeyValue方法,从reduceTask级别的迭代器中取记录,并同时更新nextKeyIsSame,这个过程充分利用了迭代器模式,规避了内存数据OOM的问题【利用了迭代器模式后只需要一次IO就行,因为真正的就是多个迭代器嵌套着玩,一次IO就可以线性处理完数据】
          在这里插入图片描述
          在这里插入图片描述
    • map与reduce中run方法的区别:
      在这里插入图片描述
      • map中的run方法
        在这里插入图片描述
      • reduce中run方法,reduce拉取回属于自己的所有的分区数据,把这些数据包装为分区级别的迭代器【Iterator = shuffle…】
        在这里插入图片描述
    • mapTask和reduceTask的比较器:
      在这里插入图片描述
      • 排序比较器和分组比较器:
        • 排序比较器返回值为-1,0,1,代表大于小于等于三种比较情况。排序比较器可以做分组比较器
        • 分组比较器,返回值为布尔值,相等不等true、false
      • mapTask
        • 1.先取用户定义的排序比较器
        • 2.取不到用户自定义的就去取key自身的排序比较器
      • reduceTask
        • 1.取用户自定义设置的分组比较器
        • 2.取getOutputKeyComparator,也就是优先取用户覆盖重写的自定义的排序比较器,如果用户没覆盖重写,就取key自身的这个类型的排序比较器

8.map和reduce的join操作

  • reduce端的join操作
    在这里插入图片描述
    • client做的事情
      在这里插入图片描述

巨人的肩膀

Hadoop官方文档:https://hadoop.apache.org/
https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/ResourceManagerHA.html
凤凰架构
深入理解计算机系统
https://cloud.tencent.com/developer/article/2088864
想做会飞的鱼:https://blog.csdn.net/xiaokang123456kao/article/details/73195681

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值