J'ai plus de 1000 fichiers disponibles dans HDFS avec une convention de dénomination de 1_fileName.txt
à N_fileName.txt
. La taille de chaque fichier est de 1024 Mo. J'ai besoin de fusionner ces fichiers en un seul (HDFS) en gardant l'ordre du fichier. Dire 5_FileName.txt
ne devrait s'ajouter qu'après 4_fileName.txt
Quelle est la manière la meilleure et la plus rapide d'effectuer cette opération.
Existe-t-il une méthode pour effectuer cette fusion sans copier les données réelles entre les nœuds de données? Pour e-g: obtenir les emplacements de bloc de ces fichiers et créer une nouvelle entrée (FileName) dans le Namenode avec ces emplacements de bloc?
Il n'y a aucun moyen efficace de le faire, vous devrez déplacer toutes les données vers un nœud, puis revenir à HDFS.
Un scriptlet de ligne de commande pour ce faire pourrait être le suivant:
hadoop fs -text *_fileName.txt | hadoop fs -put - targetFilename.txt
Cela catiera tous les fichiers qui correspondent au glob à la sortie standard, puis vous dirigerez ce flux vers la commande put et sortirez le flux vers un fichier HDFS nommé targetFilename.txt
Le seul problème que vous avez est la structure de nom de fichier que vous avez choisie - si vous avez une largeur fixe, zérosez la partie numérique, ce serait plus facile, mais dans son état actuel, vous obtiendrez un ordre lexigraphique inattendu (1, 10, 100, 1000 , 11, 110, etc.) plutôt que l'ordre numérique (1,2,3,4, etc.). Vous pouvez contourner cela en modifiant le scriptlet pour:
hadoop fs -text [0-9]_fileName.txt [0-9][0-9]_fileName.txt \
[0-9][0-9[0-9]_fileName.txt | hadoop fs -put - targetFilename.txt
Il existe une méthode API org.Apache.hadoop.fs.FileUtil.copyMerge qui effectue cette opération:
public static boolean copyMerge(
FileSystem srcFS,
Path srcDir,
FileSystem dstFS,
Path dstFile,
boolean deleteSource,
Configuration conf,
String addString)
Il lit tous les fichiers dans srcDir
par ordre alphabétique et ajoute leur contenu à dstFile.
Si vous pouvez utiliser une étincelle. Cela peut se faire comme
sc.textFile("hdfs://...../part*).coalesce(1).saveAsTextFile("hdfs://...../filename)
J'espère que cela fonctionne, car spark fonctionne de manière distribuée, vous n'aurez pas à copier les fichiers dans un seul nœud. Bien que ce soit une prudence, la fusion des fichiers dans spark peut être lent si les fichiers sont très volumineux.
Étant donné que l'ordre des fichiers est important et que l'ordre lexicographique ne remplit pas l'objectif, il semble être un bon candidat pour écrire un programme de mappage pour cette tâche, qui peut probablement s'exécuter périodiquement. Bien sûr, il n'y a pas de réducteur, l'écrire en tant que tâche de carte HDFS est efficace car il peut fusionner ces fichiers en un seul fichier de sortie sans beaucoup de mouvement de données entre les nœuds de données. Comme les fichiers source sont dans HDFS, et puisque les tâches de mappage vont essayer l'affinité des données, il peut fusionner les fichiers sans déplacer les fichiers sur différents nœuds de données.
Le programme de mappage aura besoin d'un InputSplit personnalisé (en prenant les noms de fichiers dans le répertoire d'entrée et en le commandant selon les besoins) et d'un InputFormat personnalisé.
Le mappeur peut utiliser l'appendice hdfs ou un flux de sortie brut où il peut écrire en octet [].
Un aperçu du programme Mapper auquel je pense est quelque chose comme:
public class MergeOrderedFileMapper extends MapReduceBase implements Mapper<ArrayWritable, Text, ??, ??>
{
FileSystem fs;
public void map(ArrayWritable sourceFiles, Text destFile, OutputCollector<??, ??> output, Reporter reporter) throws IOException
{
//Convert the destFile to Path.
...
//make sure the parent directory of destFile is created first.
FSDataOutputStream destOS = fs.append(destFilePath);
//Convert the sourceFiles to Paths.
List<Path> srcPaths;
....
....
for(Path p: sourcePaths) {
FSDataInputStream srcIS = fs.open(p);
byte[] fileContent
srcIS.read(fileContent);
destOS.write(fileContent);
srcIS.close();
reporter.progress(); // Important, else mapper taks may timeout.
}
destOS.close();
// Delete source files.
for(Path p: sourcePaths) {
fs.delete(p, false);
reporter.progress();
}
}
}
J'ai écrit une implémentation pour PySpark car nous l'utilisons assez souvent.
Modélisé d'après copyMerge()
de Hadoop et utilise les mêmes API Hadoop de niveau inférieur pour y parvenir.
https://github.com/Tagar/abalon/blob/v2.3.3/abalon/spark/sparkutils.py#L335
Il conserve l'ordre alphabétique des noms de fichiers.