2013-09-24 15 views
11

Sto costruendo un sito Web Django con un backend Oracle e osservo prestazioni molto lente anche quando eseguo semplici ricerche sulla chiave primaria. Lo stesso codice funziona molto velocemente quando gli stessi dati vengono caricati in MySQL.Scarsa performance di Django ORM con Oracle

Quale potrebbe essere il motivo della scarsa prestazione? Ho il sospetto che il problema sia legato all'uso dei parametri di collegamento Oracle, ma potrebbe non essere il caso.

Django modello (una tabella di test con ~ 6.200.000 righe)

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 (prende ~ 1s)

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

interrogazione grezza con i parametri bind (prende ~ 1s)

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

interrogazione crudo senza legano parametri (istantanea)

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

Il mio ambiente

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

Quando si collega al database Oracle a specificare:

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

Ogni aiuto sarà molto apprezzato.

[Update] Ho fatto qualche ulteriore prova utilizzando lo strumento debugsqlshell dalla barra degli strumenti di debug Django.

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

Questo suggerisce che Django utilizza i parametri legano Oracle e query stessa è molto veloce, ma la creazione dell'oggetto Python corrispondente richiede un tempo molto lungo.

Giusto per confermare, ho eseguito la stessa query utilizzando cx_Oracle (si noti che il cursor nella mia domanda originale è il 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() 

Cosa potrebbe rallentare Django ORM?

[Aggiornamento 2] Abbiamo esaminato le prestazioni del database dal lato Oracle e si scopre che l'indice non viene utilizzato quando la query proviene da Django. Qualche idea sul perché questo potrebbe essere il caso?

+0

Hai controllato l'indice per il campo di ricerca esistono in db? – esauro

+0

Quando controllo la tabella in SQL Developer, vedo che esiste un indice normale valido su quella colonna. – apetrov

+0

Cosa succede se si eseguono le 2 versioni in SQL Developer e i piani di query sono diversi (utilizzare i pulsanti Spiega piano o Tracciamento automatico)? Per le variabili di bind usa 'SELECT * FROM mytable WHERE upi =: s' e SQL Developer ti chiederà il valore. –

risposta

1

Dopo aver lavorato con i nostri DBA, si è scoperto che per qualche motivo le query Django get(upi='xxxxxxxxxxxx') non utilizzavano l'indice del database.

Quando la stessa query è stata riscritta utilizzando filter(upi='xxxxxxxxxxxx')[:1].get(), la query è stata rapida.

La query get era veloce solo con chiavi primarie intere (era una stringa nella domanda originale).

soluzione finale

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

Sembra esserci qualche disallineamento tra i set di caratteri utilizzati da cx_Oracle e Oracle. L'aggiunta dell'indice C2C risolve il problema.

UPDATE: Inoltre, il passaggio a NVARCHAR2 da VARCHAR2 in Oracle ha lo stesso effetto e possono essere utilizzati invece del indice funzionale.

Ecco alcuni thread di discussione utili che mi hanno aiutato: http://comments.gmane.org/gmane.comp.python.db.cx-oracle/3049 http://comments.gmane.org/gmane.comp.python.db.cx-oracle/2940

2

Utilizzando TO_CHAR(character) dovrebbe risolvere il problema di prestazioni:

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