Risolvere i problemi di query relativi all'uso di Azure Cosmos DB per MongoDB
SI APPLICA A: MongoDB
Questo articolo illustra un approccio generale consigliato per la risoluzione dei problemi relativi alle query in Azure Cosmos DB. I passaggi descritti in questo articolo non rappresentano una soluzione completa ai potenziali problemi di query, ma offrono suggerimenti per la soluzione degli errori più comuni relativi alle prestazioni. È consigliabile usare questo articolo come punto di partenza per la risoluzione dei problemi relativi a query lente o con costo elevato nell'API Azure Cosmos DB for MongoDB. Se si usa Azure Cosmos DB for NoSQL, vedere la guida alla risoluzione dei problemi delle query dell'API per NoSQL.
L'ottimizzazione delle query in Azure Cosmos DB può essere classificata in queste categorie principali:
- Ottimizzazioni che consentono di ridurre l'addebito dell'unità richiesta (UR) della query
- Ottimizzazioni che consentono di ridurre solo la latenza
Se si riduce l'addebito UR di una query, generalmente è possibile ridurre anche la latenza.
Nota
Questo articolo presuppone che si usino account dell'API Azure Cosmos DB for MongoDB versione 3.6 e successive. Alcune query con scarse prestazioni nella versione 3.2 presentano miglioramenti significativi nelle versioni 3.6 e successive. Eseguire l'aggiornamento alla versione 3.6 inviando una richiesta di supporto.
Usare il comando $explain per ottenere le metriche
Quando si ottimizza una query in Azure Cosmos DB, il primo passaggio consiste sempre nell'ottenere l'addebito delle UR per la query. Come linea guida approssimativa, è consigliabile esplorare i modi per ridurre l'addebito delle UR per le query con addebiti superiori a 50 UR.
Oltre a ottenere l'addebito delle UR, è necessario usare il comando $explain
per ottenere le metriche di utilizzo delle query e dell'indice. Di seguito è riportato un esempio che esegue una query e usa il comando $explain
per visualizzare le metriche di utilizzo della query e degli indici:
Comando $explain:
db.coll.find({foodGroup: "Baby Foods"}).explain({"executionStatistics": true })
Output:
{
"stages" : [
{
"stage" : "$query",
"timeInclusiveMS" : 905.2888,
"timeExclusiveMS" : 905.2888,
"in" : 362,
"out" : 362,
"details" : {
"database" : "db-test",
"collection" : "collection-test",
"query" : {
"foodGroup" : {
"$eq" : "Baby Foods"
}
},
"pathsIndexed" : [],
"pathsNotIndexed" : [
"foodGroup"
],
"shardInformation" : [
{
"activityId" : "e68e6bdd-5e89-4ec5-b053-3dbbc2428140",
"shardKeyRangeId" : "0",
"durationMS" : 788.5867,
"preemptions" : 1,
"outputDocumentCount" : 362,
"retrievedDocumentCount" : 8618
}
],
"queryMetrics" : {
"retrievedDocumentCount" : 8618,
"retrievedDocumentSizeBytes" : 104963042,
"outputDocumentCount" : 362,
"outputDocumentSizeBytes" : 2553535,
"indexHitRatio" : 0.0016802042237178,
"totalQueryExecutionTimeMS" : 777.72,
"queryPreparationTimes" : {
"queryCompilationTimeMS" : 0.19,
"logicalPlanBuildTimeMS" : 0.14,
"physicalPlanBuildTimeMS" : 0.09,
"queryOptimizationTimeMS" : 0.03
},
"indexLookupTimeMS" : 0,
"documentLoadTimeMS" : 687.22,
"vmExecutionTimeMS" : 774.09,
"runtimeExecutionTimes" : {
"queryEngineExecutionTimeMS" : 37.45,
"systemFunctionExecutionTimeMS" : 10.82,
"userDefinedFunctionExecutionTimeMS" : 0
},
"documentWriteTimeMS" : 49.42
}
}
}
],
"estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
"continuation" : {
"hasMore" : false
},
"ok" : 1.0
}
L'output del comando $explain
è esteso e contiene informazioni dettagliate sull'esecuzione della query. In generale, tuttavia, esistono alcune sezioni su cui è consigliabile concentrarsi quando si ottimizzano le prestazioni delle query:
Metrico | Descrizione |
---|---|
timeInclusiveMS |
Latenza di query back-end |
pathsIndexed |
Mostra gli indici usati dalla query |
pathsNotIndexed |
Mostra gli indici che la query avrebbe potuto usare, se disponibili |
shardInformation |
Riepilogo delle prestazioni della query per una particolare partizione fisica |
retrievedDocumentCount |
Numero di documenti caricati dal motore di query |
outputDocumentCount |
Numero di documenti restituiti nei risultati della query |
estimatedDelayFromRateLimitingInMilliseconds |
Latenza aggiuntiva stimata per la query a causa della limitazione della frequenza |
Dopo aver recuperate le metriche di query, confrontare il valore di retrievedDocumentCount
con quello di outputDocumentCount
per la query. Usare questo confronto per identificare le sezioni rilevanti da esaminare in questo articolo. Il valore di retrievedDocumentCount
corrisponde al numero di documenti che il motore di query deve caricare. Il valore di outputDocumentCount
corrisponde al numero di documenti che erano necessari per i risultati della query. Se il valore di retrievedDocumentCount
è notevolmente superiore a quello di outputDocumentCount
, significa che almeno una parte della query non ha potuto usare un indice e ha dovuto eseguire un'analisi.
Per informazioni sull'ottimizzazione delle query relative a ogni scenario, vedere le sezioni che seguono.
L'addebito UR della query è troppo elevato
Il conteggio dei documenti recuperati è significativamente superiore al conteggio dei documenti di output
Il conteggio documenti recuperati è approssimativamente uguale al conteggio documenti di output
L'addebito UR della query è accettabile, ma la latenza è ancora troppo elevata
Query in cui il conteggio dei documenti recuperati supera il conteggio documenti di output
Il valore di retrievedDocumentCount
corrisponde al numero di documenti che il motore di query doveva caricare. Il valore di outputDocumentCount
corrisponde al numero di documenti restituiti dalla query. Se il valore di retrievedDocumentCount
è notevolmente superiore a quello di outputDocumentCount
, significa che almeno una parte della query non ha potuto usare un indice e ha dovuto eseguire un'analisi.
Di seguito è riportato un esempio di una query di analisi che non è stata interamente gestita dall'indice:
Comando $explain:
db.coll.find(
{
$and : [
{ "foodGroup" : "Cereal Grains and Pasta"},
{ "description" : "Oat bran, cooked"}
]
}
).explain({"executionStatistics": true })
Output:
{
"stages" : [
{
"stage" : "$query",
"timeInclusiveMS" : 436.5716,
"timeExclusiveMS" : 436.5716,
"in" : 1,
"out" : 1,
"details" : {
"database" : "db-test",
"collection" : "indexing-test",
"query" : {
"$and" : [
{
"foodGroup" : {
"$eq" : "Cereal Grains and Pasta"
}
},
{
"description" : {
"$eq" : "Oat bran, cooked"
}
}
]
},
"pathsIndexed" : [],
"pathsNotIndexed" : [
"foodGroup",
"description"
],
"shardInformation" : [
{
"activityId" : "13a5977e-a10a-4329-b68e-87e4f0081cac",
"shardKeyRangeId" : "0",
"durationMS" : 435.4867,
"preemptions" : 1,
"outputDocumentCount" : 1,
"retrievedDocumentCount" : 8618
}
],
"queryMetrics" : {
"retrievedDocumentCount" : 8618,
"retrievedDocumentSizeBytes" : 104963042,
"outputDocumentCount" : 1,
"outputDocumentSizeBytes" : 6064,
"indexHitRatio" : 0.0,
"totalQueryExecutionTimeMS" : 433.64,
"queryPreparationTimes" : {
"queryCompilationTimeMS" : 0.12,
"logicalPlanBuildTimeMS" : 0.09,
"physicalPlanBuildTimeMS" : 0.1,
"queryOptimizationTimeMS" : 0.02
},
"indexLookupTimeMS" : 0,
"documentLoadTimeMS" : 387.44,
"vmExecutionTimeMS" : 432.93,
"runtimeExecutionTimes" : {
"queryEngineExecutionTimeMS" : 45.36,
"systemFunctionExecutionTimeMS" : 16.86,
"userDefinedFunctionExecutionTimeMS" : 0
},
"documentWriteTimeMS" : 0.13
}
}
}
],
"estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
"continuation" : {
"hasMore" : false
},
"ok" : 1.0
}
Il valore di retrievedDocumentCount
(8618) è notevolmente superiore a quello di outputDocumentCount
(1), il che significa che è stata necessaria un'analisi dei documenti.
Includere gli indici necessari
È necessario controllare la matrice pathsNotIndexed
e aggiungere questi indici. In questo esempio i percorsi foodGroup
e description
devono essere indicizzati.
"pathsNotIndexed" : [
"foodGroup",
"description"
]
Le procedure consigliate per l'indicizzazione nell'API Azure Cosmos DB for MongoDB sono diverse da quelle di MongoDB. Nell'API Azure Cosmos DB for MongoDB, gli indici composti vengono usati solo nelle query che devono essere ordinate in modo efficiente in base a più proprietà. Se si hanno query con filtri basati su più proprietà, è necessario creare indici a campo singolo per ognuna delle proprietà. I predicati di query possono usare più indici a campo singolo.
Gli indici con caratteri jolly possono semplificare l'indicizzazione. A differenza di MongoDB, gli indici con caratteri jolly possono supportare più campi nei predicati di query. Non ci sarà alcuna differenza nelle prestazioni delle query se si usa un singolo indice con caratteri jolly anziché creare un indice separato per ogni proprietà. L'aggiunta di un indice con caratteri jolly per tutte le proprietà è il modo più semplice per ottimizzare tutte le query.
È possibile aggiungere nuovi indici in qualsiasi momento, senza alcun effetto sulla disponibilità di scrittura o lettura. È possibile tenere traccia dell'avanzamento della trasformazione dell'indice.
Comprendere quali operazioni di aggregazione usano l'indice
Nella maggior parte dei casi, le operazioni di aggregazione nell'API Azure Cosmos DB for MongoDB usano gli indici solo parzialmente. In genere il motore di query applica prima i filtri di uguaglianza e di intervallo e dopo usa gli indici. Dopo aver applicato questi filtri, il motore di query può valutare filtri aggiuntivi e ricorrere al caricamento dei documenti rimanenti per calcolare l'aggregazione, se necessario.
Ecco un esempio:
db.coll.aggregate( [
{ $match: { foodGroup: 'Fruits and Fruit Juices' } },
{
$group: {
_id: "$foodGroup",
total: { $max: "$version" }
}
}
] )
In questo caso, gli indici possono ottimizzare la fase $match
. L'aggiunta di un indice per foodGroup
migliorerà in modo significativo le prestazioni della query. Come in MongoDB, è consigliabile posizionare $match
il più possibile all'inizio della pipeline di aggregazione per ottimizzare l'utilizzo degli indici.
Nell'API Azure Cosmos DB for MongoDB gli indici non vengono usati per l'aggregazione effettiva, che in questo caso è $max
. L'aggiunta di un indice in version
non migliorerà le prestazioni della query.
Query in cui il conteggio dei documenti recuperati è uguale al conteggio dei documenti di output
Se il valore di retrievedDocumentCount
è approssimativamente uguale a quello di outputDocumentCount
, significa che il motore di query non ha dovuto analizzare molti documenti non necessari.
Ridurre al minimo le query su più partizioni
Azure Cosmos DB usa il partizionamento per la scalabilità dei singoli contenitori quando si riscontra un aumento dell'UR e delle risorse di archiviazione dati necessarie. Ogni partizione fisica dispone di un indice distinto e indipendente. Se la query include un filtro di uguaglianza che corrisponde alla chiave di partizione del contenitore, è necessario controllare solo l'indice della partizione pertinente. Questa ottimizzazione consente di ridurre il numero totale di UR necessarie per la query. Altre informazioni sulle differenze tra query in partizione e query tra partizioni.
Se è presente un numero elevato di UR di cui è stato effettuato il provisioning (più di 30.000) o di una quantità elevata di dati archiviati (circa oltre 100 GB), è probabile che si disponga di un contenitore sufficientemente grande da consentire una riduzione significativa degli addebiti UR.
È possibile controllare la matrice shardInformation
per comprendere le metriche di query per ogni singola partizione fisica. Il numero di valori univoci di shardKeyRangeId
è il numero di partizioni fisiche in cui deve essere eseguita la query. In questo esempio la query è stata eseguita su quattro partizioni fisiche. È importante comprendere che l'esecuzione è completamente indipendente dall'utilizzo degli indici. In altre parole, le query tra partizioni possono comunque usare indici.
"shardInformation" : [
{
"activityId" : "42f670a8-a201-4c58-8023-363ac18d9e18",
"shardKeyRangeId" : "5",
"durationMS" : 24.3859,
"preemptions" : 1,
"outputDocumentCount" : 463,
"retrievedDocumentCount" : 463
},
{
"activityId" : "a8bf762a-37b9-4c07-8ed4-ae49961373c0",
"shardKeyRangeId" : "2",
"durationMS" : 35.8328,
"preemptions" : 1,
"outputDocumentCount" : 905,
"retrievedDocumentCount" : 905
},
{
"activityId" : "3754e36b-4258-49a6-8d4d-010555628395",
"shardKeyRangeId" : "1",
"durationMS" : 67.3969,
"preemptions" : 1,
"outputDocumentCount" : 1479,
"retrievedDocumentCount" : 1479
},
{
"activityId" : "a69a44ee-db97-4fe9-b489-3791f3d52878",
"shardKeyRangeId" : "0",
"durationMS" : 185.1523,
"preemptions" : 1,
"outputDocumentCount" : 867,
"retrievedDocumentCount" : 867
}
]
Ottimizzazioni che consentono di ridurre la latenza della query
In molti casi, l'addebito UR potrebbe essere accettabile quando la latenza della query è ancora troppo elevata. Le sezioni seguenti forniscono una panoramica dei suggerimenti per la riduzione della latenza delle query. Se la stessa query viene eseguita più volte nello stesso set di dati, lo stesso addebito UR viene generalmente applicato ogni volta. La latenza delle query potrebbe tuttavia variare tra le esecuzioni della query.
Migliorare la prossimità
Le query eseguite da un'area diversa da quella dell'account di Azure Cosmos DB avranno una latenza superiore rispetto a quando vengono eseguite all'interno della stessa area. Se, ad esempio, se si esegue il codice sul computer desktop, è prevedibile che la latenza sia superiore di decine o centinaia di millisecondi (o più) rispetto a una query eseguita su una macchina virtuale all'interno della stessa area di Azure come Azure Cosmos DB. La distribuzione a livello globale dei dati in Azure Cosmos DB è un'operazione semplice che consente di migliorare la prossimità dei dati all'app.
Aumentare la velocità effettiva sottoposta a provisioning
In Azure Cosmos DB la velocità effettiva con provisioning viene misurata in Unità Richieste (UR). Si supponga di disporre di una query che utilizza 5 UR di velocità effettiva. Se, ad esempio, si esegue il provisioning di 1.000 UR, sarà possibile eseguire la query 200 volte al secondo. Se si prova a eseguire la query quando la velocità effettiva disponibile non è sufficiente, Azure Cosmos DB limiterà la velocità delle richieste. L'API Azure Cosmos DB per MongoDB eseguirà automaticamente nuovi tentativi per questa query dopo un breve periodo di attesa. Le richieste limitate richiedono più tempo, quindi l'aumento della velocità effettiva con provisioning può diminuire la latenza delle query.
Il valore di estimatedDelayFromRateLimitingInMilliseconds
dà un'idea dei potenziali vantaggi in termini di latenza se si aumenta la velocità effettiva.
Passaggi successivi
- Risolvere i problemi di prestazioni delle query (API per NoSQL)
- Impedire la limitazione della frequenza con SSR
- Gestire l'indicizzazione nell'API di Azure Cosmos DB per MongoDB
- Si sta tentando di pianificare la capacità per una migrazione ad Azure Cosmos DB? È possibile usare le informazioni del cluster di database esistente per la pianificazione della capacità.
- Se si conosce solo il numero di vcore e server nel cluster di database esistente, leggere le informazioni sulla stima delle unità richieste usando vCore o vCPU
- Se si conosce la frequenza delle richieste tipiche per il carico di lavoro corrente del database, leggere le informazioni sulla stima delle unità richieste con lo strumento di pianificazione della capacità di Azure Cosmos DB