Query-problemen oplossen bij het gebruik van de Azure Cosmos DB voor MongoDB
VAN TOEPASSING OP: MongoDB
In dit artikel wordt een algemene aanbevolen aanpak beschreven voor het oplossen van problemen met query's in Azure Cosmos DB. Hoewel u niet rekening moet houden met de stappen die in dit artikel worden beschreven, moet u een volledige verdediging tegen potentiële queryproblemen overwegen, maar we hebben hier de meest voorkomende tips voor prestaties opgenomen. Gebruik dit artikel als uitgangspunt voor het oplossen van problemen met trage of dure query's in de API van Azure Cosmos DB voor MongoDB. Als u azure Cosmos DB for NoSQL gebruikt, raadpleegt u het artikel over het oplossen van problemen met API voor NoSQL-query's.
Queryoptimalisaties in Azure Cosmos DB worden globaal als volgt gecategoriseerd:
- Optimalisaties die de ru-kosten (Request Unit) van de query verminderen
- Optimalisaties die alleen latentie verminderen
Als u de RU-kosten van een query verlaagt, vermindert u doorgaans ook de latentie.
Notitie
In dit artikel wordt ervan uitgegaan dat u de API van Azure Cosmos DB gebruikt voor MongoDB-accounts met versie 3.6 en hoger. Sommige query's die slecht presteren in versie 3.2, hebben aanzienlijke verbeteringen in versie 3.6+. Voer een upgrade uit naar versie 3.6 door een ondersteuningsaanvraag in te dienen.
Gebruik $explain opdracht om metrische gegevens op te halen
Wanneer u een query optimaliseert in Azure Cosmos DB, is de eerste stap altijd het verkrijgen van de RU-kosten voor uw query. Als ruwe richtlijn moet u manieren verkennen om de RU-kosten voor query's met meer dan 50 RU's te verlagen.
Naast het verkrijgen van de RU-kosten moet u de $explain
-opdracht gebruiken om de metrische gegevens over het query- en indexgebruik te verkrijgen. Hier volgt een voorbeeld waarin een query wordt uitgevoerd en de $explain
opdracht wordt gebruikt om metrische gegevens over het gebruik van query's en indexen weer te geven:
$explain opdracht:
db.coll.find({foodGroup: "Baby Foods"}).explain({"executionStatistics": true })
Uitvoer:
{
"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
}
De uitvoer van de $explain
opdracht is lang en bevat gedetailleerde informatie over de uitvoering van query's. Over het algemeen zijn er echter enkele secties waarin u zich moet richten bij het optimaliseren van prestaties van query's:
Metrisch | Beschrijving |
---|---|
timeInclusiveMS |
Latentie van back-endquery |
pathsIndexed |
Geeft indexen weer die door de query worden gebruikt |
pathsNotIndexed |
Geeft indexen weer die de query zou kunnen hebben gebruikt, indien beschikbaar |
shardInformation |
Samenvatting van queryprestaties voor een bepaalde fysieke partitie |
retrievedDocumentCount |
Aantal documenten dat door de query-engine is geladen |
outputDocumentCount |
Aantal documenten dat wordt geretourneerd in de queryresultaten |
estimatedDelayFromRateLimitingInMilliseconds |
Geschatte extra querylatentie vanwege snelheidsbeperking |
Nadat u de metrische querygegevens hebt weergegeven, vergelijkt u de retrievedDocumentCount
metrische outputDocumentCount
gegevens voor de query. Gebruik deze vergelijking om de relevante secties in dit artikel die u wilt bekijken te bepalen. Het retrievedDocumentCount
is het aantal documenten dat de query-engine moet laden. Het outputDocumentCount
is het aantal documenten dat nodig was voor de resultaten van de query. Als de query retrievedDocumentCount
aanzienlijk hoger is dan de outputDocumentCount
, was er ten minste één deel van uw query die geen index kon gebruiken en die nodig was om een scan uit te voeren.
Raadpleeg de volgende secties voor meer informatie over de relevante queryoptimalisaties voor uw scenario.
Ru-kosten van query's zijn te hoog
Het aantal opgehaalde documenten is aanzienlijk hoger dan het aantal uitgevoerde documenten
Het opgehaalde aantal documenten is ongeveer gelijk aan het aantal uitvoerdocument
Ru-kosten van query's zijn acceptabel, maar latentie is nog steeds te hoog
Query's waarbij het aantal opgehaalde documenten groter is dan het aantal uitgevoerde documenten
Het retrievedDocumentCount
is het aantal documenten dat de query-engine nodig heeft om te laden. Het outputDocumentCount
is het aantal documenten dat door de query wordt geretourneerd. Als de query retrievedDocumentCount
aanzienlijk hoger is dan de outputDocumentCount
, was er ten minste één deel van uw query die geen index kon gebruiken en die nodig was om een scan uit te voeren.
Hier volgt een voorbeeld van een scanquery die niet volledig wordt geleverd door de index:
$explain opdracht:
db.coll.find(
{
$and : [
{ "foodGroup" : "Cereal Grains and Pasta"},
{ "description" : "Oat bran, cooked"}
]
}
).explain({"executionStatistics": true })
Uitvoer:
{
"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
}
De retrievedDocumentCount
(8618) is aanzienlijk hoger dan ( outputDocumentCount
1), wat impliceert dat deze query een documentscan vereist.
Vereiste indexen opnemen
Controleer de pathsNotIndexed
matrix en voeg deze indexen toe. In dit voorbeeld moeten de paden foodGroup
description
worden geïndexeerd.
"pathsNotIndexed" : [
"foodGroup",
"description"
]
Het indexeren van best practices in de API van Azure Cosmos DB voor MongoDB verschilt van MongoDB. In de API van Azure Cosmos DB voor MongoDB worden samengestelde indexen alleen gebruikt in query's die efficiënt moeten worden gesorteerd op meerdere eigenschappen. Als u query's hebt met filters op meerdere eigenschappen, moet u voor elk van deze eigenschappen enkelvoudige veldindexen maken. Querypredicaten kunnen meerdere indexen voor één veld gebruiken.
Jokertekenindexen kunnen het indexeren vereenvoudigen. In tegenstelling tot in MongoDB kunnen jokertekenindexen meerdere velden in querypredicaten ondersteunen. Er is geen verschil in queryprestaties als u één jokertekenindex gebruikt in plaats van een afzonderlijke index te maken voor elke eigenschap. Het toevoegen van een jokertekenindex voor alle eigenschappen is de eenvoudigste manier om al uw query's te optimaliseren.
U kunt op elk gewenst moment nieuwe indexen toevoegen, zonder dat dit van invloed is op de beschikbaarheid van schrijf- of leesbewerkingen. U kunt de voortgang van de indextransformatie bijhouden.
Begrijpen welke aggregatiebewerkingen gebruikmaken van de index
In de meeste gevallen gebruiken aggregatiebewerkingen in de API van Azure Cosmos DB voor MongoDB gedeeltelijk indexen. Normaal gesproken past de query-engine eerst gelijkheids- en bereikfilters toe en gebruikt u indexen. Nadat u deze filters hebt toegepast, kan de query-engine aanvullende filters evalueren en eventueel andere documenten laden om de statistische functie te berekenen.
Hier volgt een voorbeeld:
db.coll.aggregate( [
{ $match: { foodGroup: 'Fruits and Fruit Juices' } },
{
$group: {
_id: "$foodGroup",
total: { $max: "$version" }
}
}
] )
In dit geval kunnen indexen de $match
fase optimaliseren. Als u een index foodGroup
toevoegt, worden de queryprestaties aanzienlijk verbeterd. Net als in MongoDB moet u zo vroeg mogelijk in de aggregatiepijplijn plaatsen $match
om het gebruik van indexen te maximaliseren.
In de API van Azure Cosmos DB voor MongoDB worden indexen niet gebruikt voor de werkelijke aggregatie, in dit geval $max
. Als u een index version
toevoegt, worden de queryprestaties niet verbeterd.
Query's waarbij het opgehaalde aantal documenten gelijk is aan het aantal uitvoerdocument
Als de retrievedDocumentCount
query-engine ongeveer gelijk is aan de outputDocumentCount
, hoeft de query-engine niet veel onnodige documenten te scannen.
Minimaliseer partitieoverkoepelende query's
Azure Cosmos DB maakt gebruik van partitionering om afzonderlijke containers te schalen naarmate de aanvraageenheid en gegevensopslag toeneemt. Elke fysieke partitie heeft een afzonderlijke en onafhankelijke index. Als je query een gelijkheidsfilter heeft dat overeenkomt met de partitiesleutel van je container, hoef je alleen de index van de relevante partitie te controleren. Deze optimalisatie vermindert het totale aantal RU's dat nodig is voor de query. Meer informatie over de verschillen tussen query's in partities en query's tussen partities.
Als u een groot aantal ingerichte RU's (meer dan 30.000) of een grote hoeveelheid gegevens hebt opgeslagen (meer dan ongeveer 100 GB), hebt u waarschijnlijk een grote container om een aanzienlijke vermindering van de query-RU-kosten te zien.
U kunt de shardInformation
matrix controleren om inzicht te hebben in de metrische querygegevens voor elke afzonderlijke fysieke partitie. Het aantal unieke shardKeyRangeId
waarden is het aantal fysieke partities waar de query moet worden uitgevoerd. In dit voorbeeld is de query uitgevoerd op vier fysieke partities. Het is belangrijk om te begrijpen dat de uitvoering volledig onafhankelijk is van het indexgebruik. Met andere woorden, query's voor meerdere partities kunnen nog steeds indexen gebruiken.
"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
}
]
Optimalisaties die de querylatentie verminderen
In veel gevallen kunnen de RU-kosten acceptabel zijn wanneer de querylatentie nog steeds te hoog is. De volgende secties geven een overzicht van tips voor het verminderen van querylatentie. Als u dezelfde query meerdere keren uitvoert op dezelfde gegevensset, worden er doorgaans steeds dezelfde RU-kosten in rekening gebracht. Maar querylatentie kan variëren tussen queryuitvoeringen.
Nabijheid verbeteren
Query's die worden uitgevoerd vanuit een andere regio dan het Azure Cosmos DB-account, hebben een hogere latentie dan wanneer ze in dezelfde regio zijn uitgevoerd. Als u bijvoorbeeld code uitvoert op uw computer, moet u verwachten dat de latentie tientallen of honderden milliseconden hoger (of meer) is dan als de query afkomstig is van een virtuele machine in dezelfde Azure-regio als Azure Cosmos DB. Het is eenvoudig om gegevens wereldwijd te distribueren in Azure Cosmos DB om ervoor te zorgen dat u uw gegevens dichter bij uw app kunt brengen.
Ingerichte doorvoer verhogen
In Azure Cosmos DB wordt uw ingerichte doorvoer gemeten in aanvraageenheden (RU's). Stel dat u een query heeft die 5 RU's aan doorvoer verbruikt. Als u bijvoorbeeld 1000 RU's inricht, kunt u die query 200 keer per seconde uitvoeren. Als u de query probeerde uit te voeren wanneer er onvoldoende doorvoer beschikbaar was, beperkt Azure Cosmos DB de aanvragen. Met de API van Azure Cosmos DB voor MongoDB wordt deze query automatisch opnieuw geprobeerd nadat er een korte tijd is gewacht. Beperkte aanvragen duren langer, zodat het verhogen van de ingerichte doorvoer de querylatentie kan verbeteren.
De waarde estimatedDelayFromRateLimitingInMilliseconds
geeft een beeld van de mogelijke latentievoordelen als u de doorvoer verhoogt.
Volgende stappen
- Problemen met queryprestaties oplossen (API voor NoSQL)
- Snelheidsbeperking voorkomen met SSR
- Indexering beheren voor de API voor MongoDB van Azure Cosmos DB
- Wilt u capaciteitsplanning uitvoeren voor een migratie naar Azure Cosmos DB? U kunt informatie over uw bestaande databasecluster gebruiken voor capaciteitsplanning.
- Als alles wat u weet het aantal vcores en servers in uw bestaande databasecluster is, leest u meer over het schatten van aanvraageenheden met behulp van vCores of vCPU's
- Als u typische aanvraagtarieven voor uw huidige databaseworkload kent, leest u meer over het schatten van aanvraageenheden met behulp van azure Cosmos DB-capaciteitsplanner