Vue d'ensemble
Je suis d'essayer d'améliorer la performance de nos requêtes de base de données de SQLAlchemy. Nous utilisons psycopg2. Dans notre système de production, nous sommes en choisissant d'aller à Java, car il est tout simplement plus rapide d'au moins 50%, si ce n'est plus près de 100%. Donc je suis en espérant que quelqu'un dans le Débordement de la Pile de la communauté a une manière d'améliorer mes performances.
Je pense que ma prochaine étape va être à la fin de la correction du psycopg2 de la bibliothèque à se comporter comme le pilote JDBC. Si c'est le cas et que quelqu'un l'a déjà fait, ce serait bien, mais je suis en espérant que j'ai encore des paramètres ou refactoring tweak je peux le faire à partir de Python.
Détails
J'ai un simple "SELECT * from someLargeDataSetTable" requête en cours d'exécution. Le dataset est Sgb dans la taille. Un rapide tableau des performances est comme suit:
Calendrier De Table
Dossiers | JDBC | SQLAlchemy[1] | SQLAlchemy[2] | Psql -------------------------------------------------------------------- 1 (4 ko) | 200 ms | 300 ms | 250ms | 10ms 10 (8 ko) | 200 ms | 300 ms | 250ms | 10ms 100 (88kB) | 200 ms | 300 ms | 250ms | 10ms De 1 000 (600 ko) | 300 ms | 300 ms | 370ms | 100ms De 10 000 (6 MO) | 800ms | 830ms | 730ms | 850ms De 100 000 (50 MO) | 4s | 5s | 4,6 s | 8s De 1 000 000 (510 MO) | 30 | 50 | 50 | 1m32s 10 000 000 de (5.1 GO) | 4m44s | 7m55s | 6m39s | n/a -------------------------------------------------------------------- De 5 000 000 (2,6 GO) | 2m30s | 4m45s | 3m52s | 14m22s -------------------------------------------------------------------- [1] - Avec la fonction processrow [2] - Sans le processrow fonction (copie directe)
Je pourrais ajouter de plus (nos données peuvent être autant que téraoctets), mais je pense que changer la pente est évident à partir des données. JDBC juste fonctionne nettement mieux que le jeu de données de taille augmente. Quelques notes...
Calendrier De Table Notes:
- Le datasizes sont approximatifs, mais ils devraient vous donner une idée de la quantité de données.
- Je suis en utilisant le "temps" de l'outil à partir d'un Linux en ligne de commande bash.
- Les temps sont à l'horloge murale fois (c'est à dire vrai).
- Je suis à l'aide de Python 2.6.6 et je suis en cours d'exécution avec
python -u
- Taille de l'extraction est de 10 000
- Je ne suis pas vraiment inquiet au sujet de la Psql le moment, il est là juste comme un point de référence. Je ne peut pas avoir correctement défini fetchsize pour elle.
- Je suis aussi vraiment pas inquiet sur le calendrier ci-dessous la taille de l'extraction de moins de 5 secondes est négligeable à ma demande.
- Java et Psql semblent prendre 1 go de mémoire de ressources; Python est plus de 100 MO (yay!!).
- Je suis en utilisant le [cdecimals] de la bibliothèque.
- J'ai remarqué un [article récent] discuter de quelque chose de semblable. Il semble que le pilote JDBC de conception est totalement différente de la psycopg2 conception (qui je pense est plutôt gênant compte tenu de la différence de performances).
- Mon cas d'utilisation est fondamentalement que j'ai pour exécuter un processus quotidien (avec environ 20 000 différentes étapes... de multiples requêtes) sur de très grands ensembles de données et j'ai une question très spécifique de la fenêtre de temps où je peux terminer ce processus. Le Java que nous utilisons n'est pas simplement JDBC, c'est un "smart" wrapper sur le dessus de la JDBC moteur... nous ne voulons pas utiliser Java et nous aimerions arrêter à l'aide de la fonction "smart" de la partie.
- Je suis l'aide de l'un de notre système de production de boîtes (base de données et backend processus) pour exécuter la requête. Donc, c'est notre meilleur des cas, le timing. Nous avons QA et boîtes de Dev qui s'exécutent beaucoup plus lent et le moment de la requête peut devenir important.
testSqlAlchemy.py
#!/usr/bin/env python # testSqlAlchemy.py import sys essayez: importation cdecimal sys.modules["décimal"]=cdecimal sauf ImportError,e: impression > > > sys.stderr, "Erreur: cdecimal ne se charge pas correctement." soulever SystemExit de sqlalchemy importation create_engine de sqlalchemy.orm importation sessionmaker def processrow (ligne,delimiter="|",null="\N"): newrow = [] pour x en ligne: si x n'est: x = null newrow.append(str(x)) retour délimiteur.join(newrow) fetchsize = 10000 connectionString = "postgresql+psycopg2://usr:pass@server:port/db" fra = create_engine(connectionString, server_side_cursors=True) session = sessionmaker(bind=fra)() avec open("test.sql","r") comme queryFD: avec open("/dev/null","w") comme nullDev: query = session.execute(queryFD.read()) cur = requête.curseur tandis que cur.statusmessage pas dans ['FETCH 0','FERMER le CURSEUR']: pour la ligne de requête.fetchmany(fetchsize): impression > > > nullDev, processrow(ligne)
Après la synchronisation, j'ai aussi couru un cProfile et c'est le cliché des pires délinquants:
Synchronisation de Profil (avec processrow)
Ven Mar 4 13:49:45 2011 sqlAlchemy.prof 415757706 appels de fonction (415756424 primitive appels) en 563.923 CPU secondes Commandé par: temps cumulé ncalls tottime percall cumtime percall nom de fichier:lineno(fonction) 1 0.001 0.001 563.924 563.924 {execfile} 1 25.151 25.151 563.924 563.924 testSqlAlchemy.py:2() 1001 0.050 0.000 329.285 0.329 de base.py:2679(fetchmany) 1001 5.503 0.005 314.665 0.314 de base.py:2804(_fetchmany_impl) 10000003 4.328 0.000 307.843 0.000 base.py:2795(_fetchone_impl) 10011 0.309 0.000 302.743 0.030 de base.py:2790(__tampon_lignes) 10011 233.620 0.023 302.425 0.030 {méthode 'fetchmany' de 'psycopg2._psycopg.curseur' objets} 10000000 145.459 0.000 209.147 0.000 testSqlAlchemy.py:13(processrow)
Synchronisation de Profil (sans processrow)
Ven Mar 4 14:03:06 2011 sqlAlchemy.prof 305460312 appels de fonction (305459030 primitive appels) en 536.368 CPU secondes Commandé par: temps cumulé ncalls tottime percall cumtime percall nom de fichier:lineno(fonction) 1 0.001 0.001 536.370 536.370 {execfile} 1 29.503 29.503 536.369 536.369 testSqlAlchemy.py:2() 1001 0.066 0.000 333.806 0.333 de base.py:2679(fetchmany) 1001 5.444 0.005 318.462 0.318 de base.py:2804(_fetchmany_impl) 10000003 4.389 0.000 311.647 0.000 base.py:2795(_fetchone_impl) 10011 0.339 0.000 306.452 0.031 de base.py:2790(__tampon_lignes) 10011 235.664 de 0,024 306.102 0.031 {méthode 'fetchmany' de 'psycopg2._psycopg.curseur' objets} 10000000 32.904 0.000 172.802 0.000 base.py:2246(__repr__)
Derniers Commentaires
Malheureusement, la processrow fonction des besoins de séjour, sauf si il y a un chemin à l'intérieur de SQLAlchemy pour spécifier la valeur null = 'userDefinedValueOrString" et delimiter = 'userDefinedValueOrString' de la sortie. Le Java que nous utilisons actuellement déjà fait cela, donc la comparaison (avec processrow) nécessaires pour être des pommes avec des pommes. Si il y a un moyen pour améliorer les performances de processrow ou SQLAlchemy pur Python ou les paramètres de tweak, je suis très intéressé.