2012-11-20 6 views
1

Je n'arrive pas à comprendre pourquoi une simple requête SELECT prend autant de temps avec sqlalchemy en utilisant le SQL brut (j'obtiens 14600 lignes/sec, mais en exécutant la même requête via psycopg2 sans sqlalchemy, je ' m obtenant 38421 lignes/sec). Après quelques recherches, j'ai réalisé que basculer le paramètre use_native_unicode de sqlalchemy dans l'appel create_engine fait une énorme différence.Pourquoi SQLAlchemy avec psycopg2 use_native_unicode a de mauvaises performances?

Cette requête prend 0.5secs pour récupérer 7300 lignes:

from sqlalchemy import create_engine 

engine = create_engine("postgresql+psycopg2://localhost...", 
         use_native_unicode=True) 
r = engine.execute("SELECT * FROM logtable") 
fetched_results = r.fetchall() 

Cette requête prend 0.19secs pour récupérer les mêmes lignes: 7300

engine = create_engine("postgresql+psycopg2://localhost...", 
         use_native_unicode=False) 
r = engine.execute("SELECT * FROM logtable") 
fetched_results = r.fetchall() 

La seule différence entre les 2 requêtes est use_native_unicode. Mais les docs de sqlalchemy indiquent qu'il vaut mieux garder use_native_unicode = True (http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html).

Est-ce que quelqu'un sait pourquoi use_native_unicode fait une telle différence de performance? Et quelles sont les conséquences de la désactivation de use_native_unicode?

Répondre

2

ce problème dépend de la quantité de données non-ASCII que vous traitez. La méthode de décodage d'unicode de psycopg2 est plus rapide que celle de SQLAlchemy, en supposant que les extensions C de SQLA ne sont pas utilisées, mais ajoute encore de la latence pour un ensemble de résultats par rapport à ne pas effectuer de conversion unicode. Dans le code ci-dessus, les fonctionnalités Unicode de SQLAlchemy ne sont pas utilisées; elles sont uniquement utilisées lorsqu'une colonne est mappée sur les types Unicode ou String, ce qui ne peut se produire que si vous utilisez text(), select() ou un équivalent ORM, où un type Unicode est mappé à ces colonnes de jeu de résultats en utilisant les métadonnées Table le paramètre "typemap" de text(). Les fonctions Unicode natives de Psycopg2 OTOH prennent effet au niveau du curseur. Elles sont toujours actives et ajoutent apparemment une latence globale.

Voici une série d'illustrations du fonctionnement des différentes méthodes. le dernier est le plus semblable à celle de SQLAlchemy, bien que lors de l'utilisation des extensions C de sqlalchemy nous sommes probablement un jeûne comme psycopg2:

import psycopg2 
from psycopg2 import extensions 

conn = psycopg2.connect(user='scott', password='tiger', host='localhost', database='test') 

cursor = conn.cursor() 
cursor.execute(""" 
create table data (
    id SERIAL primary key, 
    data varchar(500) 
) 
""") 

cursor.executemany("insert into data (data) values (%(data)s)", [ 
     {"data":"abcdefghij" * 50} for i in xrange(10000) 
    ]) 
cursor.close() 


def one(conn): 
    cursor = conn.cursor() 
    cursor.execute("SELECT data FROM data") 
    for row in cursor: 
     row[0] 

def two(conn): 
    cursor = conn.cursor() 
    extensions.register_type(extensions.UNICODE, cursor) 
    cursor.execute("SELECT data FROM data") 
    for row in cursor: 
     row[0] 

def three(conn): 
    cursor = conn.cursor() 
    cursor.execute("SELECT data FROM data") 
    for row in cursor: 
     row[0].decode('utf-8') 

def four(conn): 
    cursor = conn.cursor() 
    def conv_unicode(value): 
     return value.decode('utf-8') 
    cursor.execute("SELECT data FROM data") 
    for row in cursor: 
     conv_unicode(row[0]) 

import timeit 

print "no unicode:", timeit.timeit("one(conn)", "from __main__ import conn, one", number=100) 

print "native unicode:", timeit.timeit("two(conn)", "from __main__ import conn, two", number=100) 

print "in Python unicode:", timeit.timeit("three(conn)", "from __main__ import conn, three", number=100) 

print "more like SQLA's unicode:", timeit.timeit("four(conn)", "from __main__ import conn, four", number=100) 

les horaires que je reçois:

no unicode: 2.10434007645 
native unicode: 4.52875208855 
in Python unicode: 4.77912807465 
more like SQLA's unicode: 4.88325881958 

donc ce qui est intéressant ici, est que l'approche de SQLA, si nous avons utilisé les extensions C, pourrait être un meilleur choix que l'approche native de psycopg2, si en fait vous n'utilisez pas beaucoup le type Unicode et la plupart de vos valeurs de chaînes sont seulement ASCII pur.

+0

Réponse incroyable, merci!Par simple curiosité, quel type de machine utilisez-vous? Quand j'ai essayé votre script de test, je reçois ~ 30secs, bien que je sois dans une VM Ubuntu de 3gb. –

0

tl; dr: il y a eu quelques améliorations de performances récemment dans la gestion d'unicode dans psycopg2 - essayez une version 2.7.

J'ai remarqué la même chose que vous et envoyé un peu de temps à @zzzeek. Voici sa réponse dans la liste de diffusion. Mais, fondamentalement, cela se résume au fait que la manipulation de l'unicode c-extension dans sqlalchemy semble être plus efficace que celle de psycopg2. J'ai notifié la liste de diffusion psycopg2 et ouvert un problème et j'ai eu une bonne réponse (https://github.com/psycopg/psycopg2/issues/473).

Questions connexes