18Hadoop大合集

此次采用问题加回答的方式。

Hadoop1.x和Hadoop2.x的区别

Hadoop1.0即第一代Hadoop,内核主要由HDFS和MapReduce两个系统组成,其中MapReduce是一个离线处理框架,由编程模型(新旧API)、运行时环境(JobTracker和TaskTracker)数据处理引擎(MapTask和ReduceTask)三部分组成。
Hadoop2.0即第二代Hadoop,内核主要由
HDFS、MapReduce和YARN三个系统组成
,其中YARN是一个资源管理系统,负责集群资源管理和调度,MapReduce则是运行在YARN上的离线处理框架,它与Hadoop 1.0中的MapReduce在编程模型(新旧API)和数据处理引擎(MapTask和ReduceTask)两个方面是相同的。

两者的区别:

  • 从Hadoop整体框架来说
    Hadoop1.0由分布式存储系统HDFS和分布式计算框架MapReduce组成,其中HDFS由一个NameNode和多个DateNode组成,MapReduce由一个JobTracker和多个TaskTracker组成
    Hadoop2.0在1.0的基础上做了如下改动:
    1、解决了NameNode单点故障问题,同时针对Hadoop1.0单NameNode制约HDFS的扩展性问题,提出HDFS Federation,它让多个NameNode分管不同的目录进而实现访问隔离和横向扩展。
    2、引入了资源管理框架Yarn,针对Hadoop1.0中的MapReduce在扩展性和多框架支持等方面的不足,它将JobTracker中的资源管理和作业控制分开,分别由ResourceManager(负责所有应用程序的资源分配)和ApplicationMaster(负责管理一个应用程序)实现。
    3、Yarn作为Hadoop2.0中的资源管理系统,它是一个通用的资源管理模块,可为各类应用程序进行资源管理和调度,不仅限于MapReduce一种框架,也可以为其他框架使用,如Tez、Spark、Storm等。
  • 从MapReduce计算框架来讲
    MapReduce1.0计算框架主要由三部分组成:编程模型、数据处理引擎和运行时环境。它的数据处理引擎由MapTask和ReduceTask组成,分别负责Map阶段逻辑和Reduce阶段的逻辑处理;它的运行时环境由一个JobTracker和若干个TaskTracker两类服务组成,其中JobTracker负责资源管理和所有作业的控制,TaskTracker负责接收来自JobTracker的命令并执行它。
    MapReducer2.0具有与MRv1相同的编程模型和数据处理引擎,唯一不同的是运行时环境。它的运行时环境不再由JobTracker和TaskTracker等服务组成,而是变为通用资源管理系统Yarn和作业控制进程ApplicationMaster,其中Yarn负责资源管理的调度而ApplicationMaster负责作业的管理。

HDFS和MapReduce的设计思路

1、 HDFS

  • 将文件进行切块处理,再通过文件信息服务器Namenode存放切块的文件信息存放地址,实际存放数据的服务器Datanode存放切块后的数据。
  • 系统默认:每个片块大小为128M,以保证寻址速度;数据会写入3个Datanode中,以保证更高的容错性。
  • HDFS还设计了Secondary Namenode来更新Namenode的日志记录文件,以避免日志文件过大。
  • HDFS Client帮助Namenode对写入读取数据进行预处理,进行文件的分块与发送读取操作。Namenode负责为数据任务寻址。

2、MapReduce

  • 通过JobClient生成任务运行文件,并在JobTracker进行调度指派TaskTracker完成任务。
  • JobTracker分为把任务文件进行分解并派送到TaskTracker的程序JobinProgress和执行调度器的TaskScheduler。
  • JobinProgress把作业分解成Map计算和Reduce计算并放置到TaskTracker服务器中。

Hadoop集群初始化的时候namenode都做了哪些工作

Hadoop集群搭建好之后,最开始都要进行初始化:hadoop namenode -format
NameNode主要被用来管理整个分布式文件系统的命名空间(实际上就是目录和文件)的元数据信息,同时为了保证数据的可靠性,还加入了操作日志,所以,NameNode会持久化这些数据(保存到本地的文件系统中)。对于第一次使用HDFS,在启动NameNode时,需要先执行-format命令,然后才能正常启动NameNode节点的服务。
在NameNode节点上,有两个最重要的路径,分别被用来存储元数据信息和操作日志,而这两个路径来自于配置文件,它们对应的属性分别是dfs.name.dir和dfs.name.edits.dir。格式化时,NameNode会清空两个目录下的所有文件,然后创建current/fsimage、edits、fstime、VERSION、image/fsimage
其中:
fsimage:存储命名空间(实际上就是目录和文件)的元数据信息。
edits:用来存储对命名空间操作的日志信息,实现NameNode节点的恢复。
fstime:用来存储元数据上一次check point 的时间。
VERSION:用来存储NameNode版本信息,命名空间ID(版本号)。
/image/fsimage: 上一次提交前的/current/fsimage文件。

SecondaryNamenode有什么作用

SecondaryNameNode的职责是合并NameNode的edit logs到fsimage文件中,防止edit logs变大后元数据恢复速度变慢。
SecondaryNameNode的工作流程:

  • 首先,它定时到NameNode去获取edit logs,并更新到fsimage上。
  • 一旦它有了新的fsimage文件,它将其拷贝回NameNode中。
  • NameNode更新fstime,下次重启时会使用这个新的fsimage文件,从而减少重启的时间。

Mapreduce流程简介

MR的流程,从数据输入到结果数据输出,大概可以分为以下8步:
在这里插入图片描述
1、切片

  • 在FileInputFormat中,计算切片大小的逻辑:Math.max(minSize, Math.min(maxSize, blockSize))
  • minSize的默认值是1,而maxSize的默认值是long类型的最大值,即可得切片的默认大小是blockSize(128M)
  • maxSize参数如果调得比blocksize小,则会让切片变小,而且就等于配置的这个参数的值
  • minSize参数调的比blockSize大,则可以让切片变得比blocksize还大
  • hadoop为每个分片构建一个map任务,可以并行处理多个分片上的数据
  • 分片也不能切得太小,否则多个map和reduce间数据的传输时间,管理分片,构建多个map任务的时间将决定整个作业的执行时间

如果文件大小小于128M,则该文件不会被切片,不管文件多小都会是一个单独的切片,交给一个maptask处理.如果有大量的小文件,将导致产生大量的maptask,大大降低集群性能。
大量小文件的优化策略:
(1) 在数据处理的前端就将小文件整合成大文件,再上传到hdfs上,即避免了hdfs不适合存储小文件的缺点,又避免了后期使用mapreduce处理大量小文件的问题。(最提倡的做法)
(2)小文件已经存在hdfs上了,可以使用另一种inputformat来做切片(CombineFileInputFormat),它的切片逻辑和FileInputFormat(默认)不同,它可以将多个小文件在逻辑上规划到一个切片上,交给一个maptask处理。
2、环形缓存区

  • 经过map函数的逻辑处理后的数据输出之后,会通过OutPutCollector收集器将数据收集到环形缓存区保存。
  • 环形缓存区的大小默认为100M,当保存的数据达到80%时,就将缓存区的数据溢出到磁盘上保存。

3、溢出

  • 环形缓存区的数据达到其容量的80%时就会溢出到磁盘上进行保存,在此过程中,程序会对数据进行分区(默认HashPartition)和排序(默认根据key进行快排)
  • 缓存区不断溢出的数据形成多个小文件

4、合并

  • 溢出的多个小文件各个区合并在一起(0区和0区合并成一个0区),形成大文件
  • 通过归并排序保证区内的数据有序

5、Shuffle
从过程2到过程7之间,即map任务和reduce任务之间的数据流称为shuffle(混洗),而过程5最能体现出混洗这一概念。一般情况下,一个reduce任务的输入数据来自与多个map任务,多个reduce任务的情况下就会出现如过程5所示的,每个reduce任务从map的输出数据中获取属于自己的那个分区的数据。

6、合并
运行reducetask的节点通过过程5,将来自多个map任务的属于自己的分区数据下载到本地磁盘工作目录。这多个分区文件通过归并排序合并成大文件,并根据key值分好组(key值相同的,value值会以迭代器的形式组在一起)。

7、reducetask
reducetask从本地工作目录获取已经分好组并且排好序的数据,将数据进行reduce函数中的逻辑处理。

8、输出
每个reducetask输出一个结果文件。

Hadoop shuffle流程

从Map输出到Reduce输入的整个过程可以广义地称为Shuffle。
hadoop shuffle的流程也就是MapReduce的流程中的 2 -7 步,shuffle可分为map端shuffle和reduce端shuffle。
map端shuffle包含了环形缓冲区、溢写、排序、合并。
reduce端shuffle包含了copy、合并排序。
在这里插入图片描述

MapReduce的job提交运行流程

mapreduce提交应用程序到yarn,主要是用AM来管理的,具体流程如下:
在这里插入图片描述第一步: 提交作业

  • Client提交Job:Client编写好Job后,调用Job实例的Submit() 或者 * waitForCompletion() 方法提交作业;然后从RM中获取新的作业ID,在YARN命名法中它是一个Application ID(步骤2)。
  • Job提交到RM:Client检查作业的输出说明,计算输入分片,并将作业资源(包括作业JAR、配置和分片信息)复制到HDFS来分发jar包(步骤3)。
  • 调用RM的 submitApplication() 方法提交作业(步骤4)。
    第二步: 作业初始化
  • 给作业分配ApplicationMaster: RM收到调用它的 submitApplication() 消息后,便将请求传递给 Scheduler (调度器);Scheduler分配一个 Container,然后 RM在该 NM的管理下在 Container中启动 ApplicationMaster(步骤5a & 5b)。
  • ApplicationMaster初始化作业: MR作业的ApplicationMaster 是一个Java应用程序,它的主类是 MRAppMaster。它对作业进行初始化:通过创建多个薄记对象以保持对作业进度的跟踪,因为它将接受来自任务的进度和完成报告(步骤6);ApplicationMaster 从HDFS中获取 在Client 计算的输入分片(map、reduce任务数)(步骤7)。
  • ApplicationMaster决定如何运行构成 MapReduce 作业的各个任务。如果作业很小,就选择在与它同一个JVM上运行。
    第三步: 任务分配
    ApplicationMaster 为该作业中的所有 map 任务和 reduce 任务向 RM 请求 Container (步骤8);【随着心跳信息的请求包括每个map任务的数据本地化信息,特别是输入分片所在的主机和相应机架信息。理想情况下,它将任务分配到数据本地化的节点,但如果不可能这么做,就会相对于本地化的分配优先使用机架本地化的分配】
    请求也为任务指定了内存需求。在默认情况下, map任务和reduce任务都分配到 1024MB 的内存,但这可以通过 mapreduce.map.memory.mb 和 mapreduce.reduce.memory.mb来设置。
    第四步: 任务执行
    一旦 RM 的 Scheduler 为任务分配了 Container, ApplicationMaster就通过与 NM通信来启动 Container(步骤9a & 9b);
    该任务由主类为 YardChild 的Java应用程序执行。在它运行任务之前,首先将任务需要的资源本地化(包括作业的配置、JAR文件和所有来自分布式缓存的文件)(步骤10);
    最后,运行 map 任务或 reduce 任务(步骤11)。
    第五步: 进度和状态的更新
    在YARN下运行,任务每 3s通过 umbilical 接口向 ApplicationMaster 汇报进度和状态(包括计数器),作为作业的汇聚试图(aggregate view)。
    第六步: 作业完成
    除了向 ApplicationMaster 查询进度外,Client 每 5s还通过调用 Job 的 waitForCompletion() 来检查作业是否完成【查询的间隔可以通过 mapreduce.client.completion.pollinterval 属性进行设置】。
    作业完成后, ApplicationMaster 和任务容器清理其工作状态, OutputCommitter 的作业清理方法会被调用。作业历史服务器保存作业的信息供用户需要时查询。

Mapreduce怎么进行序列化反序列化的(inputFormat,outputFormat)

首先来看一下MapReduce中的输入输出类型:

  • 输入数据类型由输入格式(InputFormat)设置,比如:TextInputFormat的Key类型为LongWritable,Value的类型是Text。
  • map函数输出的Key类型通过setMapOutputKeyClass设置,Value类型通过setMapOutputValueClass设置。
  • reduce的输出的Key类型通过setOutputKeyClass设置,Value类型通过setOutputValueClass设置。
MapReduce输入格式

InputFormat接口是MapReduce的输入格式接口,InputFormat接口实现类有很多,其层次结构如下所示:
在这里插入图片描述

  • FileInputFormat
    FileInputFormat是所有使用文件作为其数据源的InputFormat实现的基类,它的主要作用是指出作业的输入文件位置。
  • KeyValueTextInputFormat
    每一行均为一条记录,被分隔符(缺省是tab)分割为key(Text),value(Text),当然分隔符可以自己设定。
  • TextInputFormat
    默认的输入格式是TextInputFormat,它把输入文件每一行作为单独的一个记录,但不做解析处理,这对那些没有被格式化的数据或是基于行的记录来说是很有用的,比如日志文件。
MapReduce输出格式

OutputFormat接口主要用于描述输出数据的格式,它能够将用户提供的key/value对写入特定格式的文件中。其功能与前面描述的InputFormat相似,Hadoop提供的OutputFormat实例会把文件写在本地磁盘或HDFS上。每一个reducer会把结果输出写在公共文件夹中一个单独的文件内。这些文件一般命名为:part-xxxxx,而xxxxx是关联到某个reduce任务的partition的id。

OutputFormat接口实现类如下图:
在这里插入图片描述

  • 文本输出
    默认的输出格式是TextOutputFormat,它把每条记录写为文本行。它的键和值可以实现Writable的任意类型,因为TextOutputFormat调用toString()方法把他们转换为字符串。每一个key/value键值对都以制表符为分隔符,当然也可以自行设定(mapreduce.output.textoutputfomat.separator),与FileOutputFormat对应的输入格式是KeyValueTextInputFormat,它可以通过配置分隔符将key/value进行文本分割。
    同时,可以使用NullWritable来省略输出的key或value,或者两个都省略,相当于NullOutputFormat的输出格式,这也会导致无分隔符的输出,以使输出适合用TextInputFormat读取。
  • 二进制输出
    SequenceFileOutputFormat将它的输出写为一个顺序文件。如果输出需要作为后续的MapReduce任务的输入,这便是一种合适的输出格式,可以通过快速的序列化任意的数据类型到文件中,而对应SequenceFileInputFormat则会把文件反序列化为相同类型并提交到下一个Mapper的输入数据

yarn的资源调度器

在Yarn中,负责给应用分配资源的是Scheduler,并提供了多种调度器和可配置的策略供选择。
Yarn中有是三种调度器可以选择:FIFO Scheduler,Capacity Scheduler,Fair Scheduler
调度器中的配置修改后可以使用admin进行动态刷新。
在这里插入图片描述

FIFO Scheduler
  • FIFO Scheduler把应用按提交的顺序排成一个队列,这是一个先进先出队列,在进行资源分配的时候,先给队列中最头上的应用分配资源,待最头上的应用需求满足后再给下一个分配,以此类推。
  • FIFO Scheduler是最简单也是最容易理解的调度器,不需要任何配置,但其**不适用于共享集群。大的应用可能会占用所有集群资源,这就导致其它应用被阻塞。**在共享集群中,更适合采用Capacity Scheduler或Fair Scheduler,这两种调度器都允许大任务和小任务在提交的同时获得一定的资源。
Capacity Scheduler
  • Capacity调度器,有一个专门的队列用来运行小任务,但是为小任务专门设置一个队列会占用一定的集群资源,这就导致大任务的执行时间会落后于使用FIFO调度器时的时间。
  • Capacity 调度器允许多个组织共享整个集群,每个组织可以获得集群的一部分计算能力。通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源,这样整个集群就可以通过设置多个队列的方式给多个组织提供服务了。除此之外,队列内部又可以垂直划分,这样一个组织内部的多个成员就可以共享这个队列资源了,在一个队列内部,资源的调度是采用的是先进先出(FIFO)策略
  • “弹性队列”(queue elasticity),如果这个队列的资源不够用了呢?其实Capacity调度器仍可能分配额外的资源给这个队列,当一个队列资源不够用时,这个队列只能获得其它队列释放后的Container资源。当然,我们可以为队列设置一个最大资源使用量,以免这个队列过多的占用空闲资源,导致其它队列无法使用这些空闲资源,这就是”弹性队列”需要权衡的地方。
Fair Scheduler
  • Fair调度器中,我们不需要预先占用一定的系统资源,Fair调度器会为所有运行的job动态的调整系统资源。当第一个大job提交时,只有这一个job在运行,此时它获得了所有集群资源;当第二个小任务提交后,Fair调度器会分配一半资源给这个小任务,让这两个任务公平的共享集群资源。
  • 需要注意的是,**在Fair调度器中,从第二个任务提交到获得资源会有一定的延迟,因为它需要等待第一个任务释放占用的Container。**小任务执行完成以后也会释放自己占用的资源,大任务又获得了全部的系统资源。最终的效果就是Fair调度器既得到了高资源的利用率又能保证小任务的及时执行。
  • 每个队列内部仍可以有不同的调度策略。
  • 除非队列被准确的定义,否则会以用户名为队列名创建队列,还有一个简单的配置策略可以使得所有的应用放入同一个队列(default),这样就可以让所有应用之间平等共享集群而不是在用户之间。直接设置yarn.scheduler.fair.user-as-default-queue=false,这样应用便会被放入default 队列,而不是各个用户名队列。另外,我们还可以设置yarn.scheduler.fair.allow-undeclared-pools=false,这样用户就无法创建队列了。
  • 每个队列可配置最大、最小资源占用数和最大可运行的应用的数量。
  • 当一个job提交到一个繁忙集群中的空队列时,job并不会马上执行,而是阻塞直到正在运行的job释放系统资源。为了使提交job的执行时间更具预测性(可以设置等待的超时时间),Fair调度器支持抢占。抢占就是允许调度器杀掉占用超过其应占份额资源队列的containers,这些containers资源便可被分配到应该享有这些份额资源的队列中。需要注意抢占会降低集群的执行效率,因为被终止的containers需要被重新执行

MapReduce的优化

MapReduce的优化可以从以下几个大的方面去进行:小文件优化数据倾斜优化MR程序map端优化MR程序reduce端优化

小文件优化

小文件的弊端:

  • 存储方面:很对小文件会有很多元数据信息,对NameNode造成压力。
  • 读取方面:很多小文件增加了磁盘寻址次数,降低性能
  • 计算方面:一个map默认处理一个分片或者一个小文件,如果map的启动时间要比计算时间还长,那么会造成性能降低;而且map端在溢写磁盘的时候,每一个map要生成reduce个数的的中间结果,,若map个数很多,则会造成中间临时文件很多,在reduce拉取数据的时候增加磁盘IO。

小文件优化策略:

  • 从源头干掉,也就是在hdfs上我们不存储小文件,也就是数据上传hdfs的时候我们就合并小文件。
  • 在FileInputFormat读取入数据的时候我们使用实现类CombineFileInputFormat读取数据,在读取数据的时候进行合并。
数据倾斜的优化

数据倾斜产生的原因:
数据本身就不平衡,所以在默认的hashpartition时造成分区数据不一致问题,还有就是代码设计不合理等,导致每个reduce处理的数据量不是同一个级别的,所以有些已经跑完了,而有些跑的很慢。
如何解决数据倾斜:

  • 自定义分区。既然默认的是hash算法进行分区,那我们自定义分区,修改分区实现逻辑,结合业务特点,使得每个分区数据基本平衡。
  • 修改分区的键。既然有默认的分区算法,那么我们可以修改分区的键,让其符合hash分区,并且使得最后的分区平衡,比如在key前加随机数n-key。
  • 增加单个reduce资源。既然reduce处理慢,我们可以增加reduce的内存和vcore呀,这样挺高性能就快了,虽然没从根本上解决问题,但是还有效果。
  • 增加reduce个数。既然一个reduce处理慢,那我们可以增加reduce的个数来分摊一些压力呀,也不能根本解决问题,还是有一定的效果。
MR程序map端优化
  • 选择合适的文件读取类。在前面我们聊过小文件的问题,所以在数据的读取这里也可以做优化,所以选择一个合适数据的文件的读取类(FIleInputFormat的实现类)也很重要。
  • 我们在作业提交的过程中,会把jar,分片信息,资源信息提交到hdfs的临时目录,默认会有10个复本,通过参数mapreduce.client.submit.file.replication控制后期作业执行都会去下载这些东西到本地,中间会产生磁盘IO,所以如果集群很大的时候,可以增加该值,提高下载的效率。
  • 计算合理的map分片数。计算切片大小的逻辑:Math.max(minSize, Math.min(maxSize, blockSize))。map数没有具体的参数指定,所以我们可以通过如上的公式调整切片的大小,这样我们就可以设置map数了。
  • 合理的map数规则。map数的设定一定要结合业务,map数太多,会产生很多中间结果,导致reduce拉取数据变慢,太少,每个map处理的时间又很长,结合数据的需求,可以把map的执行时间调至到一分钟左右比较合适,那如果数据量就是很大呢,我们有时候还是需要控制map的数量,这个时候每个map的执行时间就比较长了,那么我们可以调整每个map的资源来提升map的处理能力呀,我司就调整了mapreduce.map.memory.mb=3G(默认1G)mapreduce.map.cpu.vcores=1(默认也是1)
  • 环形缓冲区大小,溢写优化。缓冲默认100Mmapreduce.task.io.sort.mb参数控制),当到达80%mapreduce.map.sort.spill.percent参数控制)时就会溢写磁盘。当内存足够,我们增大mapreduce.task.io.sort.mb完全会提高溢写的过程,而且会减少中间结果的文件数量。当文件溢写完后,会对这些文件进行合并,默认每次合并10mapreduce.task.io.sort.factor参数控制)个溢写的文件,可调整mapreduce.task.io.sort.factor=64。这样可以提高合并的并行度,减少合并的次数,降低对磁盘操作的次数。
  • mapreduce.shuffle.max.threads(默认为0,表示可用处理器的两倍),该参数表示每个节点管理器的工作线程,用于map输出到reduce。
  • combiner操作。在reduce拉取数据之前,我们完全还可以combiner呀(不影响最终结果的情况下),此时会根据Combiner定义的函数对map的结果进行合并这样就可以减少数据的传输,降低磁盘io,提高性能。
  • 数据压缩。从map到reduce的数据传输可以通过mapreduce.map.output.compress(default:false)设置为true进行压缩,数据会被压缩写入磁盘。mapreduce.map.output.compress.codec(default:org.apache.hadoop.io.compress.DefaultCodec)参数设置,可使用org.apache.hadoop.io.compress.SnappyCodec算法
    在这里插入图片描述
MR程序reduce端优化
  • 设置合理的reduce个数。可以通过参数设置合理的reduce个数(mapreduce.job.reduces参数控制)。
  • 每个reduce设置合理的资源。通过参数设置每个reduce的资源,mapreduce.reduce.memory.mb=5G(默认1G)
    mapreduce.reduce.cpu.vcores=1(默认为1)。
  • 调整数据复制并行度。reduce在copy的过程中默认使用5mapreduce.reduce.shuffle.parallelcopies参数控制)个并行度进行复制数据,可调整至mapreduce.reduce.shuffle.parallelcopies=100。
  • 设置reduce端shuffle超时时间。reduce的每一个下载线程在下载某个map数据的时候,有可能因为那个map中间结果所在机器发生错误,或者中间结果的文件丢失,或者网络瞬断等等情况,这样reduce的下载就有可能失败,所以reduce的下载线程并不会无休止的等待下去,当一定时间后下载仍然失败,那么下载线程就会放弃这次下载,并在随后尝试从另外的地方下载(因为这段时间map可能重跑)。reduce下载线程的这个最大的下载时间段是可以通过**mapreduce.reduce.shuffle.read.timeout(default180000豪秒)**调整的。
  • reduce端内存缓冲池比例优化。Copy过来的数据会先放入内存缓冲区中,然后当使用内存达到一定量的时候才spill磁盘。这里的缓冲区大小要比map端的更为灵活,它基于JVM的heap size设置。这个内存大小的控制就不像map一样可以通过io.sort.mb来设定了,而是通过另外一个参数 **mapreduce.reduce.shuffle.input.buffer.percent(default 0.7)控制的。意思是说,shuffile在reduce内存中的数据最多使用内存量为:0.7 × maxHeap of reduce task,内存到磁盘merge的启动门限可以通过mapreduce.reduce.shuffle.merge.percent(default0.66)**配置(为内存缓冲池的66%)。
  • reduce端归并因子调优。copy完成后,reduce进入归并排序阶段,合并因子默认为10(mapreduce.task.io.sort.factor参数控制),如果map输出很多,则需要合并很多趟,所以可以提高此参数来减少合并次数。

**归并因子详解:假设这里有50 个Map 输出(可能有保存在内存中的),并且归并因子是10(由io.sort.factor控制,就像Map 端的merge 一样),那最终需要5 次归并。每次归并会把10个文件归并为一个,最终生成5 个中间文件。在这一步之后,系统不再把5 个中间文件归并成一个,而是排序后直接“喂”给Reduce 函数,省去向磁盘写数据这一步。最终归并的数据可以是混合数据,既有内存上的也有磁盘上的。由于归并的目的是归并最少的文件数目,使得在最后一次归并时总文件个数达到归并因子的数目,所以每次操作所涉及的文件个数在实际中会更微妙些。**譬如,如果有40 个文件,并不是每次都归并10 个最终得到4 个文件,相反第一次只归并4 个文件,然后再实现三次归并,每次10 个,最终得到4 个归并好的文件和6 个未归并的文件。要注意,这种做法并没有改变归并的次数,只是最小化写入磁盘的数据优化措施,因为最后一次归并的数据总是直接送到Reduce 函数那里。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值