Django QuerySet vs Raw Query-Leistung

Ich habe einen großen zeitlichen Unterschied zwischen der Verwendung von django connection.cursor und der Verwendung der Modellschnittstelle festgestellt, selbst bei kleinen Abfragesätzen. Ich habe die Modellschnittstelle mit values_list so effizient wie möglich gestaltet, damit keine Objekte erstellt werden und so. Im Folgenden sind die beiden getesteten Funktionen aufgeführt, ohne Rücksicht auf die spanischen Namen.

def t3():
    q = "select id, numerosDisponibles FROM samibackend_eventoagendado LIMIT 1000"
    with connection.cursor() as c:
        c.execute(q)
        return list(c)

def t4():
    return list(EventoAgendado.objects.all().values_list('id','numerosDisponibles')[:1000])

Dann mit einer Funktion zur Zeit (selbst gemacht mit time.clock ())

r1 = timeme(t3); r2 = timeme(t4)

Die Ergebnisse lauten wie folgt: 0.00180384529631 und 0.00493390727024 für t3 und t4

Und nur um sicherzustellen, dass die Abfragen korrekt sind, führen Sie die folgenden Schritte aus:

connection.queries[-2::]

Yields:

[
    {u'sql': u'select id, numerosDisponibles FROM samibackend_eventoagendado LIMIT 1000',  u'time': u'0.002'},
    {u'sql': u'SELECT `samiBackend_eventoagendado`.`id`, `samiBackend_eventoagendado`.`numerosDisponibles` FROM `samiBackend_eventoagendado` LIMIT 1000', u'time': u'0.002'}
]

Wie Sie sehen können, sind zwei exakte Abfragen, die zwei exakte Listen zurückgeben (r1 == r2 ergibt True), völlig unterschiedliche Timings (Differenz wird größer mit einem größeren Abfragesatz). Ich weiß, dass Python langsam ist, aber es ist Django, das dies tut viel arbeit hinter den kulissen, um die abfrage so langsam zu machen? Um sicherzugehen, habe ich versucht, das Queryset-Objekt zuerst (außerhalb des Timers) zu erstellen, aber die Ergebnisse sind dieselben. Daher bin ich mir zu 100% sicher, dass das Abrufen und Erstellen der Ergebnisstruktur mehr Zeit kostet. Ich habe auch versucht, die Iterator () - Funktion am Ende der Abfrage zu verwenden, aber das hilft auch nicht. Ich weiß, dass der Unterschied minimal ist, beide unglaublich schnell ausgeführt werden, aber dies wird mit Apache ab verglichen, und dieser minimale Unterschied macht bei 1k gleichzeitigen Anforderungen Tag und Lich

Übrigens verwende ich Django 1.7.10 mit mysqlclient als DB-Connector.

EDIT: Zum Vergleich: Beim gleichen Test mit einer 11k-Ergebnisabfrage wird der Unterschied sogar noch größer (3x langsamer, verglichen mit dem ersten Test, bei dem er etwa 2,6x langsamer ist).

r1 = timeme(t3); r2 = timeme(t4)
0.0149241530889
0.0437563529558

EDIT2: Ein weiterer lustiger Test: Wenn ich das Queryset-Objekt tatsächlich in eine tatsächliche Zeichenfolgenabfrage konvertiere (mit str (queryset.query)) und es stattdessen in einer unformatierten Abfrage verwende, erhalte ich die gleiche gute Leistung wie die unformatierte Abfrage von Die Ausführung, die unter Verwendung der Zeichenfolge "queryset.query" manchmal zu einer tatsächlich ungültigen SQL-Abfrage führt (dh, wenn die Abfrage einen Filter für einen Datumswert enthält, wird der Datumswert nicht mit "" in der Zeichenfolgenabfrage maskiert, was zu einem SQL-Fehler führt, wenn es mit einer rohen Abfrage auszuführen, ist dies ein weiteres Rätsel)

- EDIT3: Beim Durchgehen des Codes scheint der Unterschied darin zu liegen, wie die Ergebnisdaten abgerufen werden. Bei einer unformatierten Abfragesammlung wird einfach @ aufgerufeiter(self.cursor), von dem ich glaube, dass es bei Verwendung eines C-implementierten Connectors nur in C-Code ausgeführt wird (da es sich auch um einen eingebauten C-Code handelt), während ValuesListQuerySet tatsächlich eine Python-Ebene für die Schleife mit einer Yield-Tupel-Anweisung (Zeile) ist, die recht langsam ist . Ich schätze, es ist in dieser Angelegenheit nichts zu tun, um die gleiche Leistung wie bei der rohen Abfrage zu erzielen: '(. Wenn jemand interessiert ist, ist die langsame Schleife die folgende:

for row in self.query.get_compiler(self.db).results_iter():
    yield tuple(row)

- EDIT 4: Ich habe einen sehr kniffligen Code zum Konvertieren einer Wertelistenabfrage in verwendbare Daten, die an eine unformatierte Abfrage gesendet werden sollen, mit derselben Leistung wie das Ausführen einer unformatierten Abfrage. Ich denke, dies ist sehr schlecht und funktioniert nur mit MySQL. Aber die Geschwindigkeit ist sehr gut, während ich die Modell-API-Filterung und so weiter beibehalten kann. Was denkst du? Hier ist der Code.

def querysetAsRaw(qs):
    q = qs.query.get_compiler(qs.db).as_sql()
    with connection.cursor() as c:
        c.execute(q[0], q[1])
        return c

Antworten auf die Frage(2)

Ihre Antwort auf die Frage