118 votes

Comment Hadoop processus records de split à travers les limites des blocs?

Selon la Hadoop - The Definitive Guide

Les enregistrements logiques qui FileInputFormats définir ne sont généralement pas parfaitement dans HDFS blocs. Par exemple, un TextInputFormat est d'enregistrements logiques sont des lignes, qui va croiser HDFS limites le plus souvent. Cela n'a aucune incidence sur le fonctionnement de votre programme-les lignes ne sont pas manqués ou cassé, par exemple-mais il vaut la peine de les connaître, car elle signifie que les données des cartes locales (qui est, les cartes qui sont en cours d'exécution sur le même hôte que leurs données d'entrée) effectuera une certaine distance de lectures. La légère surcharge cela provoque normalement n'est pas significative.

Supposons qu'une ligne d'enregistrement est divisé en deux blocs (b1 et b2). Le mappeur de traitement, le premier bloc (b1) remarquerez que la dernière ligne ne dispose pas d'une fin de ligne (EOL séparateur et va chercher le reste de la ligne à partir du prochain bloc de données (b2).

Comment le mappeur de traitement de la deuxième bloc (b2) de déterminer que le premier dossier est incomplet et doit traiter à partir de la deuxième enregistrement dans le bloc (b2)?

159voto

Charles Menguy Points 13531

Question intéressante, j'ai passé un peu de temps à regarder le code pour plus de détails et voici mes pensées. Les divisions sont gérés par le client, en InputFormat.getSplits, donc un coup d'oeil à FileInputFormat donne les informations suivantes:

  • Pour chaque fichier d'entrée, d'obtenir la longueur du fichier, la taille du bloc et de calculer la taille du segment en tant que max(minSize, min(maxSize, blockSize))maxSize correspond mapred.max.split.size et minSize est mapred.min.split.size.
  • Diviser le fichier en différents FileSplits, basée sur la répartition de la taille calculée ci-dessus. Ce qui est important ici, c'est que chaque FileSplit est initialisé avec un start paramètre correspondant à l'offset dans le fichier d'entrée. Il n'y a pas encore de manutention des lignes à ce point. La partie pertinente du code ressemble à ceci:

    while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
      int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
      splits.add(new FileSplit(path, length-bytesRemaining, splitSize, 
                               blkLocations[blkIndex].getHosts()));
      bytesRemaining -= splitSize;
    }
    

Après cela, si vous regardez l' LineRecordReader qui est défini par l' TextInputFormat, c'est l'endroit où les lignes sont traitées:

  • Lorsque vous initialisez votre LineRecordReader il tente d'instancier un LineReader qui est une abstraction pour être en mesure de lire les lignes de plus de FSDataInputStream. Il y a 2 cas:
  • Si il y a un CompressionCodec défini, ce codec est responsable de la gestion des frontières. Probablement pas pertinentes à votre question.
  • Si il n'y a pas de codec cependant, c'est là que les choses sont intéressantes: si l' start de votre InputSplit est différent de 0, alors vous backtrack 1 personnage et ensuite ignorer la première ligne que vous rencontrez identifiés par \n ou \r\n (Windows) ! Le backtrack est important parce que, dans le cas où votre ligne limites sont les mêmes que split limites, cela garantit que vous ne sautez pas de la validité de la ligne. Voici le code correspondant:

    if (codec != null) {
       in = new LineReader(codec.createInputStream(fileIn), job);
       end = Long.MAX_VALUE;
    } else {
       if (start != 0) {
         skipFirstLine = true;
         --start;
         fileIn.seek(start);
       }
       in = new LineReader(fileIn, job);
    }
    if (skipFirstLine) {  // skip first line and re-establish "start".
      start += in.readLine(new Text(), 0,
                        (int)Math.min((long)Integer.MAX_VALUE, end - start));
    }
    this.pos = start;
    

Donc, depuis que les divisions sont calculés dans le client, les utilisateurs n'ont pas besoin de s'exécutent en séquence, chaque mappeur sait déjà si il dne de jeter la première ligne ou pas.

Donc, fondamentalement, si vous avez 2 lignes de chaque 100 mo dans le même fichier, et pour simplifier disons que la scission de la taille est de 64 mo. Ensuite, lorsque l'entrée splits sont calculés, nous aurons le scénario suivant:

  • Split 1 contenant le chemin d'accès et les hôtes de ce bloc. Initialisé au début 200-200=0Mb, longueur de 64 mo.
  • Split 2 initialisé au début 200-200+64=64 mo, longueur de 64 mo.
  • Split 3 initialisé au début 200-200+128=128 mo, la longueur de 64 mo.
  • Split 4 initialisé au début 200-200+192=avec 192mo, longueur de 8 mo.
  • Mapper Une volonté processus de division 1, de départ est 0 afin de ne pas sauter de première ligne, et de lire une ligne complète qui va au-delà de la limite de 64 mo, donc des besoins de lecture à distance.
  • Mappeur B processus de split 2, le début est != 0 donc ignorer la première ligne après 64 mo-1 octet, ce qui correspond à la fin de la ligne 1 à 100 mo, qui est toujours en split 2, nous avons 28Mb de la ligne à split 2, donc à distance de lire le reste 72Mb.
  • Mappeur C processus de split 3, le début est != 0 donc ignorer la première ligne après 128 mo-1 octet, ce qui correspond à la fin de la ligne 2 à 200 mo, ce qui est en fin de fichier, de façon à ne pas faire n'importe quoi.
  • Mappeur D est la même que mappeur C sauf qu'il regarde pour un retour à la ligne après avec 192mo-1byte.

7voto

David Gruzman Points 5129

Je le vois comme suit: InputFormat est responsable pour diviser les données en logique divise en tenant compte de la nature des données.
Rien ne l'empêche de le faire, mais il peut ajouter une latence importante à l'emploi de la logique et de la lecture autour de l'souhaité diviser la taille de limites qui va se passer dans le jobtracker.
La plus simple enregistrement au courant de format d'entrée est TextInputFormat. Il fonctionne comme suit (selon ce que j'ai compris de code) - format d'entrée de créer divise par la taille, quelle que soit les lignes, mais LineRecordReader toujours :
a) Ignorer la première ligne dans la fente (ou une partie), si elle n'est pas la première division
b) Lu une ligne après la limite de la scission de la fin (si il est disponible, de sorte qu'il n'est pas le dernier split).

3voto

ArunAllamsetty Points 1273

De ce que j'ai compris, lorsque l' FileSplit est initialisé pour le premier bloc, le constructeur par défaut est appelé. Par conséquent, les valeurs de début et la durée sont à zéro au début. D'ici à la fin de la transformation du poing bloc, si la dernière ligne est incomplète, alors la valeur de la longueur est supérieure à la longueur de la fente et il va lire la première ligne du bloc suivant. Pour cette raison, la valeur de départ pour le premier bloc sera plus grand que zéro et sous cette condition, l' LineRecordReader d'ignorer la première ligne du deuxième bloc. (Voir la source)

Dans le cas où la dernière ligne du premier bloc est complet, alors la valeur de la longueur sera égale à la longueur du premier bloc et la valeur de départ pour le deuxième bloc est égal à zéro. Dans ce cas, l' LineRecordReader ne sera pas ignorer la première ligne et de lire le deuxième bloc du début.

Du sens?

0voto

user3507308 Points 1

Les contributeurs n'ont pas à communiquer. Le fichier de blocs dans HDFS et peuvent la actuelle mappeur(RecordReader) peuvent lire le bloc qui a la partie restante de la ligne. Ce qui se passe en coulisses.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X