web-dev-qa-db-fra.com

Trier un fichier avec un énorme volume de données en fonction des contraintes de mémoire

Points: 

  • Nous traitons simultanément des milliers de fichiers plats par jour. 
  • La contrainte de mémoire est un problème majeur. 
  • Nous utilisons thread pour chaque processus de fichier.
  • Nous ne trions pas par colonnes. Chaque ligne (enregistrement) du fichier est traitée comme une colonne.

Ne peut pas faire:

  • Nous ne pouvons pas utiliser les commandes de tri de unix/linux.
  • Nous ne pouvons utiliser aucun système de base de données, aussi léger soit-il.

Maintenant, nous ne pouvons pas simplement charger tout le contenu d'une collection et utiliser le mécanisme de tri. Il va manger toute la mémoire et le programme va avoir une erreur de tas.

Dans ce cas, comment trieriez-vous les enregistrements/lignes d'un fichier?

28
Erika Gomez

Il semble que ce que vous recherchez soit tri externe .

Fondamentalement, vous triez d’abord de petites quantités de données, vous les écrivez sur le disque, puis vous les parcourez pour les trier.

43
phisch

Vous pouvez lire les fichiers en petites parties, les trier et les écrire dans des fichiers temporaires. Ensuite, vous lisez à nouveau deux d'entre eux de manière séquentielle et les fusionnez dans un fichier temporaire plus volumineux, etc. S'il ne reste qu'un seul fichier, votre fichier est trié. En gros, c'est l'algorithme Megresort exécuté sur des fichiers externes. Il s'adapte assez bien avec de gros fichiers, mais génère des E/S de fichier supplémentaires.

Éditer: Si vous avez des connaissances sur la variance probable des lignes dans vos fichiers, vous pouvez utiliser un algorithme plus efficace (type de distribution). Simplifié, vous liriez le fichier d'origine une fois et écririez chaque ligne dans un fichier temporaire prenant uniquement des lignes avec le même premier caractère (ou une certaine plage de premiers caractères). Ensuite, vous parcourez tous les fichiers temporaires (désormais petits) par ordre croissant, vous les triez en mémoire et vous les ajoutez directement au fichier de sortie. Si un fichier temporaire s'avère trop volumineux pour le tri en mémoire, vous pouvez répéter le même processus en fonction du deuxième caractère dans les lignes, etc. Ainsi, si votre premier partitionnement était assez bon pour produire des fichiers assez petits, vous ne subirez que 100% de surcharge I/O, quelle que soit la taille du fichier, mais dans le pire des cas, il peut devenir bien plus qu'avec le tri par fusion stable.

10
x4u

Malgré votre restriction, j'utiliserais une base de données intégrée SQLITE3 . Comme vous, je travaille chaque semaine avec 10 à 15 millions de lignes de fichier à plat et il est très très rapide d’importer et de générer des données triées. Par exemple: Une fois que vous avez téléchargé le fichier .exe, dans une invite de commande, vous pouvez procéder comme suit:

C:> sqlite3.exe dbLines.db
sqlite> create table tabLines(line varchar(5000));
sqlite> create index idx1 on tabLines(line);
sqlite> .separator '\r\n'
sqlite> .import 'FileToImport' TabLines

puis:

sqlite> select * from tabLines order by line;

or save to a file:
sqlite> .output out.txt
sqlite> select * from tabLines order by line;
sqlite> .output stdout
9
Eduardo

Je voudrais faire tourner un cluster EC2 et exécuter MergeSort de Hadoop.

Edit: vous ne savez pas combien de détails vous souhaitez ni sur quoi. EC2 est le logiciel Elastic Compute Cloud d'Amazon. Il vous permet de louer des serveurs virtuels à l'heure, à faible coût. Voici leur site web .

Hadoop est un framework Open Source MapReduce conçu pour le traitement parallèle de grands ensembles de données. Un travail est un bon candidat pour MapReduce lorsqu'il peut être divisé en sous-ensembles pouvant être traités individuellement puis fusionnés, généralement en effectuant un tri sur les clés (stratégie de division et de conquête). Voici son site web .

Comme mentionné par les autres affiches, le tri externe est également une bonne stratégie. Je pense que la façon dont je choisirais entre les deux dépend de la taille des données et de la vitesse requise. Un seul ordinateur sera probablement limité au traitement d'un seul fichier à la fois (car vous allez utiliser la mémoire disponible). Examinez donc quelque chose comme EC2 uniquement si vous devez traiter des fichiers plus rapidement que cela.

8
danben

Comme mentionné ci-dessus, vous pouvez procéder par étapes.
Je voudrais expliquer cela avec mes propres mots (diffère sur le point 3):

  1. Lire le fichier séquentiellement, traiter N enregistrements à la fois en mémoire (N est arbitraire, en fonction de votre contrainte de mémoire et du nombre T de fichiers temporaires que vous souhaitez).

  2. Triez les N enregistrements en mémoire, écrivez-les dans un fichier temporaire. Boucle sur T jusqu'à ce que vous avez terminé.

  3. Ouvre tous les fichiers temporaires T en même temps, mais ne lit qu'un seul enregistrement par fichier. (Bien sûr, avec des tampons). Pour chacun de ces enregistrements T, recherchez le plus petit, écrivez-le dans le fichier final et avancez uniquement dans ce fichier.


Avantages:

  • La consommation de mémoire est aussi faible que vous le souhaitez.
  • Vous ne faites que le double des accès disque en le comparant à une stratégie tout en mémoire. Pas mal! :-)

Exemple avec des chiffres:

  1. Fichier original avec 1 million d'enregistrements.
  2. Choisissez d’avoir 100 fichiers temporaires. Lisez et triez 10 000 enregistrements à la fois, puis déposez-les dans leur propre fichier temporaire.
  3. Ouvrez le fichier 100 temp à la fois, lisez le premier enregistrement en mémoire.
  4. Comparez les premiers enregistrements, écrivez le plus petit et avancez ce fichier temporaire.
  5. Boucle sur l'étape 5, un million de fois.

ÉDITÉ

Vous avez parlé d'une application multithread, alors je me demande ...

Comme nous l’avons vu lors de ces discussions sur ce besoin, utiliser moins de mémoire donne moins de performance, avec un facteur dramatique dans ce cas. Je pourrais donc aussi suggérer à utilisez un seul thread de ne traiter qu'un tri à la fois, et non comme une application multithread.

Si vous traitez dix threads, chacun avec un dixième de la mémoire disponible, votre performance sera misérable, beaucoup moins qu'un dixième du temps initial. Si vous utilisez un seul thread, mettez en file d'attente les 9 autres demandes et les traitez à leur tour, vos performances globales seront bien meilleures. Vous terminerez les dix tâches beaucoup plus rapidement.


Après avoir lu cette réponse: Triez un fichier avec un énorme volume de données en fonction des contraintes de mémoire Je vous suggère d’envisager ce tri par répartition. Cela pourrait être un gain énorme dans votre contexte.

L'amélioration par rapport à ma proposition est qu'il n'est pas nécessaire d'ouvrir tous les fichiers temporaires à la fois, vous n'en ouvrez qu'un seul. Ça sauve ta journée! :-)

6
KLE

Vous pouvez utiliser la stratégie de division et de conquête suivante:

Créez une fonction H() qui peut attribuer un numéro à chaque enregistrement du fichier d'entrée. Pour un enregistrement r2 qui sera trié derrière un enregistrement r1, il doit renvoyer un nombre plus grand pour r2 que pour r1. Utilisez cette fonction pour partitionner tous les enregistrements dans des fichiers distincts qui tiendront dans la mémoire afin que vous puissiez les trier. Une fois que vous avez fait cela, vous pouvez simplement concaténer les fichiers triés pour obtenir un gros fichier trié.

Supposons que vous ayez ce fichier d’entrée où chaque ligne représente un enregistrement

Alan Smith
Jon Doe
Bill Murray
Johnny Cash

Construisons simplement H() afin qu’il utilise la première lettre de l’enregistrement afin d’obtenir jusqu’à 26 fichiers. Dans cet exemple, vous obtiendrez simplement 3:

<file1>
Alan Smith

<file2>
Bill Murray

<file10>
Jon Doe
Johnny Cash

Maintenant, vous pouvez trier chaque fichier individuel. Qui échangerait "Jon Doe" et "Johnny Cash" dans <file10>. Maintenant, si vous venez de concaténer les 3 fichiers, vous aurez une version triée de l’entrée.

Notez que vous divisez d'abord et que vous triez (triez) plus tard. Cependant, vous vous assurez de faire le partitionnement de manière à ce que les parties résultantes que vous devez trier ne se chevauchent pas, ce qui facilitera la fusion du résultat. 

La méthode par laquelle vous implémentez la fonction de partitionnement H() dépend beaucoup de la nature de vos données d'entrée. Une fois que vous avez cette partie compris le reste devrait être un jeu d'enfant.

2
VoidPointer

Si votre restriction consiste uniquement à ne pas utiliser un système de base de données external, vous pouvez essayer une base de données intégrée (par exemple, Apache Derby ). De cette façon, vous bénéficiez de tous les avantages d'une base de données sans aucune dépendance d'infrastructure externe.

2
FRotthowe

Vous pouvez utiliser la base de données de fichiers SQL Lite, charger les données dans la base de données, puis le laisser trier et renvoyer les résultats pour vous . espace disque, traitement plus lent . https://sites.google.com/site/arjunwebworld/Home/programming/sorting-large-data-files

0
user2071703

Voici un moyen de le faire sans utiliser lourdement le tri en Java côté et sans utiliser DB . Hypothèses: vous disposez de 1 To d'espace et les fichiers contiennent ou commencent par un numéro unique,

Divisez les fichiers N fois.

Lire ces N fichiers un par un et créer un fichier pour chaque ligne/numéro

Nommez ce fichier avec le numéro correspondant. Tout en nommant, maintenez un compteur à jour pour stocker le plus petit nombre.

Maintenant, vous pouvez déjà avoir le dossier racine des fichiers marqués pour le tri par nom ou mettre votre programme en pause pour vous donner l'heure de déclencher une commande sur votre système d'exploitation pour trier les fichiers par noms. Vous pouvez aussi le faire par programme. 

Maintenant vous avez un dossier avec les fichiers triés avec leur nom, en utilisant le compteur, commencez à prendre chaque fichier un par un, insérez des numéros dans votre fichier OUTPUT, fermez-le.

Lorsque vous avez terminé, vous aurez un fichier volumineux avec des nombres triés.

0
MayurRB

Vous pouvez le faire avec seulement deux fichiers temporaires - source et destination - et avec aussi peu de mémoire que vous le souhaitez . À la première étape, votre source est le fichier d'origine, à la dernière étape, le fichier de résultat.

A chaque itération:

  • lire du fichier source dans un tampon glissant un bloc de données de la moitié de la taille du tampon; 
  • trier le tampon entier
  • écrire dans le fichier de destination la première moitié du tampon. 
  • décale la seconde moitié du tampon au début et répète 

Conservez un indicateur booléen indiquant si vous devez déplacer certains enregistrements de l'itération en cours . Si l'indicateur reste faux, votre fichier est trié . S'il est déclenché, répétez le processus en utilisant le fichier de destination comme source.

Nombre maximum d'itérations: (taille du fichier)/(taille du tampon) * 2

0
user7932299

Je sais que vous avez mentionné ne pas utiliser de base de données, peu importe sa luminosité ... alors, peut-être que ce n'est pas une option. Mais que dire de hsqldb en mémoire ... soumettez-le, triez-le par requête, purgez-le. Juste une pensée.

0
PaulP1975