Questo è cercare l'ago in un pagliaio. Avremmo bisogno di un output di explain()
per quelle query che non funzionano bene. Sfortunatamente, anche questo risolverebbe il problema solo per quella particolare query, quindi ecco una strategia su come affrontare questo problema:
- Assicurati che non sia dovuto a RAM insufficiente e paging eccessivo
- Abilita il DB Profiler (usando
db.setProfilingLevel(1, timeout)
dovetimeout
è la soglia per il numero di millisecondi necessari per la query o il comando, qualsiasi cosa più lenta verrà registrata) - Ispeziona le query lente in
db.system.profile
ed esegui le query manualmente usandoexplain()
- Cerca di identificare le operazioni lente in
explain()
output, comescanAndOrder
onscanned
di grandi dimensioni , ecc. - Motivo della selettività della query e se è possibile migliorare la query utilizzando un indice del tutto . In caso contrario, valuta la possibilità di non consentire l'impostazione del filtro per l'utente finale o inviagli una finestra di avviso che l'operazione potrebbe essere lenta.
Un problema chiave è che apparentemente stai consentendo ai tuoi utenti di combinare i filtri a piacimento. Senza l'intersezione degli indici, ciò farà aumentare notevolmente il numero di indici richiesti.
Inoltre, lanciare alla cieca un indice a ogni possibile query è una pessima strategia. È importante strutturare le query e assicurarsi che i campi indicizzati abbiano una selettività sufficiente .
Supponiamo che tu abbia una query per tutti gli utenti con status
"attivo" e alcuni altri criteri. Ma dei 5 milioni di utenti, 3 milioni sono attivi e 2 milioni no, quindi su 5 milioni di voci ci sono solo due valori diversi. Un tale indice di solito non aiuta. È meglio cercare prima gli altri criteri, quindi eseguire la scansione dei risultati. In media, quando restituisci 100 documenti, dovrai scansionare 167 documenti, il che non danneggerà troppo le prestazioni. Ma non è così semplice. Se il criterio principale è joined_at
la data dell'utente e la probabilità che gli utenti interrompano l'uso nel tempo sono elevate, potresti finire per dover scansionare migliaia di documenti prima di trovare cento corrispondenze.
Quindi l'ottimizzazione dipende molto dai dati (non solo dalla sua struttura , ma anche i dati stessi ), le sue correlazioni interne e i tuoi modelli di query .
Le cose peggiorano quando i dati sono troppo grandi per la RAM, perché quindi avere un indice è fantastico, ma la scansione (o anche semplicemente la restituzione) dei risultati potrebbe richiedere il recupero casuale di molti dati dal disco, il che richiede molto tempo.
Il modo migliore per controllare questo è limitare il numero di diversi tipi di query, non consentire query su informazioni a bassa selettività e cercare di impedire l'accesso casuale ai vecchi dati.
Se tutto il resto fallisce e se hai davvero bisogno di tanta flessibilità nei filtri, potrebbe essere utile prendere in considerazione un DB di ricerca separato che supporti le intersezioni degli indici, recuperare gli ID mongo da lì e quindi ottenere i risultati da mongo usando $in
. Ma questo è irto di pericoli.
-- MODIFICA --
La spiegazione che hai pubblicato è un bellissimo esempio del problema con la scansione dei campi a bassa selettività. Apparentemente, ci sono molti documenti per "[email protected]". Ora, trovare quei documenti e ordinarli decrescente per timestamp è piuttosto veloce, perché è supportato da indici ad alta selettività. Sfortunatamente, poiché esistono solo due tipi di dispositivi, mongo deve scansionare 30060 documenti per trovare il primo che corrisponda a "mobile".
Presumo che si tratti di una sorta di tracciamento Web e che il modello di utilizzo dell'utente renda la query lenta (se cambiasse mobile e Web ogni giorno, la query sarebbe veloce).
È possibile velocizzare questa particolare query utilizzando un indice composto che contiene il tipo di dispositivo, ad es. usando
a) ensureIndex({'username': 1, 'userAgent.deviceType' : 1, 'timestamp' :-1})
o
b) ensureIndex({'userAgent.deviceType' : 1, 'username' : 1, 'timestamp' :-1})
Sfortunatamente, ciò significa che query come find({"username" : "foo"}).sort({"timestamp" : -1});
non posso più utilizzare lo stesso indice, quindi, come descritto, il numero di indici aumenterà molto rapidamente.
Temo che non ci sia una buona soluzione per questo usando mongodb in questo momento.