2011-12-13 3 views
2

Le problème est le suivant:Comment extraire efficacement le nombre de chaque élément d'un grand ensemble de données?

  • Entrée: Tous les articles de Wikipedia (33GB de texte)
  • Sortie: Nombre de chaque skipgram mots (n-grammes avec skips maximum de k) de Wikipedia dans le fichier SQLite.

schéma de la table de sortie est:

CREATE TABLE [tokens] ([token] TEXT UNIQUE NOT NULL PRIMARY KEY, [count] INTEGER NOT NULL 

L'approche naïve est que pour chaque skipgram nous créons un nouveau record dans le compteur de table ou incrément dans le dossier existant:

INSERT OR REPLACE INTO [tokens] VALUES (@token, COALESCE((SELECT count FROM [tokens] WHERE [email protected]), 0) + 1) 

Le problème avec cette approche est que l'index est constamment mis à jour et lorsque la base de données passe à plusieurs giga, ces mises à jour sont très lentes. Nous pouvons résoudre cela en créant la table "jetons" sans index et en ajoutant l'index à la fin du traitement.

Le problème est que l'instruction select SELECT count FROM [tokens] WHERE [email protected] qui doit analyser la table réduit considérablement les performances.

La meilleure méthode que je l'ai trouvé à ce jour est la suite (j'utilise C#):

  1. Créer une Dictionary<string,int> pour compter les jetons.

  2. Ajoutez des jetons à ce dictionnaire jusqu'à ce qu'il devienne trop grand pour tenir dans la RAM.

  3. Insérer (ne pas mettre à jour) les jetons du dictionnaire pour une table temporaire sans index. Le tableau a suivant le schéma:

    CREATE TABLE [temp] ([token] TEXT, [count] INTEGER) 
    
  4. S'il y a plus de jetons, désactivez le dictionnaire et passez à l'étape 2.

  5. jetons de copie de table temporaire à la table des jetons:

    INSERT INTO [tokens] SELECT [token], SUM([count]) AS [count] FROM [temp] GROUP BY [token] 
    

Cette méthode prend seulement "24 heures" pour traiter l'ensemble de données, mais je crois que ce n'est pas la meilleure approche parce que l'étape 5 prend 22 heures sur 24.

Connaissez-vous une approche alternative qui peut résoudre ce problème?

P.S. Mon application est à un seul thread et je fais les insertions ci-dessus en lots (100000 par lot) dans la transaction.

+0

Votre application est-elle multithread? –

Répondre

0

Je suggère d'ajouter SET TRANSACTION ISOLATION READ UNCOMMITTED. Cela signifie qu'il est possible que les comptes soient légèrement décalés, en particulier dans un environnement threadé où plusieurs essayent d'insérer/de mettre à jour en même temps.

+0

Pouvez-vous expliquer pourquoi cela améliore les performances? – user1096250

+0

Désolé pour MS SQL Je n'ai pas remarqué que vous utilisiez SQLITE –

1

Je suggère de créer une autre table avec la même définition, de remplir la table dans un certain état, de fusionner les résultats à la table principale, de purger la table et de commencer à traiter le prochain ensemble d'éléments.

+0

J'ai essayé votre suggestion, mais elle ne résout pas complètement le problème de mise à jour lente de l'index qui rend ces mises à jour moins fréquentes tandis que chaque mise à jour prend plus de temps. Après quelques fusions, les mises à jour deviennent relativement lentes (dix minutes pour fusionner 10 millions d'enregistrements). Et mon ensemble de données contient des milliards de jetons. – user1096250

+0

@ user1096250, je voudrais également regarder le tuning de fichiers temporaires sqlite (http://sqlite.org/tempfiles.html) – newtover

0

Si vous avez beaucoup de concerts à ...

Je vous suggère de ne pas compter les jetons au fur et à mesure, mais plutôt d'ajouter tous les jetons dans une seule table et de créer un index qui organise les jetons.

CREATE TABLE tokens (token TEXT); 
CREATE INDEX tokens_token ON tokens (token ASC); 

puis ajoutez tous jeton un à la fois ...

INSERT INTO tokens VALUES ('Global Warming'); 
INSERT INTO tokens VALUES ('Global Cooling'); 

execute enfin un SELECT ... GROUP BY

SELECT token, COUNT(0) token_count FROM tokens GROUP BY token 
+0

Merci pour suggestion, je suis actuellement utiliser une approche similaire. Je l'ai ajouté à ma question initiale. Pensez-vous que l'index sur ma table temporaire peut être utile? – user1096250

0

Cela sonne comme un bon endroit pour utiliser un « comptage fleurs filtre "pour moi.

Il faudrait deux passages sur vos données, et c'est un peu heuristique, mais cela devrait être rapide. Les filtres Bloom permettent des tests d'insertion et de présence en temps constant. Un filtre de bloom de comptage compte combien d'une valeur particulière a été trouvée, par opposition au filtre de bloom habituel qui ne garde que la trace de la présence/absence.

+0

Je ne peux pas utiliser un "filtre de bloom à comptage" parce que j'ai besoin de la possibilité d'interroger cette table, par exemple. "sélectionnez * des jetons où compte> 1000". – user1096250

Questions connexes