2012-10-24 2 views
7

Ho la seguente query che è un po 'come un campo di ricerca inversa:

db.ip_ranges.find({ $and: [{ start_ip_num: { $lte: 1204135028 } }, { end_ip_num: { $gt: 1204135028 } }] }) 

Quando viene eseguito con solo l'identificatore LTE $, la query restituisce subito. Ma quando corro con $ gt e $ lte nella stessa query, è estremamente lento (in secondi).

Sia i campi start_ip_num che end_ip_num sono indicizzati.

Come posso ottimizzare la ricerca?

EDIT

ricevo il seguente quando uso la funzione spiegare() sulla query:

{ 
    "cursor" : "BtreeCursor start_ip_num_1", 
    "nscanned" : 452336, 
    "nscannedObjects" : 452336, 
    "n" : 1, 
    "millis" : 2218, 
    "nYields" : 0, 
    "nChunkSkips" : 0, 
    "isMultiKey" : false, 
    "indexOnly" : false, 
    "indexBounds" : { 
     "start_ip_num" : [ 
      [ 
       -1.7976931348623157e+308, 
       1204135028 
      ] 
     ] 
    } 
} 

EDIT 2

Una volta ho aggiunto l'indice composto, il La funzione explain() restituisce quanto segue:

{ 
    "cursor" : "BtreeCursor start_ip_num_1_end_ip_num_1", 
    "nscanned" : 431776, 
    "nscannedObjects" : 1, 
    "n" : 1, 
    "millis" : 3433, 
    "nYields" : 0, 
    "nChunkSkips" : 0, 
    "isMultiKey" : false, 
    "indexOnly" : false, 
    "indexBounds" : { 
     "start_ip_num" : [ 
      [ 
       -1.7976931348623157e+308, 
       1204135028 
      ] 
     ], 
     "end_ip_num" : [ 
      [ 
       1204135028, 
       1.7976931348623157e+308 
      ] 
     ] 
    } 
} 

Tuttavia, il perf è ancora scarso (in secondi).

+0

Un '' .find ({...}). Explain() '' è un buon punto di partenza. Come chiede Wes Freeman, hai un indice su '' {start_ip_nm: 1, end_ip_num: 1} ''? – slee

+1

Una cosa che dovresti risolvere potrebbe aiutarti a usare un singolo oggetto selettore di query invece di usare '$ e'. 'db.ip_ranges.find ({start_ip_num: {$ lte: 1204135028}, end_ip_num: {$ gt: 1204135028}})' – JohnnyHK

+0

L'albero B deve scansionare> 400k voci per trovare l'unica corrispondenza. Prova la query sulla casella per vedere se questo aiuta. Scommetto che lo farai meno di un secondo. –

risposta

3

Quindi, le query a doppio intervallo sono sconsiderate in Mongo. Suppongo tu abbia un singolo indice contenente sia {start_ip_num: 1, end_ip_num: 1}.

Se questo non ti avvicina abbastanza (spesso è ancora lento se hai abbastanza dati restituiti dal primo campo, dato che deve fare molta scansione B-tree), c'è un trucco che puoi fare per combattere questo usando le domande di casella 2D (funziona solo per due intervalli alla volta).

Fondamentalmente, si inserisce un indice geografico 2D su un campo contenente i due punti in un array, come [start_ip, end_ip], e gli si dà un valore min/max alto in modo tale da non raggiungere i limiti che sono di default solo -180/180.

Infine, utilizzare una query sui limiti con l'intervallo che va dal valore minimo al valore $ lte su un angolo della casella e il valore gt e il valore massimo sull'altro angolo della casella. Vedere http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-BoundsQueries per la sintassi.

Sembra qualcosa di simile a questo:

db.ip_ranges.find({ip_range:{$within:{$box:[[0, 1204135028], [1204135028, max]]}}}); 

dove massima è la più grande IP si può avere.

È passato un po 'di tempo dall'ultima volta che ho visto questo, quindi il riquadro potrebbe essere sbagliato, ma il concetto è corretto e ha reso le query a doppia gamma un po' meglio rispetto a un normale indice B-tree a due campi . Coerentemente sotto un secondo (anche se di solito qualche centinaio di millisecondi), rispetto a pochi secondi con l'indice normale - penso di avere centinaia di milioni di documenti al momento, ma è passato un po 'di tempo quindi prendi questi parametri di riferimento con una grana di sale. I risultati varieranno in base ai dati e alle dimensioni della gamma, ne sono sicuro.

Aggiornamento: È possibile provare l'impostazione bits, provare un numero basso e un numero elevato per vedere se fa la differenza. Per me, non sembra influenzare le query in media. Vedi http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-CreatingtheIndex per la sintassi.

+0

sì, ho già due indici singoli sui campi start_ip_num e end_ip_num ... lasciatemi provare la soluzione ... grazie! – Bryan

+0

Provare prima con un indice composto, su entrambi i campi. Ricorda che mongo può usare solo un indice per query. Pubblica i tuoi risultati di explain() nella domanda, che potrebbe essere interessante. –

+0

Ho provato la query $ box. Funziona e torna indietro di 1 secondo. Questo è così strano Ho solo circa 1 mil di documenti. Sembra che le operazioni di portata come questa dovrebbero essere abbastanza dritte, ma Mongo non la sta gestendo affatto bene. – Bryan

0

Dopo una tonnellata di sperimentazione e ricerca, mi sono imbattuto in questo:

https://groups.google.com/forum/?fromgroups=#!topic/mongodb-user/IUwOzWsc0Sg

Sono in grado di ottenere la query giù intorno 200-300ms con questa query, E far cadere tutti gli indici (È necessario eliminare tutti gli indici per far funzionare tutto questo !!!):

db.ip_ranges.find ({start_ip_num: {$ LTE: 1.204.135,028 mila}, end_ip_num: {$ gt: 1.204.135,028 mila}}.) limite (1)

Non chiedermi il motivo. Non posso spiegarlo Se sei interessato, stavo costruendo il database GeoIP da MaxMind con MongoDB.

+0

Mongo non esegue correttamente le query a doppio intervallo. I tuoi risultati senza indici non rimarranno veloci su scala, ma se questo è solo un elenco GeoIP, probabilmente non crescerà molto. Inoltre, non hai mai menzionato che avevi solo bisogno di un risultato. Potresti semplicemente trovare findOne anziché .limit (1). Sono curioso di sapere perché è necessario cercare una gamma così ampia solo per trovare una singola gamma. Se dovessi sottrarre una quantità ragionevole dal tuo IP, (per raggiungere la parte inferiore/superiore di una rete o qualcosa del genere), probabilmente otterrai risultati molto più veloci, specificando un minimo/massimo per le query di intervallo. –

0

Il trucco è utilizzare $ lte e un ordinamento. Ho ottenuto la query fino a pochi ms.

Ho avuto lo stesso identico problema: trovare il blocco CIDR corrispondente a un particolare indirizzo IP. Ho anche provato a usare $ gte e $ lte e ricevevo 10 secondi di tempo di risposta.

Ho risolto il problema in un modo diverso. Notare che i blocchi CIDR (gli intervalli di indirizzi IP) nel database MaxMind non si sovrappongono. Al massimo ogni indirizzo IP corrisponde a un risultato. Quindi tutto ciò che devi fare è trovare il blocco CIDR con il più grande start_ip_num che sia inferiore al particolare indirizzo IP. Quindi verificare nel codice dell'applicazione che end_ip_num sia maggiore del particolare indirizzo IP.

Ecco il codice (utilizzando il nodo client MongoDB):

// Convert IP address to base 10. 
var ipToDecimal = function (ipAddress) { 
    var split = ipAddress.split('.'); 
    return (split[0] * 16777216) + (split[1] * 65536) + (split[2] * 256) + (+split[3]); 
}; 

var ipAddress = '1.2.3.4'; 
var ipDecimal = ipToDecimal(ipAddress); 

db.ip_addresses.find({start_ip_num: {$lte: ipDecimal}}, {_id: 0, country_code: 1, end_ip_num: 1}, {limit: 1, sort: [['start_ip_num', -1]]}).toArray(function (error, ipAddresses) { 
    if (ipAddresses[0] && ipAddresses[0]['end_ip_num'] >= ipDecimal) { 
    console.log('IP address found: ', ipAddresses[0]['country_code']); 
    } else { 
    console.log('IP address not found.'); 
    } 
}); 

Assicurarsi di creare un indice start_ip_num.

5

In base allo Ip2location website, è possibile ottenere query veloci su indirizzi IP con mongodb senza query di intervallo. Creare un solo indice su MongoDB { ip_to: 1 }, e interrogare l'ip con:

db.collection_name.find({ ip_to: { $gte : ip_integer } }).sort({ ip_end: 1 }).limit(1) 

Con questa configurazione ho avuto tempo di risposta di 1 ms con una collezione di 6 milioni di documenti.

+0

fantastico. questo ha funzionato per me. la query impiega circa 50 millisecondi in media con circa 3,3 milioni di documenti. btw, nel mio codice ho aggiunto un controllo per garantire che il numero di start_ip_num del documento restituito sia inferiore o uguale all'indirizzo IP richiesto. questo per garantire che ci sia effettivamente un documento con l'intervallo ip che soddisfi la controparte dell'intervallo dell'intervallo equivalente – Tanvir