2009-07-23 9 views
5

Stavo confrontando un vecchio script PHP contro il più recente, più follemente la versione Django e quella PHP, con sputi completi di HTML e tutto funzionava più velocemente. MOLTO più veloce al punto che qualcosa deve essere sbagliato su quello Django.Django (?) Molto lento con set di dati di grandi dimensioni dopo aver eseguito alcuni profili Python

In primo luogo, un contesto: ho una pagina che sputa i rapporti dei dati di vendita. I dati possono essere filtrati da un certo numero di cose ma sono per lo più filtrati per data. Ciò rende un po 'difficile metterlo nella cache poiché le possibilità di risultati sono quasi infinite. Ci sono un sacco di numeri e calcoli fatti ma non è mai stato un gran problema da gestire in PHP.

AGGIORNAMENTI:

  • Dopo alcuni test supplementari non c'è nulla nel mio punto di vista che sta causando il rallentamento. Se mi limito a digitare i dati e sputare 5 righe di HTML reso, non è così lento (ancora più lento di PHP), ma se sto facendo un sacco di dati, è MOLTO lento.

  • Ogni volta che ho eseguito un rapporto di grandi dimensioni (ad esempio tutte le vendite dell'anno), l'utilizzo della CPU della macchina è pari al 100%. Non so se questo significa molto. Sto usando mod_python e Apache. Forse passare a WSGI può aiutare?

  • I miei tag modello che mostrano i totali parziali/totali vanno da 0,1 secondi a 1 secondo per set molto grandi. Li chiamo circa 6 volte all'interno del report in modo che non sembri il problema più grande.

Ora, ho eseguito un profiler Python e tornato con questi risultati:

 
Ordered by: internal time 
    List reduced from 3074 to 20 due to restriction 

    ncalls tottime percall cumtime percall filename:lineno(function) 
    2939417 26.290 0.000 44.857 0.000 /usr/lib/python2.5/tokenize.py:212(generate_tokens) 
    2822655 17.049 0.000 17.049 0.000 {built-in method match} 
    1689928 15.418 0.000 23.297 0.000 /usr/lib/python2.5/decimal.py:515(__new__) 
12289605 11.464 0.000 11.464 0.000 {isinstance} 
    882618 9.614 0.000 25.518 0.000 /usr/lib/python2.5/decimal.py:1447(_fix) 
    17393 8.742 0.001 60.798 0.003 /usr/lib/python2.5/tokenize.py:158(tokenize_loop) 
     11 7.886 0.717 7.886 0.717 {method 'accept' of '_socket.socket' objects} 
    365577 7.854 0.000 30.233 0.000 /usr/lib/python2.5/decimal.py:954(__add__) 
    2922024 7.199 0.000 7.199 0.000 /usr/lib/python2.5/inspect.py:571(tokeneater) 
    438750 5.868 0.000 31.033 0.000 /usr/lib/python2.5/decimal.py:1064(__mul__) 
    60799 5.666 0.000 9.377 0.000 /usr/lib/python2.5/site-packages/django/db/models/base.py:241(__init__) 
    17393 4.734 0.000 4.734 0.000 {method 'query' of '_mysql.connection' objects} 
    1124348 4.631 0.000 8.469 0.000 /usr/lib/python2.5/site-packages/django/utils/encoding.py:44(force_unicode) 
    219076 4.139 0.000 156.618 0.001 /usr/lib/python2.5/site-packages/django/template/__init__.py:700(_resolve_lookup) 
    1074478 3.690 0.000 11.096 0.000 /usr/lib/python2.5/decimal.py:5065(_convert_other) 
    2973281 3.424 0.000 3.424 0.000 /usr/lib/python2.5/decimal.py:718(__nonzero__) 
    759014 2.962 0.000 3.371 0.000 /usr/lib/python2.5/decimal.py:4675(__init__) 
    381756 2.806 0.000 128.447 0.000 /usr/lib/python2.5/site-packages/django/db/models/fields/related.py:231(__get__) 
    842130 2.764 0.000 3.557 0.000 /usr/lib/python2.5/decimal.py:3339(_dec_from_triple) 

tokenize.py esce in cima, che possono rendere un certo senso come sto facendo un sacco di formattazione dei numeri . Decimal.py ha senso poiché il report è essenzialmente al 90%. Non ho idea di cosa sia il metodo integrato match come non sto facendo alcun Regex o simile nel mio stesso codice (Qualcosa che Django sta facendo?) La cosa più vicina è che sto usando itertools ifilter.

Sembra che questi siano i principali colpevoli e se potessi capire come ridurre i tempi di elaborazione di quelli allora avrei una pagina molto più veloce.

Qualcuno ha qualche suggerimento su come potrei iniziare a ridurlo? Non so davvero come risolverei i problemi tokenize/decimal senza semplicemente rimuoverli.

Aggiornamento: ho eseguito alcuni test con/senza filtri sulla maggior parte dei dati e i tempi dei risultati sono tornati praticamente uguali, essendo quest'ultimo un po 'più veloce ma non molto per essere la causa del problema. Cosa sta succedendo esattamente in tokenize.py?

+0

È impossibile suggerire qualcosa di utile senza il codice di visualizzazione e la guida per la creazione di profili. –

+0

Alex: La mia opinione è abbastanza semplice. Apre un elenco iniziale di voci, quindi se il report viene modificato aggiunge alcuni filtri. Questo è davvero. Il mio modello quindi raggruppa il set di dati in due sezioni e quindi esegue il ciclo di tutto, chiamando templatetags lungo la strada (ma ho programmato i tag del template da eseguire in 0.1 -> 0.5 secondi .. questi templatetags sono i subtotali/totali del report in modo che il tempo di esecuzione sia corretto su enormi serie di dati.) – Bartek

+0

@Bartek: per favore non commentare la tua stessa domanda. È la tua domanda, puoi aggiornarla per contenere tutti i fatti rilevanti. –

risposta

6

Ci sono molte cose da considerare sul tuo problema in quanto non hai alcun tipo di esempio di codice.

Ecco le mie ipotesi: Stai utilizzando gli strumenti e i modelli ORM incorporati di Django (ad esempio sales-data = modelobj.objects(). All()) e sul lato PHP hai a che fare con query SQL dirette e lavoro con un query_set.

Django sta eseguendo molti tipi di conversione e fusione di tipi di dati che vanno da una query di database all'oggetto ORM/Modello e al gestore associato (objects() per impostazione predefinita).

In PHP si controllano le conversioni e si conosce esattamente come eseguire il cast da un tipo di dati a un altro, si risparmiano alcuni tempi di esecuzione in base a tale problema.

Si consiglia di provare a spostare alcuni di quei fantasiosi numeri di lavoro nel database, soprattutto se si sta eseguendo l'elaborazione basata su record - i database mangiano quel tipo di elaborazione dalla prima colazione. In Django è possibile inviare RAW SQL sopra al database: http://docs.djangoproject.com/en/dev/topics/db/sql/#topics-db-sql

Spero che questo almeno si può ottenere puntato nella giusta direzione ...

+0

Grazie. Hai ragione, questo ha senso. Stavo vedendo le query in esecuzione bene e con bassa MS, quindi non l'ho mai considerato. Il problema è, naturalmente, che l'ORM è adorabile e mantiene il codice molto più pulito in casi come questo, quindi mi piacerebbe non dover andare su quella strada se possibile. L'elaborazione numerica che sto facendo non è complessa (aggiungi questi tre numeri, moltiplica questo) e poi li sto semplicemente esportando usando i filtri django | intcomma e | floatformat: 2 quindi non sono sicuro se questo è il nocciolo del problema. – Bartek

+0

Il problema che potresti incontrare per quanto riguarda l'aggiunta di numeri, moltiplicando i numeri è la quantità di record. Se si riduce il numero di record restituiti, si ridurrà il sovraccarico della memoria e il tempo necessario per elaborare tali dati. Tieni questo a mente: non puoi rendere un'applicazione più veloce, puoi solo farlo funzionare meno. –

+0

Sfortunatamente in alcuni casi gravi, l'utente vuole un rapporto sui dati di vendita annuali e non posso davvero mantenere i record inferiori. .. :) – Bartek

2

"tokenize.py esce sulla parte superiore, che può fare un po ' Sento che sto facendo un sacco di formattazione dei numeri. "

Non ha alcun senso.

Vedere http://docs.python.org/library/tokenize.html.

Il modulo fornisce una tokenize scanner lessicale per il codice sorgente Python, implementato in Python

Tokenize che esce in alto significa che avete codice di analisi dinamica in corso.

AFAIK (eseguendo una ricerca nel repository Django) Django non utilizza tokenize. In questo modo il tuo programma farà una specie di istanza di codice dinamica. Oppure, stai solo creando il profilo prima del tempo in cui il tuo programma viene caricato, analizzato ed eseguito, portando a false ipotesi su dove sta andando il tempo.

È necessario non fare mai calcoli nei tag modello: è lento. Implica una meta-valutazione complessa del tag del modello. Dovresti fare tutti i calcoli nella vista in Python semplice, a basso overhead. Utilizzare i modelli solo per la presentazione.

Inoltre, se si eseguono costantemente query, filtri, somme e quant'altro, si dispone di un data warehouse. Ottieni un libro sulla progettazione del data warehouse e segui i modelli di progettazione del data warehouse.

È necessario disporre di una tabella dei fatti centrale, circondata da tabelle delle dimensioni. Questo è molto, molto efficiente.

Somme, group-by, ecc. Possono essere eseguiti come operazioni defaultdict in Python. Bulk recupera tutte le righe, costruendo il dizionario con i risultati desiderati. Se questo è troppo lento, allora devi usare tecniche di data warehousing per salvare somme persistenti e gruppi separati dai tuoi fatti a grana fine. Spesso ciò comporta l'uscita dall'ORM Django e l'utilizzo di funzionalità RDBMS come viste o tabelle di dati derivati.

+0

Puoi dirmi perché non dovrei fare nessun calcolo (aggiunta di base per sommare i numeri) in templatetags? Dopo il tuo post, ho scoperto un modo più efficiente di fare ciò che i miei templatetags stanno facendo, il che è piuttosto utile, ma non sono ancora il collo di bottiglia. Almeno si raderà alcuni secondi di tempo di elaborazione :) Grazie – Bartek

2

Quando si utilizzano insiemi di dati di grandi dimensioni, è possibile anche risparmiare un sacco di CPU e memoria utilizzando lo ValuesQuerySet che accede ai risultati della query in modo più diretto invece di creare un'istanza di modello per ogni riga del risultato.

utilizzo di sembra un po 'come questo:

Blog.objects.order_by('id').values() 
1

In un tale scenario il database è spesso il collo di bottiglia. Inoltre, l'utilizzo di un ORM potrebbe causare query SQL non ottimali.

Come alcuni hanno sottolineato non è possibile dire cosa sia realmente il problema, solo con le informazioni fornite.

ho appena posso darvi alcuni consigli generali:

  • Se la vista sta lavorando con oggetti del modello correlate, considerare l'utilizzo di select_related(). Questo semplice metodo potrebbe velocizzare notevolmente le query generate dall'ORM.
  • Utilizzare Debug Footer Middleware per vedere quali query SQL sono generate dalle visualizzazioni e il tempo impiegato per l'esecuzione.

PS: Solo fyi, ho avuto una volta una visione abbastanza semplice che era molto lento. Dopo aver installato il Debug Footer Middleware l'ho visto intorno ai 500! query SQL sono state eseguite in quella singola vista. L'uso di select_related() lo ha ridotto a 5 query e la visualizzazione è stata eseguita come previsto.

Problemi correlati