2014-07-22 24 views
6

Ho oltre 600k di record in MongoDb. il mio schema utente è simile al seguente:La proiezione rende più lenta la query

{ 
    "_id" : ObjectId, 
    "password" : String, 
    "email" : String, 
    "location" : Object, 
    "followers" : Array, 
    "following" : Array, 
    "dateCreated" : Number, 
    "loginCount" : Number, 
    "settings" : Object, 
    "roles" : Array, 
    "enabled" : Boolean, 
    "name" : Object 
} 

seguente query:

db.users.find(
    {}, 
    { 
     name:1, 
     settings:1, 
     email:1, 
     location:1 
    } 
).skip(656784).limit(10).explain() 

risultati in questo:

{ 
    "cursor" : "BasicCursor", 
    "isMultiKey" : false, 
    "n" : 10, 
    "nscannedObjects" : 656794, 
    "nscanned" : 656794, 
    "nscannedObjectsAllPlans" : 656794, 
    "nscannedAllPlans" : 656794, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 5131, 
    "nChunkSkips" : 0, 
    "millis" : 1106, 
    "server" : "shreyance:27017", 
    "filterSet" : false 
} 

e dopo aver rimosso la proiezione stessa query db.users.find().skip(656784).limit(10).explain()

risultati in questo :

{ 
    "cursor" : "BasicCursor", 
    "isMultiKey" : false, 
    "n" : 10, 
    "nscannedObjects" : 656794, 
    "nscanned" : 656794, 
    "nscannedObjectsAllPlans" : 656794, 
    "nscannedAllPlans" : 656794, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 5131, 
    "nChunkSkips" : 0, 
    "millis" : 209, 
    "server" : "shreyance:27017", 
    "filterSet" : false 
} 

Per quanto ne so, la proiezione aumenta sempre le prestazioni di una query. Quindi non sono in grado di capire perché MongoDB si comporta in questo modo. Qualcuno può spiegarlo. E quando usare la proiezione e quando no. E come effettivamente la proiezione è implementata in MongoDB.

+1

Ricevi questi risultati ripetutamente? La seconda query potrebbe essere più veloce solo perché i dati vengono memorizzati nella cache (caricati in memoria) dalla prima query. – Messa

+0

Poiché applica la proiezione ai documenti 656794 – Sammaye

+0

ma perché applica la proiezione ai documenti 656794 è un'implementazione molto errata. Ho bisogno solo di 10 dischi, quindi la proiezione dovrebbe essere applicata solo a 10 documenti –

risposta

4

Sei corretto che la proiezione rallenta questa query di salto in MongoDB 2.6.3. Ciò è correlato a un problema di ottimizzazione con il pianificatore di query 2.6 monitorato come SERVER-13946.

Il pianificatore di query 2.6 (come in 2.6.3) sta aggiungendo fasi SKIP (e LIMIT) dopo l'analisi della proiezione, quindi la proiezione viene inutilmente applicata ai risultati che vengono espulsi durante il salto per questa query. Ho provato una query simile in MongoDB 2.4.10 e lo nScannedObjects era uguale al numero di risultati restituiti dal mio limit anziché da skip + limit.

Ci sono diversi fattori che contribuiscono al prestazioni delle query:

1) Non hai specificato alcun criterio di query ({}), in modo da questa query sta facendo una scansione collezione natural order piuttosto che utilizzare un indice.

2) La query non può essere coperta perché non c'è alcuna proiezione.

3) Il valore skip estremamente largo è 656.784.

C'è sicuramente spazio per migliorare il piano di query, ma non mi aspetto che i valori di salto di questa grandezza siano ragionevoli nell'uso normale. Ad esempio, se si trattasse di una query dell'applicazione per l'impaginazione con 50 risultati per pagina, il valore skip() corrisponderebbe al numero di pagina 13.135.

+1

In effetti questo skip non è fattibile ma è una buona scoperta, si spera che questo venga risolto presto perché può significare che anche i piccoli salti devono fare più lavoro del necessario – Sammaye

1

A meno che il risultato della vostra proiezione fa qualcosa per la produzione di una query "unico indice", e ciò significa che solo i campi "proiettati" nel risultato sono tutto presenti nell'indice solo, allora siete sempre producendo altro funziona per il motore di query.

Devi considerare il processo:

  1. Come posso abbinare? Su documento o indice? Trova l'indice primario o altro appropriato.

  2. Dato l'indice, scansiona e trova le cose.

  3. Ora, cosa devo restituire? all ' dei dati nell'indice? Se non torni alla raccolta e tiri i documenti.

Questa è la procedura di base. Quindi, a meno che uno di questi stadi non "ottimizzi" in alcun modo, ovviamente le cose "richiedono più tempo".

È necessario considerare questo come progettare un "server engine" e comprendere i passaggi che è necessario intraprendere. Considerando che nessuna delle tue condizioni abbia soddisfatto nulla che possa produrre "ottimale" nei passaggi specificati devi imparare ad accettarlo.

Il tuo caso "migliore", è dove solo i campi proiettati sono i campi presenti nell'indice scelto. Ma in realtà, anche questo ha il sovraccarico di caricare l'indice.

Quindi scegliere con saggezza e comprendere i vincoli ei requisiti di memoria per ciò che si sta scrivendo la nostra query. Questo è ciò che significa "ottimizzazione".

+2

Non penso che questo spieghi perché MongoDB sembra che stia proiettando per i 656784 documenti che può contare con un indice _id – Sammaye

+1

@NeilLunn ho capito il tuo punto. Ma sono d'accordo con Sammaye anche "perché mongo sta applicando la proiezione su ogni documento" dovrebbe applicare la proiezione solo ai documenti di ritorno –

+0

@ShreyanceJain La spiegazione ** più lunga ** di ciò che "proiezione" effettivamente è nel contesto di dove questo non è " solo indice "e quindi" prelievo "dai campi dell'indice richiede effettivamente una comprensione dell'operatore' $ project' dalla pipeline del framework di aggregazione e la comprensione dell'elaborazione del motore di query. In sostanza, altrimenti stai chiedendo di "passare" tutti i tuoi documenti e "rimodellarli". Ha bisogno di ulteriori spiegazioni? –

Problemi correlati