最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MapTask階段shuffle源碼分析

 更新時(shí)間:2019年01月10日 09:57:37   作者:qq_43193797  
今天小編就為大家分享一篇關(guān)于MapTask階段shuffle源碼分析,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧

1. 收集階段

Mapper中,調(diào)用context.write(key,value)實(shí)際是調(diào)用代理NewOutPutCollectorwirte方法

public void write(KEYOUT key, VALUEOUT value
          ) throws IOException, InterruptedException {
  output.write(key, value);
 }

實(shí)際調(diào)用的是MapOutPutBuffercollect(),在進(jìn)行收集前,調(diào)用partitioner來(lái)計(jì)算每個(gè)key-value的分區(qū)號(hào)

@Override
  public void write(K key, V value) throws IOException, InterruptedException {
   collector.collect(key, value,
            partitioner.getPartition(key, value, partitions));
  }

2. NewOutPutCollector對(duì)象的創(chuàng)建

@SuppressWarnings("unchecked")
  NewOutputCollector(org.apache.hadoop.mapreduce.JobContext jobContext,
            JobConf job,
            TaskUmbilicalProtocol umbilical,
            TaskReporter reporter
            ) throws IOException, ClassNotFoundException {
  // 創(chuàng)建實(shí)際用來(lái)收集key-value的緩存區(qū)對(duì)象
   collector = createSortingCollector(job, reporter);
  // 獲取總的分區(qū)個(gè)數(shù)
   partitions = jobContext.getNumReduceTasks();
   if (partitions > 1) {
    partitioner = (org.apache.hadoop.mapreduce.Partitioner<K,V>)
     ReflectionUtils.newInstance(jobContext.getPartitionerClass(), job);
   } else {
    // 默認(rèn)情況,直接創(chuàng)建一個(gè)匿名內(nèi)部類(lèi),所有的key-value都分配到0號(hào)分區(qū)
    partitioner = new org.apache.hadoop.mapreduce.Partitioner<K,V>() {
     @Override
     public int getPartition(K key, V value, int numPartitions) {
      return partitions - 1;
     }
    };
   }
  }

3. 創(chuàng)建環(huán)形緩沖區(qū)對(duì)象

@SuppressWarnings("unchecked")
 private <KEY, VALUE> MapOutputCollector<KEY, VALUE>
     createSortingCollector(JobConf job, TaskReporter reporter)
  throws IOException, ClassNotFoundException {
  MapOutputCollector.Context context =
   new MapOutputCollector.Context(this, job, reporter);
  // 從當(dāng)前Job的配置中,獲取mapreduce.job.map.output.collector.class,如果沒(méi)有設(shè)置,使用MapOutputBuffer.class
  Class<?>[] collectorClasses = job.getClasses(
   JobContext.MAP_OUTPUT_COLLECTOR_CLASS_ATTR, MapOutputBuffer.class);
  int remainingCollectors = collectorClasses.length;
  Exception lastException = null;
  for (Class clazz : collectorClasses) {
   try {
    if (!MapOutputCollector.class.isAssignableFrom(clazz)) {
     throw new IOException("Invalid output collector class: " + clazz.getName() +
      " (does not implement MapOutputCollector)");
    }
    Class<? extends MapOutputCollector> subclazz =
     clazz.asSubclass(MapOutputCollector.class);
    LOG.debug("Trying map output collector class: " + subclazz.getName());
   // 創(chuàng)建緩沖區(qū)對(duì)象
    MapOutputCollector<KEY, VALUE> collector =
     ReflectionUtils.newInstance(subclazz, job);
   // 創(chuàng)建完緩沖區(qū)對(duì)象后,執(zhí)行初始化
    collector.init(context);
    LOG.info("Map output collector class = " + collector.getClass().getName());
    return collector;
   } catch (Exception e) {
    String msg = "Unable to initialize MapOutputCollector " + clazz.getName();
    if (--remainingCollectors > 0) {
     msg += " (" + remainingCollectors + " more collector(s) to try)";
    }
    lastException = e;
    LOG.warn(msg, e);
   }
  }
  throw new IOException("Initialization of all the collectors failed. " +
   "Error in last collector was :" + lastException.getMessage(), lastException);
 }

3. MapOutPutBuffer的初始化   環(huán)形緩沖區(qū)對(duì)象

@SuppressWarnings("unchecked")
  public void init(MapOutputCollector.Context context
          ) throws IOException, ClassNotFoundException {
   job = context.getJobConf();
   reporter = context.getReporter();
   mapTask = context.getMapTask();
   mapOutputFile = mapTask.getMapOutputFile();
   sortPhase = mapTask.getSortPhase();
   spilledRecordsCounter = reporter.getCounter(TaskCounter.SPILLED_RECORDS);
   // 獲取分區(qū)總個(gè)數(shù),取決于ReduceTask的數(shù)量
   partitions = job.getNumReduceTasks();
   rfs = ((LocalFileSystem)FileSystem.getLocal(job)).getRaw();
   //sanity checks
   // 從當(dāng)前配置中,獲取mapreduce.map.sort.spill.percent,如果沒(méi)有設(shè)置,就是0.8
   final float spillper =
    job.getFloat(JobContext.MAP_SORT_SPILL_PERCENT, (float)0.8);
   // 獲取mapreduce.task.io.sort.mb,如果沒(méi)設(shè)置,就是100MB
   final int sortmb = job.getInt(JobContext.IO_SORT_MB, 100);
   indexCacheMemoryLimit = job.getInt(JobContext.INDEX_CACHE_MEMORY_LIMIT,
                     INDEX_CACHE_MEMORY_LIMIT_DEFAULT);
   if (spillper > (float)1.0 || spillper <= (float)0.0) {
    throw new IOException("Invalid \"" + JobContext.MAP_SORT_SPILL_PERCENT +
      "\": " + spillper);
   }
   if ((sortmb & 0x7FF) != sortmb) {
    throw new IOException(
      "Invalid \"" + JobContext.IO_SORT_MB + "\": " + sortmb);
   }
// 在溢寫(xiě)前,對(duì)key-value排序,采用的排序器,使用快速排序,只排索引
   sorter = ReflectionUtils.newInstance(job.getClass("map.sort.class",
      QuickSort.class, IndexedSorter.class), job);
   // buffers and accounting
   int maxMemUsage = sortmb << 20;
   maxMemUsage -= maxMemUsage % METASIZE;
   // 存放key-value
   kvbuffer = new byte[maxMemUsage];
   bufvoid = kvbuffer.length;
  // 存儲(chǔ)key-value的屬性信息,分區(qū)號(hào),索引等
   kvmeta = ByteBuffer.wrap(kvbuffer)
     .order(ByteOrder.nativeOrder())
     .asIntBuffer();
   setEquator(0);
   bufstart = bufend = bufindex = equator;
   kvstart = kvend = kvindex;
   maxRec = kvmeta.capacity() / NMETA;
   softLimit = (int)(kvbuffer.length * spillper);
   bufferRemaining = softLimit;
   LOG.info(JobContext.IO_SORT_MB + ": " + sortmb);
   LOG.info("soft limit at " + softLimit);
   LOG.info("bufstart = " + bufstart + "; bufvoid = " + bufvoid);
   LOG.info("kvstart = " + kvstart + "; length = " + maxRec);
   // k/v serialization
    // 獲取快速排序的Key的比較器,排序只按照key進(jìn)行排序!
   comparator = job.getOutputKeyComparator();
  // 獲取key-value的序列化器
   keyClass = (Class<K>)job.getMapOutputKeyClass();
   valClass = (Class<V>)job.getMapOutputValueClass();
   serializationFactory = new SerializationFactory(job);
   keySerializer = serializationFactory.getSerializer(keyClass);
   keySerializer.open(bb);
   valSerializer = serializationFactory.getSerializer(valClass);
   valSerializer.open(bb);
   // output counters
   mapOutputByteCounter = reporter.getCounter(TaskCounter.MAP_OUTPUT_BYTES);
   mapOutputRecordCounter =
    reporter.getCounter(TaskCounter.MAP_OUTPUT_RECORDS);
   fileOutputByteCounter = reporter
     .getCounter(TaskCounter.MAP_OUTPUT_MATERIALIZED_BYTES);
   // 溢寫(xiě)到磁盤(pán),可以使用一個(gè)壓縮格式! 獲取指定的壓縮編解碼器
   // compression
   if (job.getCompressMapOutput()) {
    Class<? extends CompressionCodec> codecClass =
     job.getMapOutputCompressorClass(DefaultCodec.class);
    codec = ReflectionUtils.newInstance(codecClass, job);
   } else {
    codec = null;
   }
   // 獲取Combiner組件
   // combiner
   final Counters.Counter combineInputCounter =
    reporter.getCounter(TaskCounter.COMBINE_INPUT_RECORDS);
   combinerRunner = CombinerRunner.create(job, getTaskID(),
                       combineInputCounter,
                       reporter, null);
   if (combinerRunner != null) {
    final Counters.Counter combineOutputCounter =
     reporter.getCounter(TaskCounter.COMBINE_OUTPUT_RECORDS);
    combineCollector= new CombineOutputCollector<K,V>(combineOutputCounter, reporter, job);
   } else {
    combineCollector = null;
   }
   spillInProgress = false;
   minSpillsForCombine = job.getInt(JobContext.MAP_COMBINE_MIN_SPILLS, 3);
   // 設(shè)置溢寫(xiě)線程在后臺(tái)運(yùn)行,溢寫(xiě)是在后臺(tái)運(yùn)行另外一個(gè)溢寫(xiě)線程!和收集是兩個(gè)線程!
   spillThread.setDaemon(true);
   spillThread.setName("SpillThread");
   spillLock.lock();
   try {
   // 啟動(dòng)線程
    spillThread.start();
    while (!spillThreadRunning) {
     spillDone.await();
    }
   } catch (InterruptedException e) {
    throw new IOException("Spill thread failed to initialize", e);
   } finally {
    spillLock.unlock();
   }
   if (sortSpillException != null) {
    throw new IOException("Spill thread failed to initialize",
      sortSpillException);
   }
  }

4. Paritionner的獲取

從配置中讀取mapreduce.job.partitioner.class,如果沒(méi)有指定,采用HashPartitioner.class

如果reduceTask > 1, 還沒(méi)有設(shè)置分區(qū)組件,使用HashPartitioner

@SuppressWarnings("unchecked")
 public Class<? extends Partitioner<?,?>> getPartitionerClass()
   throws ClassNotFoundException {
  return (Class<? extends Partitioner<?,?>>)
   conf.getClass(PARTITIONER_CLASS_ATTR, HashPartitioner.class);
 }
public class HashPartitioner<K, V> extends Partitioner<K, V> {
 /** Use {@link Object#hashCode()} to partition. **/
 public int getPartition(K key, V value,
             int numReduceTasks) {
  return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
 }
}

分區(qū)號(hào)的限制:0 <= 分區(qū)號(hào) < 總的分區(qū)數(shù)(reduceTask的個(gè)數(shù))

if (partition < 0 || partition >= partitions) {
    throw new IOException("Illegal partition for " + key + " (" +
      partition + ")");
   }

5.MapTask shuffle的流程

              ①在map()調(diào)用context.write()

              ②調(diào)用MapoutPutBuffer的collect()

  •                             調(diào)用分區(qū)組件Partitionner計(jì)算當(dāng)前這組key-value的分區(qū)號(hào)

              ③將當(dāng)前key-value收集到MapOutPutBuffer中

  •                             如果超過(guò)溢寫(xiě)的閥值,在后臺(tái)啟動(dòng)溢寫(xiě)線程,來(lái)進(jìn)行溢寫(xiě)!

              ④溢寫(xiě)前,先根據(jù)分區(qū)號(hào),將相同分區(qū)號(hào)的key-value,采用快速排序算法,進(jìn)行排序!

  •                             排序并不在內(nèi)存中移動(dòng)key-value,而是記錄排序后key-value的有序索引!

              ⑤ 開(kāi)始溢寫(xiě),按照排序后有序的索引,將文件寫(xiě)入到一個(gè)臨時(shí)的溢寫(xiě)文件中

  •                             如果沒(méi)有定義Combiner,直接溢寫(xiě)!
  •                             如果定義了Combiner,使用CombinerRunner.conbine()對(duì)key-value處理后再次溢寫(xiě)!

              ⑥多次溢寫(xiě)后,每次溢寫(xiě)都會(huì)產(chǎn)生一個(gè)臨時(shí)文件

              ⑦最后,執(zhí)行一次flush(),將剩余的key-value進(jìn)行溢寫(xiě)

              ⑧MergeParts: 將多次溢寫(xiě)的結(jié)果,保存為一個(gè)總的文件!

  •                      在合并為一個(gè)總的文件前,會(huì)執(zhí)行歸并排序,保證合并后的文件,各個(gè)分區(qū)也是有序的!
  •                      如果定義了Conbiner,Conbiner會(huì)再次運(yùn)行(前提是溢寫(xiě)的文件個(gè)數(shù)大于3)!
  •                      否則,就直接溢寫(xiě)!

              ⑨最終保證生成一個(gè)最終的文件,這個(gè)文件根據(jù)總區(qū)號(hào),分為若干部分,每個(gè)部分的key-value都已經(jīng)排好序,等待ReduceTask來(lái)拷貝相應(yīng)分區(qū)的數(shù)據(jù)

6. Combiner

combiner其實(shí)就是Reducer類(lèi)型:

Class<? extends Reducer<K,V,K,V>> cls =
    (Class<? extends Reducer<K,V,K,V>>) job.getCombinerClass();

Combiner的運(yùn)行時(shí)機(jī):

MapTask:

  •               ①每次溢寫(xiě)前,如果指定了Combiner,會(huì)運(yùn)行
  •               ②將多個(gè)溢寫(xiě)片段,進(jìn)行合并為一個(gè)最終的文件時(shí),也會(huì)運(yùn)行Combiner,前提是片段數(shù)>=3

ReduceTask:

              ③reduceTask在運(yùn)行時(shí),需要啟動(dòng)shuffle進(jìn)程拷貝MapTask產(chǎn)生的數(shù)據(jù)!

  •                      數(shù)據(jù)在copy后,進(jìn)入shuffle工作的內(nèi)存,在內(nèi)存中進(jìn)行merge和sort!
  •                      數(shù)據(jù)過(guò)多,內(nèi)部不夠,將部分?jǐn)?shù)據(jù)溢寫(xiě)在磁盤(pán)!
  •                      如果有溢寫(xiě)的過(guò)程,那么combiner會(huì)再次運(yùn)行!

①一定會(huì)運(yùn)行,②,③需要條件!

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • SpringBoot如何使用mail實(shí)現(xiàn)登錄郵箱驗(yàn)證

    SpringBoot如何使用mail實(shí)現(xiàn)登錄郵箱驗(yàn)證

    在實(shí)際的開(kāi)發(fā)當(dāng)中,不少的場(chǎng)景中需要我們使用更加安全的認(rèn)證方式,同時(shí)也為了防止一些用戶(hù)惡意注冊(cè),我們可能會(huì)需要用戶(hù)使用一些可以證明個(gè)人身份的注冊(cè)方式,如短信驗(yàn)證、郵箱驗(yàn)證等,這篇文章主要介紹了SpringBoot如何使用mail實(shí)現(xiàn)登錄郵箱驗(yàn)證,需要的朋友可以參考下
    2024-06-06
  • Java中final修飾的方法是否可以被重寫(xiě)示例詳解

    Java中final修飾的方法是否可以被重寫(xiě)示例詳解

    這篇文章主要給大家介紹了關(guān)于Java中final修飾的方法是否可以被重寫(xiě)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • java fastJson轉(zhuǎn)JSON兩種常見(jiàn)的轉(zhuǎn)義操作

    java fastJson轉(zhuǎn)JSON兩種常見(jiàn)的轉(zhuǎn)義操作

    在實(shí)際開(kāi)發(fā)中,我們有時(shí)需要將特殊字符進(jìn)行轉(zhuǎn)義,本文主要介紹了java fastJson轉(zhuǎn)JSON兩種常見(jiàn)的轉(zhuǎn)義操作,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • 關(guān)于maven環(huán)境的安裝及maven集成idea環(huán)境的問(wèn)題

    關(guān)于maven環(huán)境的安裝及maven集成idea環(huán)境的問(wèn)題

    Maven 是一個(gè)基于 Java 的工具,所以要做的第一件事情就是安裝 JDK。本文重點(diǎn)給大家介紹關(guān)于maven環(huán)境的安裝及和idea環(huán)境的集成問(wèn)題,感興趣的朋友一起看看吧
    2021-09-09
  • 詳解Java異常處理中throw與throws關(guān)鍵字的用法區(qū)別

    詳解Java異常處理中throw與throws關(guān)鍵字的用法區(qū)別

    這篇文章主要介紹了詳解Java異常處理中throw與throws關(guān)鍵字的用法區(qū)別,這也是Java面試題目中的常客,需要的朋友可以參考下
    2015-11-11
  • JAVA中Collections.sort()方法使用詳解

    JAVA中Collections.sort()方法使用詳解

    這篇文章主要給大家介紹了關(guān)于JAVA中Collections.sort()方法使用的相關(guān)資料,Java中Collections.sort()方法是用來(lái)對(duì)List類(lèi)型進(jìn)行排序的,文中通過(guò)代碼將使用的方法介紹的非常詳細(xì),需要的朋友可以參考下
    2024-05-05
  • java實(shí)現(xiàn)打印正三角的方法

    java實(shí)現(xiàn)打印正三角的方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)打印正三角的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Spring關(guān)于@Configuration配置處理流程

    Spring關(guān)于@Configuration配置處理流程

    這篇文章主要介紹了Spring關(guān)于@Configuration配置處理流程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Java 如何讀取Excel格式xls、xlsx數(shù)據(jù)工具類(lèi)

    Java 如何讀取Excel格式xls、xlsx數(shù)據(jù)工具類(lèi)

    這篇文章主要介紹了Java 如何讀取Excel格式xls、xlsx數(shù)據(jù)工具類(lèi)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池

    淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池

    這篇文章主要介紹了淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02

最新評(píng)論

民丰县| 宁武县| 沈阳市| 浑源县| 阿拉善盟| 林西县| 休宁县| 内黄县| 彝良县| 枞阳县| 邹城市| 游戏| 仁化县| 左贡县| 鄂温| 阿拉尔市| 丽江市| 大方县| 巴南区| 德化县| 唐山市| 东丽区| 囊谦县| 黑水县| 江阴市| 北辰区| 禄丰县| 潢川县| 保定市| 日喀则市| 盈江县| 通州市| 绍兴县| 新和县| 丰镇市| 达孜县| 尚义县| 长垣县| 集贤县| 安福县| 临桂县|