Sto lavorando per ottimizzare un'applicazione Django che è (principalmente) supportata da MongoDB. Sta morendo sotto test di carico. Nella pagina problematica corrente, New Relic mostra oltre 700 chiamate allo pymongo.collection:Collection.find
. Gran parte del codice è stata scritta da programmatori junior e normalmente cercherò luoghi in cui aggiungere indici, creare join più intelligenti e rimuovere i loop per ridurre le chiamate di query, ma i join non sono un'opzione qui. Quello che ho fatto (dopo aver aggiunto gli indici basati su EXPLAIN) è provato a ridurre il costo nei loop facendo una query generale e quindi filtrando quella piccola serie nei loop *. Mentre ho ottenuto il numero da 900 query, 700 sembra ancora folle anche con l'intenso lavoro svolto sulla pagina. Ho pensato che forse find
è stato chiamato anche durante il filtraggio di un queryset esistente, ma il codice suggerisce che è sempre un database query.Ridurre il numero di chiamate a MongoDB con mongoengine
Ho aggiunto alcune registrazioni a mongoengine per vedere da dove provengono le query e per dare un'occhiata alle istruzioni EXPLAIN, ma non sto avendo un sacco di fortuna spulciando il muro delle informazioni. la stessa mongoengine sembra essere parte del problema delle prestazioni: sono passato a mongomallard come test e ho ottenuto un miglioramento delle prestazioni del 50% sulla pagina. Purtroppo ho ricevuto degli errori su un sacco di altre pagine (come posso dire che sembra che Mallard non funzioni bene quando filtra un queryset esistente, l'errore si lamenta di una chiamata a deepcopy
che avviene in un generatore, che non è possibile do-- Ho colpito un muro di mattoni lì). Sebbene Mallard non sembri un rimpiazzo praticabile per noi, suggerisce che gran parte del tempo di proiezione è dedicato alla conversione di oggetti da e verso Python in mongoengine.
Cosa posso fare per ridurre ulteriormente le chiamate? O mi sto concentrando sulla cosa sbagliata e dovrei attaccare il problema da qualche altra parte?
EDIT: fornendo alcuni codici/modelli
La pagina in questione mostra il programma per un corso, che mostra tutti i moduli del corso, le lezioni ei concetti sotto le lezioni. Per ogni concetto, viene mostrato anche il progresso dell'utente nel concetto. Quindi c'è un sacco di loop per ottenere la gerarchia presa in giro (e non è memorizzata secondo uno qualsiasi dei patterns the Mongo docs suggest).
class CourseVersion(Document):
...
course_instances = ListField(ReferenceField('CourseInstance'))
courseware_containers = ListField(EmbeddedDocumentField('CoursewareContainer'))
class CoursewareContainer(EmbeddedDocument):
id = UUIDField(required=True, binary=False, default=uuid.uuid4)
....
courseware_containers = ListField(EmbeddedDocumentField('self'))
teaching_element_instances = ListField(StringField())
del corso moduli, lezioni e concetti sono memorizzati in courseware_containers
; abbiamo bisogno di ottenere tutti i concetti in modo da poter ottenere l'elenco degli ID in teaching_element_instances
per trovare quello più recente su cui l'utente ha lavorato (se esiste) per quel concetto e quindi cercare i loro progressi.
* Giusto per essere chiari, sto usando un profiler e guardo alle cose degli eventi e delle cose Il modo giusto come meglio lo so, non semplicemente cambiando le cose e sperando per il meglio.
un sacco di chiamate suona come un'implementazione del modello "di riferimento" piuttosto che "embedded" a me. Ma non il giusto tipo di informazioni nella tua domanda per chiunque per determinare questo. Da una prospettiva di programmazione che è. –
Non hai pubblicato alcun codice quindi sto solo cercando di indovinare, ma se esegui un loop su un set di query Django e accedi a una chiave esterna sulle istanze del modello in quel ciclo, viene attivata una query 'find' in quel punto. Hai provato http://docs.mongoengine.org/apireference.html#mongoengine.Document.select_related? fa ancora richieste aggiuntive ma potrebbe essere un po 'più ottimizzato http://stackoverflow.com/a/16166970/202168 – Anentropic
Senza * qualsiasi * codice questo è impossibile eseguire il debug. Ma come detto sopra uso 'select_related' per il recupero efficiente delle relazioni. Un 'find()' non è una query poiché è un cursore lazy - solo quando è iterato diventa una query a MongoDB. Se si stanno eseguendo cicli in cui si filtrano i risultati, si ripetono i risultati filtrati, quindi si tratta di almeno una query, se esistono relazioni nidificate che esploderanno il numero di query. Potrebbe essere più efficiente ottenere il cast del singolo set di risultati in un elenco, quindi filtrare usando python. – Ross