2012-03-19 19 views
14
//FAST 
db.datasources.find().count() 
12036788 

//SLOW  
db.datasources.find({nid:19882}).count() 
10161684 

Index sul nidMongo conta molto lento quando ci sono milioni di dischi

Un modo per rendere la seconda query più veloce? (Ci vogliono circa 8 secondi)

+0

Anche se hai un indice su nid, potrebbe non essere residente in memoria. Cosa succede se si esegue la query una seconda volta, è più veloce? –

+0

No, è ancora lento –

+0

Quanto dura la prima query? Sei sicuro che non ci siano altre operazioni in esecuzione sul server? –

risposta

23

Le query di conteggio, indicizzate o meno, sono lente a causa del fatto che MongoDB deve ancora eseguire una camminata b-tree completa per trovare il numero appropriato di documenti che corrispondono ai criteri. La ragione di ciò è che la struttura b-tree MongoDB non viene "contata", nel senso che ciascun nodo non memorizza informazioni sulla quantità di elementi nel nodo/sottoalbero.

Il problema è riportato qui https://jira.mongodb.org/browse/SERVER-1752 e non esiste attualmente alcuna soluzione alternativa per migliorare le prestazioni se non quella di mantenere manualmente un contatore per quella raccolta che ovviamente presenta alcuni aspetti negativi.

Si noti inoltre che la versione db.col.count() (quindi nessun criterio) può richiedere una grande scorciatoia e in realtà non esegue una query, quindi la sua velocità. Detto questo, non sempre riporta lo stesso valore di una query di conteggio che dovrebbe restituire tutti gli elementi (non sarà in ambienti con un elevato throughput di scrittura, ad esempio). Per discutere se questo sia un bug. Io penso che sia.

Si noti che in 2.3+ è stata introdotta una significativa ottimizzazione che dovrebbe (e lo fa) migliorare le prestazioni dei conteggi nei campi indicizzati. Vedi: https://jira.mongodb.org/browse/SERVER-7745

+2

Mi chiedo come usare per l'impaginazione dell'esame con mongodb, se il conteggio è così lento. – Oleg

+1

L'impaginazione è un problema più complesso di quello che potrebbe sembrare quando si lavora con i big data. Ad esempio, skip (N) è un'operazione o (N) quindi non è possibile effettuare realmente l'impaginazione basata su skip (pageIndex * pageSize) .limit (pageSize) né è possibile determinare il conteggio totale della pagina con un conteggio(). È possibile, ma diventerà più lento con l'aumento delle dimensioni dei dati, che è uno schema errato. Ci sono soluzioni per intenderci, sono solo più complicate di così. Tutto ciò che ha detto count() dovrebbe essere più veloce di quello che sono e MongoDB dovrebbe (almeno opzionalmente) usare b-alberi contati secondo me. –

+2

Non ho avuto fortuna a cercare l'impaginazione sui big data che non utilizza skip e limit. Se qualcuno ha un esempio, sarebbe bello condividere – vodich

0

Deve guardare attraverso ogni campo di ogni documento per il secondo. È possibile indicizzare nid per rendere il conteggio più veloce.

+6

Ho un indice su nid –

11

Come ha detto @Remon, count() deve eseguire la scansione di tutti i documenti corrispondenti alla query/filtro. È O (n) dove n è il numero di documenti che corrisponderanno all'indice o il numero di documenti nella raccolta se il campo non è indicizzato.

In questi casi, in genere si desidera rivedere il requisito. Hai davvero bisogno di un numero preciso per il risultato 10161684? Se la precisione è importante, è necessario conservare un contatore separato per la query specifica.

Ma nella maggior parte dei casi, la precisione non è importante. E 'uno dei due:

  • Non ti importa se si tratta di 10 milioni o 10,2 milioni, ma l'ordine di grandezza è importante, vale a dire, vi preoccupate se si tratta di 8 milioni o 10 milioni di euro.
  • Ti interessa solo il numero preciso se è piccolo. Ad esempio, ti interessa sapere che ci sono 44 risultati o 72. Ma una volta che va oltre, per esempio, 1000, puoi semplicemente dire "Più di 1000 oggetti" trovati all'utente.

Nelle mie app, ho trovato che la seconda opzione è ciò che voglio. Quindi, limito anche la query count(), in modo che il conteggio si fermi quando raggiunge un limite. In questo modo:

db.datasources.find({nid: 19882}).limit(1000).count(true) 

Per l'utente, io espongo '1000 o più risultati' se il numero è 1000, in caso contrario, visualizzo il numero esatto.

Per quanto riguarda la prima opzione ... Non ho ancora pensato a una soluzione accurata.

+0

cosa fa '.count (true)' fare qui? – chovy

+1

count() restituisce il numero di documenti corrispondenti. count() accetta un argomento 'applySkipLimit' che influisce sul fatto che il conteggio sia sul set saltato e limitato o sull'intero set (solo filtrato). Se si passa true, limiterà il conteggio a 1000. Se si passa false, passerà attraverso tutti i documenti corrispondenti per ottenere il conteggio - questo sarà lento e non quello che si desidera. – user3392439