2010-08-19 31 views
13

Je stocke une table en utilisant python et j'ai besoin de persistance. Essentiellement, je stocke la table comme une chaîne de dictionnaire à des nombres. Et le tout est stocké avec shelveL'étalement est trop lent pour les grands dictionnaires, que puis-je faire pour améliorer les performances?

self.DB=shelve.open("%s%sMoleculeLibrary.shelve"%(directory,os.sep),writeback=True) 

J'utilise writeback-True comme je l'ai trouvé le système a tendance à être instable si je ne le fais pas. Après les calculs, le système doit fermer la base de données et la stocker. Maintenant, la base de données (la table) est d'environ 540 Mo, et cela prend des années. Le temps a explosé après que la table ait atteint 500MB. Mais j'ai besoin d'une table beaucoup plus grande. En fait j'ai besoin de deux d'entre eux. Je n'utilise probablement pas la mauvaise forme de persistance. Que puis-je faire pour améliorer les performances?

+0

Avez-vous rencontré une utilisation du processeur inadéquate avec de grands dicts en utilisant shelve? –

Répondre

13

Pour stocker un grand dictionnaire de string : number paires clé-valeur, je suggérerais une solution de stockage natif JSON telle que MongoDB. Il a une API merveilleuse pour Python, Pymongo. MongoDB lui-même est léger et incroyablement rapide, et les objets json seront nativement des dictionnaires en Python. Cela signifie que vous pouvez utiliser votre clé string comme ID d'objet, ce qui permet un stockage compressé et une recherche rapide.

À titre d'exemple de la facilité avec laquelle le code serait, consultez les rubriques suivantes:

d = {'string1' : 1, 'string2' : 2, 'string3' : 3} 
from pymongo import Connection 
conn = Connection() 
db = conn['example-database'] 
collection = db['example-collection'] 
for string, num in d.items(): 
    collection.save({'_id' : string, 'value' : num}) 
# testing 
newD = {} 
for obj in collection.find(): 
    newD[obj['_id']] = obj['value'] 
print newD 
# output is: {u'string2': 2, u'string3': 3, u'string1': 1} 

Vous auriez juste pour reconvertir de unicode, ce qui est trivial.

+0

Merci. Les données sont en fait un numéro de table symétrique * number -> number, mais étant donné que shelve voulait que les chaînes soient des clés, j'étais induit en l'écrivant comme une chaîne -> number, où la chaîne est "a_b" avec a et b nombres et un

1

Combien plus grande? Quels sont les modèles d'accès? Quels types de calculs avez-vous besoin de faire? Gardez à l'esprit que vous allez avoir des limites de performance si vous ne pouvez pas garder la table en mémoire, peu importe comment vous le faites.

Vous pouvez vouloir aller à SQLAlchemy, ou directement en utilisant quelque chose comme bsddb, mais les deux sacrifieront la simplicité du code. Toutefois, avec SQL, vous pouvez peut-être décharger une partie du travail sur la couche de base de données en fonction de la charge de travail.

+2

Je développe un algorithme théorique, donc le problème que j'utilise maintenant a probablement des tables de quelques giga. Mais comme les gens vont utiliser l'algorithme pour d'autres problèmes (principalement en biologie des systèmes, penser grand, puis augmenter), il est important de trouver une solution qui peut évoluer. L'accès est aléatoire, et chaque terme sera consulté plusieurs fois. Le seul calcul que je dois faire est d'obtenir la valeur, de calculer la valeur si elle n'est pas là, et de la stocker. J'envisageais d'utiliser MySQL pour que la BD ne soit pas en mémoire. Mais cela rendrait le code plus complexe et plus lent. Merci. –

9

Sur la base de mon expérience, je vous recommande d'utiliser SQLite3, qui est livré avec Python. Cela fonctionne bien avec des bases de données plus grandes et des nombres clés. Des millions de clés et de gigaoctets de données ne posent aucun problème. Shelve est totalement gaspillé à ce moment-là. Avoir un processus db séparé n'est pas bénéfique, cela nécessite simplement plus de swaps de contexte. Dans mes tests, j'ai découvert que SQLite3 était l'option préférée à utiliser, lors de la manipulation de grands ensembles de données localement. L'exécution d'un moteur de base de données local comme mongo, mysql ou postgresql ne fournit aucune valeur supplémentaire et a également été plus lente.

0

Je pense que votre problème est dû au fait que vous utilisez le writeback=True. Le documentation dit (souligné dans l'original):

En raison de la sémantique Python, une étagère ne peut pas savoir quand un mutable entrée persistant dictionnaire est modifié. Par défaut, les objets modifiés sont écrits uniquement lorsqu'ils sont affectés à l'étagère (voir Exemple).Si le paramètre d'écriture différée est défini sur True, toutes les entrées accédées sont également également mises en mémoire cache et réécrites sur sync() et close(); ce peut rendre plus facile à muter des entrées mutables dans le dictionnaire persistant , mais, si de nombreuses entrées sont consultées, il peut consommer de grandes quantités de mémoire pour le cache, et il peut rendre l'opération de fermeture très lente puisque tous ont accédé les entrées sont réécrites (il n'y a aucun moyen de déterminer quelles entrées accédées sont mutables, ni lesquelles ont réellement muté).

Vous pouvez éviter d'utiliser writeback=True et assurez-vous que les données sont écrites qu'une seule fois (vous devez faire attention que les modifications suivantes vont être perdues).

Si vous pensez que ce n'est pas la bonne option de stockage (difficile à dire sans savoir comment les données sont structurées), je suggère sqlite3, il est intégré en python et donc très performant. C'est un peu plus compliqué qu'un simple magasin de clé-valeur.

Voir d'autres réponses pour des alternatives.