web-dev-qa-db-fra.com

Java - Traitement simultané d'un gros fichier

Donc, à un niveau élevé, mon cas d'utilisation est le suivant -

Je reçois périodiquement (toutes les 24 heures) un très gros fichier (la taille peut varier de Mo à 10 s de Go) que je dois traiter dans les 24 heures. Le traitement implique la lecture d'un enregistrement, l'application d'une logique métier et la mise à jour d'une base de données avec l'enregistrement.

Solution actuelle est une version à thread unique qui

  1. lit initialement le fichier entier en mémoire, c'est-à-dire qu'il lit chaque ligne et construit un POJO. Donc, essentiellement, cela crée une grande liste
  2. Il itère ensuite sur la liste et applique la logique métier sur chaque Pojo et les enregistre dans la base de données

Cela fonctionne pour les petits fichiers avec moins de 10 millions d'enregistrements. Mais à mesure que les systèmes évoluent, nous obtenons plus de charge, c'est-à-dire des fichiers plus volumineux (avec> 100 millions d'enregistrements occasionnellement). Dans ce scénario, nous voyons des délais d'attente, c'est-à-dire que nous ne pouvons pas traiter l'intégralité du fichier dans les 24 heures.

Je prévois donc d'ajouter de la concurrence ici.

Une solution simple serait-

  1. Lire l'intégralité du fichier en mémoire (créer des POJO pour chaque enregistrement, comme nous le faisons actuellement) ou lire chaque enregistrement un par un et créer POJO
  2. Générez des threads pour traiter simultanément ces POJO.

Cette solution semble simple, le seul inconvénient que je vois est que l'analyse de fichiers peut prendre du temps car elle est monothread (la RAM n'est pas un problème, j'utilise une assez grande instance EC2).

Une autre solution serait de -

  1. Décomposer le fichier en plusieurs sous-fichiers
  2. Traitez chaque fichier en parallèle

Cela semble légèrement compliqué car je devrais diviser le fichier en plusieurs fichiers plus petits.

Toute contribution sur les suggestions ici sur les approches serait la bienvenue.

8
AgentX

La façon la plus efficace de le faire est:

  • Avoir un seul thread qui lit le fichier d'entrée. Les disques durs sont à leur maximum lors de la lecture séquentielle.
  • Ne pas le lire en mémoire d'un seul coup! C'est un énorme gaspillage de mémoire qui pourrait être utilisé beaucoup mieux pour accélérer le traitement!
  • Au lieu de cela, demandez à ce thread unique de lire un ensemble d'entrées (peut-être 100, peut-être 1000, il s'agit d'un paramètre de réglage) à la fois et de les soumettre à un thread pour le traiter. Si chaque ligne représente un enregistrement, le thread de lecture peut reporter toute l'analyse (autre que la recherche de nouvelles lignes) aux threads de traitement. Mais même si ce n'est pas le cas, il est très peu probable que l'analyse des enregistrements soit votre goulot d'étranglement.
  • Faites la gestion des threads via un pool de threads de taille fixe , choisissez la taille comme étant le nombre de cœurs CPU sur la machine, ou peut-être un peu plus.
  • Si votre base de données est une base de données SQL, assurez-vous que les threads individuels accèdent à la base de données via un pool de connexions et effectuent toutes leurs mises à jour de base de données pour un ensemble d'entrées en une seule transaction et en utilisant des insertions par lots.

Vous voudrez peut-être utiliser Spring Batch pour cela, car cela vous guidera vers la bonne chose. Mais il est quelque peu sur-conçu et difficile à utiliser.

Gardez à l'esprit que tout cela pourrait encore être inutile si la base de données devient votre goulot d'étranglement, ce qui peut très facilement être le cas - les bases de données SQL sont notoirement mauvaises pour gérer les mises à jour simultanées, et cela peut nécessiter un certain nombre de réglages pour éviter les conflits de verrous et impasses.

15
Michael Borgwardt

Commençons par une arithmétique de base.

(* 24 60 60)
86400

Cela signifie qu'il y a 86400 secondes en une journée.

(/ 100e6 86400)
1157.4074074074074

Cela signifie que pour traiter 100 millions d'enregistrements en une journée, vous devez pouvoir traiter 1157,4 enregistrements par seconde.

Aller plus loin:

(/ 1.0 1157.4074074074074)
0.000864

Cela signifie que vous devez pouvoir traiter un enregistrement, de bout en bout, en 864 microsecondes.

Peu importe ce que vous faites, c'est une vérité fondamentale. S'il faut plus de 864 microsecondes pour traiter un enregistrement complètement, vous ne pourrez pas traiter 100 millions d'enregistrements en 24 heures.

L'ajout de "threading" ne fera qu'empirer, pas améliorer, car vous ajoutez des frais généraux et ne supprimez aucune charge de travail sous-jacente.

Je soupçonne, après près de 40 ans dans cette raquette folle, que lire le fichier en mémoire et écrire les résultats dans votre SGBD vous mange vivant.

7
John R. Strohm