6 votes

gérer de nombreux et énormes fichiers journaux avec python

J'utilise des scripts python scripts pour faire des statistiques. Une sorte de contenu de logs est comme ceci, je l'appelle A logs : chaque A logs a le format suivant :

[2012-09-12 12:23:33] SOME_UNIQ_ID filesize

un autre journal que j'appelle le journal B a le format suivant :

[2012-09-12 12:24:00] SOME_UNIQ_ID

J'ai besoin de compter combien d'enregistrements dans les journaux A sont également dans les journaux B, et d'obtenir l'écart de temps entre les deux enregistrements avec le même identifiant.Mon implémentation était de charger tous les temps et identifiants des journaux B dans une carte, puis d'itérer les journaux A pour vérifier si l'identifiant existait dans la carte.Le problème est que cela utilise trop de mémoire car j'ai presque 100 millions d'enregistrements dans les journaux B.Une suggestion pour améliorer les performances et l'utilisation de la mémoire ? Je vous remercie.

3voto

Jon Clements Points 51556

Vous pouvez essayer d'inverser la recherche en fonction de la place de "A" dans la mémoire et de balayer séquentiellement "B".

Sinon, chargez les fichiers journaux dans une base de données SQLite3 avec deux tables (log_a, log_b) contenant (timestamp, uniq_id, rest_of_line), puis exécutez une jointure SQL sur uniq_id et procédez à tous les traitements nécessaires sur la base des résultats obtenus. Cette méthode permet de maintenir une faible surcharge de mémoire, permet au moteur SQL d'effectuer la jointure, mais nécessite bien sûr de dupliquer les fichiers journaux sur le disque (mais ce n'est généralement pas un problème sur la plupart des systèmes).

exemple

import sqlite3
from datetime import datetime

db = sqlite3.connect(':memory:')

db.execute('create table log_a (timestamp, uniq_id, filesize)')
a = ['[2012-09-12 12:23:33] SOME_UNIQ_ID filesize']
for line in a:
    timestamp, uniq_id, filesize = line.rsplit(' ', 2)
    db.execute('insert into log_a values(?, ?, ?)', (timestamp, uniq_id, filesize))
db.commit()

db.execute('create table log_b (timestamp, uniq_id)')
b = ['[2012-09-12 13:23:33] SOME_UNIQ_ID']
for line in b:
    timestamp, uniq_id = line.rsplit(' ', 1)
    db.execute('insert into log_b values(?, ?)', (timestamp, uniq_id))
db.commit()

TIME_FORMAT = '[%Y-%m-%d %H:%M:%S]'
for matches in db.execute('select * from log_a join log_b using (uniq_id)'):
    log_a_ts = datetime.strptime(matches[0], TIME_FORMAT)
    log_b_ts = datetime.strptime(matches[3], TIME_FORMAT)
    print matches[1], 'has a difference of', abs(log_a_ts - log_b_ts)
    # 'SOME_UNIQ_ID has a difference of 1:00:00'
    # '1:00:00' == datetime.timedelta(0, 3600)

Notez que :

  • les .connect sur sqlite3 devrait être un nom de fichier
  • a y b devraient être vos fichiers

1voto

Geordee Naliyath Points 853

Essayez ceci :

  • Tri externe des deux fichiers
  • Lire le fichier A Logs et enregistrer SOME_UNIQ_ID (A)
  • Lire le fichier B Logs et enregistrer SOME_UNIQ_ID (B)
  • Comparer l'identifiant SOME_UNIQ_ID (B) avec l'identifiant SOME_UNIQ_ID (A)
    • Si c'est moins, relisez le fichier B Logs.
    • S'il est supérieur, relisez le fichier A Logs et comparez avec l'identifiant SOME_UNIQ_ID (B) sauvegardé.
    • S'il est égal, trouver l'écart de temps

En supposant que le tri externe fonctionne efficacement, vous finissez le processus en lisant les deux fichiers une seule fois.

0voto

lukas Points 7789

Tout d'abord, quel est le format de la carte d'identité ? Est-il unique au niveau mondial ?

Je choisirais l'une de ces trois options.

  • Utiliser la base de données
  • Union de deux ensembles d'identifiants
  • Outils Unix

Je suppose que vous préférez une deuxième option. Chargez uniquement les identifiants de A et B. En supposant que l'identifiant tienne dans un entier de 32 bits, l'utilisation de la mémoire sera inférieure à 1 Go. Chargez ensuite les dates des mêmes identifiants et calculez l'écart. La première option est celle qui répond le mieux aux besoins.

0voto

Hans Then Points 5570

Si l'identifiant unique peut être trié (par exemple, par ordre alphabétique ou numérique), vous pouvez regrouper les comparaisons.

Supposons que, dans notre exemple, l'identifiant soit numérique et qu'il soit compris entre 1 et 10^7. Vous pourriez alors placer les 10^6 premiers éléments dans votre table de hachage, puis effectuer un balayage séquentiel dans le second fichier pour trouver les enregistrements correspondants.

En pseudopython, je n'ai pas testé cela :

for i in xrange(0,9):
    for line in file1:
        time, id = line.split(']')
        id = int(id)
        if i * 10**6 < id < (i+1) * 10**6:
            hash_table[id] = time

    for line in file2:
        time, id = line.split(']') # needs a second split to get the id
        id = int(id)
        if id in hashtable:
            # compare timestamps

Si les ID ne sont pas numériques, vous pouvez créer des lots à l'aide d'une clé alphabétique :

if id.startswith(a_letter_from_the_alphabet):
    hash_table[id] = time

0voto

rpbear Points 393

Comme le goulot d'étranglement est la traduction des horodatages, j'ai divisé cette action en plusieurs machines isolées qui génèrent des logs A et des logs B. Ces machines traduisent les horodatages en temps d'époque, et la machine CENTER qui utilise tous ces logs pour calculer mon résultat prend maintenant presque 1/20 de temps par rapport à la méthode d'origine.

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