2013-09-24 5 views
11

Ich baue eine Django-Website mit einem Oracle-Backend, und ich beobachte sehr langsame Leistung, auch wenn ich einfache Lookups auf dem Primärschlüssel durchführe. Derselbe Code funktioniert sehr schnell, wenn die gleichen Daten in MySQL geladen werden.Schlechte Leistung von Django ORM mit Oracle

Was könnte der Grund für die schlechte Leistung sein? Ich habe den Verdacht, dass das Problem mit der Verwendung von Oracle-Bindeparametern zusammenhängt, aber dies ist möglicherweise nicht der Fall.

Django Modell (eine Testtabelle mit ~ 6.200.000 Zeilen)

from django.db import models 

class Mytable(models.Model): 
    upi = models.CharField(primary_key=True, max_length=13) 

    class Meta: 
     db_table = 'mytable' 

Django ORM (dauert ~ 1s)

from myapp.models import * 
r = Mytable.objects.get(upi='xxxxxxxxxxxxx') 

Raw Abfrage mit bind Parameter (nimmt ~ 1s)

cursor.execute("SELECT * FROM mytable WHERE upi = %s", ['xxxxxxxxxxxxx']) 
row = cursor.fetchone() 
print row 

Raw Abfrage ohne Bind Parameter (momentane)

cursor.execute("SELECT * FROM mytable WHERE upi = 'xxxxxxxxxxxxx'") 
row = cursor.fetchone() 
print row 

Meine Umgebung

  • Python 2.6.6
  • Django 1.5.4
  • cx-Oracle 5.1.2
  • Oracle 11g

Wenn die Oracle-Datenbank verbinden ich angeben:

'OPTIONS': { 
    'threaded': True, 
} 

Jede Hilfe sehr geschätzt wird.

[Update] habe ich einige weitere Tests die debugsqlshell Werkzeug aus der Django-Debug-Toolbar verwenden.

# takes ~1s 
>>>Mytable.objects.get(upi='xxxxxxxxxxxxx') 
SELECT "Mytable"."UPI" 
FROM "Mytable" 
WHERE "Mytable"."UPI" = :arg0 [2.70ms] 

Dies legt nahe, dass die Oracle Django bind Parameter verwendet, und die Abfrage selbst ist sehr schnell, aber das entsprechende Objekt Python Erstellung dauert sehr lange Zeit.

Nur um zu bestätigen, habe ich die gleiche Abfrage mit cx_Oracle (beachten Sie, dass die cursor in meiner ursprünglichen Frage ist die Django cursor).

import cx_Oracle 
db= cx_Oracle.connect('connection_string') 
cursor = db.cursor() 

# instantaneous 
cursor.execute('SELECT * from mytable where upi = :upi', {'upi':'xxxxxxxxxxxxx'}) 
cursor.fetchall() 

Was könnte Django ORM verlangsamen?

[Update 2] Wir haben die Datenbankleistung von der Oracle-Seite betrachtet, und es stellt sich heraus, dass der Index nicht verwendet wird, wenn die Abfrage von Django kommt. Irgendwelche Ideen, warum das der Fall sein könnte?

+0

Sie den Index für das Nachschlag-Feld überprüfen Gab es in db? – esauro

+0

Wenn ich die Tabelle in SQL Developer überprüfe, sehe ich, dass es einen gültigen normalen Index für diese Spalte gibt. – apetrov

+0

Was passiert, wenn Sie die zwei Versionen in SQL Developer ausführen und sich die Abfragepläne unterscheiden (verwenden Sie die Schaltflächen Plan erklären oder AutoTrace verwenden)? Für Bind-Variablen verwenden Sie SELECT * FROM meine Tabelle WHERE upi =: s und SQL Developer wird Sie nach dem Wert fragen. –

Antwort

1

Nach der Arbeit mit unseren DBAs stellte sich heraus, dass die Django get(upi='xxxxxxxxxxxx') Abfragen aus irgendeinem Grund den Datenbankindex nicht verwendeten.

Wenn die gleiche Abfrage mit filter(upi='xxxxxxxxxxxx')[:1].get() umgeschrieben wurde, war die Abfrage schnell.

Die get Abfrage war schnell nur mit ganzzahligen Primärschlüssel (es war String in der ursprünglichen Frage).

ENDLÖSUNG

create index index_name on Mytable(SYS_OP_C2C(upi)); 

Es scheint eine gewisse Diskrepanz zwischen den Zeichensätzen von cx_Oracle und Oracle verwendet werden. Das Hinzufügen des C2C-Index behebt das Problem.

UPDATE: Auch aus VARCHAR2 in Oracle NVARCHAR2 Schalt hat die gleiche Wirkung und kann anstelle des Funktions Index verwendet werden.

Hier sind einige nützliche Diskussions-Threads, die mir geholfen: http://comments.gmane.org/gmane.comp.python.db.cx-oracle/3049 http://comments.gmane.org/gmane.comp.python.db.cx-oracle/2940

2

TO_CHAR(character) Verwendung sollte das Leistungsproblem lösen:

cursor.execute("SELECT * FROM mytable WHERE upi = TO_CHAR(%s)", ['xxxxxxxxxxxxx'])