Felsök problem med frågor när du använder Azure Cosmos DB
GÄLLER FÖR: NoSQL
Den här artikeln går igenom en allmän metod som rekommenderas för felsökning av frågor i Azure Cosmos DB. Även om du inte bör betrakta stegen som beskrivs i den här artikeln som ett fullständigt skydd mot potentiella frågeproblem har vi tagit med de vanligaste prestandatipsen här. Du bör använda den här artikeln som utgångspunkt för felsökning av långsamma eller dyra frågor i Azure Cosmos DB för NoSQL. Du kan också använda diagnostikloggarna till att identifiera vilka frågor som är långsamma eller som förbrukar stora mängder dataflöde. Om du använder Azure Cosmos DB:s API för MongoDB bör du använda felsökningsguiden för Azure Cosmos DB:s API för MongoDB-frågor
Frågeoptimeringar i Azure Cosmos DB kategoriseras i stort sett enligt följande:
- Optimeringar som minskar RU-avgiften (Request Unit) för frågan
- Optimeringar som bara minskar svarstiden
Om du minskar RU-avgiften för en fråga minskar du vanligtvis även svarstiden.
Den här artikeln innehåller exempel som du kan återskapa med hjälp av näringsdatauppsättningen.
Vanliga SDK-problem
Innan du läser den här guiden är det bra att överväga vanliga SDK-problem som inte är relaterade till frågemotorn.
- Följ dessa SDK-prestandatips för frågor.
- Ibland kan frågor ha tomma sidor även om det finns resultat på en framtida sida. Orsaker till detta kan vara:
- SDK:n kan utföra flera nätverksanrop.
- Frågan kan ta lång tid att hämta dokumenten.
- Alla frågor har en fortsättningstoken som gör att frågan kan fortsätta. Se till att tömma frågan helt. Läs mer om att hantera flera sidor med resultat
Hämta frågans mätvärden
När du optimerar en fråga i Azure Cosmos DB är det första steget alltid att hämta frågemåtten för din fråga. Dessa mått är också tillgängliga via Azure-portalen. När du kör frågan i Datautforskaren visas frågemåtten bredvid fliken Resultat :
När du har samlat in frågemåtten jämför du Antalet hämtade dokument med Antalet utdatadokument för din fråga. Använd den här jämförelsen för att identifiera relevanta avsnitt som ska granskas i den här artikeln.
Antalet hämtade dokument är det antal dokument som frågemotorn behövde läsa in. Antal utdatadokument är det antal dokument som behövdes för resultatet av frågan. Om antalet hämtade dokument är betydligt högre än antalet utdatadokument fanns det minst en del av frågan som inte kunde använda ett index och som behövde utföra en genomsökning.
Se följande avsnitt för att förstå relevanta frågeoptimeringar för ditt scenario.
Frågans RU-avgift är för hög
Antalet hämtade dokument är betydligt högre än antalet utdatadokument
Antal hämtade dokument är ungefär lika med antal utdatadokument
Frågans RU-avgift är acceptabel men svarstiden är fortfarande för hög
Frågor där antalet hämtade dokument överskrider antalet utdatadokument
Antalet hämtade dokument är det antal dokument som frågemotorn behövde läsa in. Antal utdatadokument är antalet dokument som returneras av frågan. Om antalet hämtade dokument är betydligt högre än antalet utdatadokument fanns det minst en del av frågan som inte kunde använda ett index och som behövde utföra en genomsökning.
Här är ett exempel på en genomsökningsfråga som inte helt hanteras av indexet:
Fråga:
SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"
Frågemått:
Retrieved Document Count : 60,951
Retrieved Document Size : 399,998,938 bytes
Output Document Count : 7
Output Document Size : 510 bytes
Index Utilization : 0.00 %
Total Query Execution Time : 4,500.34 milliseconds
Query Preparation Times
Query Compilation Time : 0.09 milliseconds
Logical Plan Build Time : 0.05 milliseconds
Physical Plan Build Time : 0.04 milliseconds
Query Optimization Time : 0.01 milliseconds
Index Lookup Time : 0.01 milliseconds
Document Load Time : 4,177.66 milliseconds
Runtime Execution Times
Query Engine Times : 322.16 milliseconds
System Function Execution Time : 85.74 milliseconds
User-defined Function Execution Time : 0.00 milliseconds
Document Write Time : 0.01 milliseconds
Client Side Metrics
Retry Count : 0
Request Charge : 4,059.95 RUs
Antalet hämtade dokument (60 951) är betydligt högre än antalet utdatadokument (7), vilket innebär att den här frågan resulterade i en dokumentgenomsökning. I det här fallet använder inte systemfunktionen UPPER() ett index.
Inkludera nödvändiga sökvägar i indexeringsprincipen
Indexeringsprincipen bör omfatta alla egenskaper som ingår i WHERE
satser, ORDER BY
satser JOIN
och de flesta systemfunktioner. De önskade sökvägarna som anges i indexprincipen ska matcha egenskaperna i JSON-dokumenten.
Kommentar
Egenskaper i Azure Cosmos DB-indexeringsprincipen är skiftlägeskänsliga
Om du kör följande enkla fråga på näringsdatauppsättningen ser du en mycket lägre RU-avgift när egenskapen i WHERE
-satsen indexeras:
Original
Fråga:
SELECT *
FROM c
WHERE c.description = "Malabar spinach, cooked"
Indexeringsprincip:
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": [
{
"path": "/description/*"
}
]
}
RU-kostnad: 409,51 RU:er
Optimerad
Uppdaterad indexeringsprincip:
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": []
}
RU-kostnad: 2,98 RU:er
Du kan lägga till egenskaper i indexeringsprincipen när som helst, utan att det påverkar skriv- eller lästillgängligheten. Du kan spåra indextransformeringens förlopp.
Förstå vilka systemfunktioner som använder indexet
De flesta systemfunktioner använder index. Här är en lista över några vanliga strängfunktioner som använder index:
- StartsWith
- Innehåller
- RegexMatch
- Vänster
- Delsträng – men bara om den första num_expr är 0
Här följer några vanliga systemfunktioner som inte använder indexet och som måste läsa in varje dokument när de används i en WHERE
sats:
Systemfunktion | Idéer för optimering |
---|---|
Övre/nedre | I stället för att använda systemfunktionen för att normalisera data för jämförelser normaliserar du höljet vid infogning. En fråga som SELECT * FROM c WHERE UPPER(c.name) = 'BOB' blir SELECT * FROM c WHERE c.name = 'BOB' . |
GetCurrentDateTime/GetCurrentTimestamp/GetCurrentTicks | Beräkna den aktuella tiden före frågekörningen och använd strängvärdet i WHERE -satsen. |
Matematiska funktioner (icke-aggregeringar) | Om du behöver beräkna ett värde ofta i frågan bör du överväga att lagra värdet som en egenskap i JSON-dokumentet. |
Dessa systemfunktioner kan använda index, förutom när de används i frågor med aggregeringar:
Systemfunktion | Idéer för optimering |
---|---|
Rumsliga systemfunktioner | Lagra frågeresultatet i en materialiserad vy i realtid |
När de SELECT
används i -satsen påverkar ineffektiva systemfunktioner inte hur frågor kan använda index.
Förbättra körningen av strängsystemfunktionen
För vissa systemfunktioner som använder index kan du förbättra frågekörningen genom att lägga till en ORDER BY
sats i frågan.
Mer specifikt kan alla systemfunktioner vars RU-debitering ökar när kardinaliteten för egenskapen ökar ha nytta av att ha ORDER BY
i frågan. De här frågorna utför en indexgenomsökning, så att få frågeresultaten sorterade kan göra frågan mer effektiv.
Den här optimeringen kan förbättra körningen för följande systemfunktioner:
- StartsWith (där skiftlägesokänsligt = sant)
- StringEquals (där skiftlägesokänslig = sant)
- Innehåller
- RegexMatch
- EndsWith
Överväg till exempel frågan nedan med CONTAINS
. CONTAINS
använder index, men ibland, även efter att du har lagt till relevant index, kan du fortfarande se en mycket hög RU-avgift när du kör frågan nedan.
Ursprunglig fråga:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
Du kan förbättra frågekörningen genom att lägga till ORDER BY
:
SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
ORDER BY c.town
Samma optimering kan hjälpa till i frågor med ytterligare filter. I det här fallet är det bäst att även lägga till egenskaper med likhetsfilter i ORDER BY
-satsen.
Ursprunglig fråga:
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
Du kan förbättra frågekörningen genom att lägga till ORDER BY
och ett sammansatt index för (c.name, c.town):
SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
ORDER BY c.name, c.town
Förstå vilka aggregeringsfrågor som använder indexet
I de flesta fall använder aggregerade systemfunktioner i Azure Cosmos DB indexet. Beroende på filtren eller ytterligare satser i en aggregerad fråga kan frågemotorn dock behöva läsa in ett stort antal dokument. Vanligtvis tillämpar frågemotorn likhets- och intervallfilter först. När du har tillämpat dessa filter kan frågemotorn utvärdera ytterligare filter och använda för att läsa in återstående dokument för att beräkna aggregeringen om det behövs.
Med tanke på dessa två exempelfrågor är frågan med både ett likhets- och CONTAINS
systemfunktionsfilter vanligtvis effektivare än en fråga med bara ett CONTAINS
systemfunktionsfilter. Det beror på att likhetsfiltret tillämpas först och använder indexet innan dokument måste läsas in för det dyrare CONTAINS
filtret.
Fråga med endast CONTAINS
filter – högre RU-avgift:
SELECT COUNT(1)
FROM c
WHERE CONTAINS(c.description, "spinach")
Fråga med både likhetsfilter och CONTAINS
filter – lägre RU-avgift:
SELECT AVG(c._ts)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats" AND CONTAINS(c.description, "spinach")
Här följer ytterligare exempel på aggregerade frågor som inte kommer att använda indexet fullt ut:
Frågor med systemfunktioner som inte använder indexet
Du bör gå till den relevanta systemfunktionens sida för att se om den använder indexet.
SELECT MAX(c._ts)
FROM c
WHERE CONTAINS(c.description, "spinach")
Aggregera frågor med användardefinierade funktioner (UDF:er)
SELECT AVG(c._ts)
FROM c
WHERE udf.MyUDF("Sausages and Luncheon Meats")
Frågor med GROUP BY
RU-avgiften för frågor med GROUP BY
ökar när kardinaliteten för egenskaperna i GROUP BY
-satsen ökar. I frågan nedan ökar till exempel RU-avgiften för frågan när antalet unika beskrivningar ökar.
RU-avgiften för en aggregeringsfunktion med en GROUP BY
sats är högre än RU-avgiften enbart för en aggregerad funktion. I det här exemplet måste frågemotorn läsa in varje dokument som matchar c.foodGroup = "Sausages and Luncheon Meats"
filtret så att RU-avgiften förväntas vara hög.
SELECT COUNT(1)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats"
GROUP BY c.description
Om du planerar att köra samma mängdfrågor ofta kan det vara mer effektivt att skapa en materialiserad vy i realtid med Azure Cosmos DB-ändringsflödet än att köra enskilda frågor.
Optimera frågor som har både ett filter och en ORDER BY-sats
Även om frågor som har ett filter och en ORDER BY
sats normalt använder ett intervallindex, blir de mer effektiva om de kan hanteras från ett sammansatt index. Förutom att ändra indexeringsprincipen bör du lägga till alla egenskaper i det sammansatta indexet i ORDER BY
-satsen. Den här ändringen av frågan säkerställer att den använder det sammansatta indexet. Du kan se effekten genom att köra en fråga på näringsdatauppsättningen:
Original
Fråga:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c._ts ASC
Indexeringsprincip:
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[]
}
RU-avgift: 44,28 RU:er
Optimerad
Uppdaterad fråga (innehåller båda egenskaperna i ORDER BY
-satsen):
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c.foodGroup, c._ts ASC
Uppdaterad indexeringsprincip:
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[],
"compositeIndexes":[
[
{
"path":"/foodGroup",
"order":"ascending"
},
{
"path":"/_ts",
"order":"ascending"
}
]
]
}
RU-avgift: 8,86 RU:er
Optimera JOIN-uttryck med hjälp av en underfråga
Underfrågor med flera värden kan optimera JOIN
uttryck genom att push-överföra predikat efter varje select-many-uttryck i stället för alla korskopplingar i WHERE
-satsen.
Tänk på den här frågan:
SELECT Count(1) AS Count
FROM c
JOIN t IN c.tags
JOIN n IN c.nutrients
JOIN s IN c.servings
WHERE t.name = 'infant formula' AND (n.nutritionValue > 0
AND n.nutritionValue < 10) AND s.amount > 1
RU-avgift: 167,62 RU:er
För den här frågan matchar indexet alla dokument som har en tagg med namnet infant formula
, nutritionValue
större än 0 och amount
större än 1. Uttrycket JOIN
här utför korsprodukten av alla taggar, näringsämnen och serveringsmatriser för varje matchande dokument innan något filter tillämpas. WHERE
Satsen tillämpar sedan filterpredikatet på varje <c, t, n, s>
tuppeln.
Om ett matchande dokument till exempel har 10 objekt i var och en av de tre matriserna expanderas det till tupplar på 1 x 10 x 10 x 10 (det vill säga 1 000). Användningen av underfrågor här kan hjälpa dig att filtrera bort kopplade matrisobjekt innan du ansluter till nästa uttryck.
Den här frågan motsvarar den föregående, men använder underfrågor:
SELECT Count(1) AS Count
FROM c
JOIN (SELECT VALUE t FROM t IN c.tags WHERE t.name = 'infant formula')
JOIN (SELECT VALUE n FROM n IN c.nutrients WHERE n.nutritionValue > 0 AND n.nutritionValue < 10)
JOIN (SELECT VALUE s FROM s IN c.servings WHERE s.amount > 1)
RU-avgift: 22,17 RU:er
Anta att endast ett objekt i taggarnas matris matchar filtret och att det finns fem objekt för både matriserna för näringsämnen och portioner. Uttrycken JOIN
expanderas till 1 x 1 x 5 x 5 = 25 objekt, till skillnad från 1 000 objekt i den första frågan.
Frågor där antalet hämtade dokument är lika med antalet utdatadokument
Om antalet hämtade dokument är ungefär lika med antalet utdatadokument behövde frågemotorn inte skanna många onödiga dokument. För många frågor, som de som använder nyckelordet TOP
, kan antalet hämtade dokument överskrida antalet utdatadokument med 1. Du behöver inte oroa dig för detta.
Minimera frågor mellan partitioner
Azure Cosmos DB använder partitionering för att skala enskilda containrar när begärandeenheten och datalagringsbehoven ökar. Varje fysisk partition har ett separat och oberoende index. Om frågan har ett likhetsfilter som matchar containerns partitionsnyckel behöver du bara kontrollera den relevanta partitionens index. Den här optimeringen minskar det totala antalet RU:er som frågan kräver.
Om du har ett stort antal etablerade RU:er (mer än 30 000) eller en stor mängd data lagrade (mer än cirka 100 GB), har du förmodligen en tillräckligt stor container för att se en betydande minskning av ru-avgifter för frågor.
Om du till exempel skapar en container med partitionsnyckeln foodGroup behöver följande frågor endast kontrollera en enda fysisk partition:
SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
Frågor som har ett IN
filter med partitionsnyckeln kontrollerar endast relevanta fysiska partitioner och kommer inte att "fläkta ut":
SELECT *
FROM c
WHERE c.foodGroup IN("Soups, Sauces, and Gravies", "Vegetables and Vegetable Products") and c.description = "Mushroom, oyster, raw"
Frågor som har intervallfilter på partitionsnyckeln, eller som inte har några filter på partitionsnyckeln, måste "fläkta ut" och kontrollera varje fysisk partitions index för resultat:
SELECT *
FROM c
WHERE c.description = "Mushroom, oyster, raw"
SELECT *
FROM c
WHERE c.foodGroup > "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"
Optimera frågor som har filter för flera egenskaper
Även om frågor som har filter för flera egenskaper normalt använder ett intervallindex, blir de mer effektiva om de kan hanteras från ett sammansatt index. För små mängder data har den här optimeringen ingen betydande inverkan. Den kan dock vara användbar för stora mängder data. Du kan bara optimera högst ett icke-likhetsfilter per sammansatt index. Om frågan har flera icke-likhetsfilter väljer du ett av dem som ska använda det sammansatta indexet. Resten fortsätter att använda intervallindex. Icke-likhetsfiltret måste definieras sist i det sammansatta indexet. Läs mer om sammansatta index.
Här följer några exempel på frågor som kan optimeras med ett sammansatt index:
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts = 1575503264
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts > 1575503264
Här är det relevanta sammansatta indexet:
{
"automatic":true,
"indexingMode":"Consistent",
"includedPaths":[
{
"path":"/*"
}
],
"excludedPaths":[],
"compositeIndexes":[
[
{
"path":"/foodGroup",
"order":"ascending"
},
{
"path":"/_ts",
"order":"ascending"
}
]
]
}
Optimeringar som minskar frågefördröjningen
I många fall kan RU-avgiften vara acceptabel även om svarstiden för frågor är för lång. I följande avsnitt finns en översikt med tips om hur svarstiden för frågor kan minskas. Om du kör samma fråga flera gånger på samma datamängd får den vanligtvis samma RU-avgift varje gång. Frågesvarstiden kan dock variera mellan frågekörningar.
Förbättra närhet
Frågor som körs från en annan region än Azure Cosmos DB-kontot har högre svarstid än om de kördes i samma region. Om du till exempel kör kod på din stationära dator bör du förvänta dig att svarstiden blir tiotals eller hundratals millisekunder längre (eller mer) än om frågan kom från en virtuell dator i samma Azure-region som Azure Cosmos DB. Det är enkelt att distribuera data globalt i Azure Cosmos DB för att säkerställa att du kan föra dina data närmare din app.
Öka etablerat dataflöde
I Azure Cosmos DB mäts ditt etablerade dataflöde i enheter för programbegäran (RU:er). Anta att du har en fråga som förbrukar 5 RU av dataflödet. Om du till exempel etablerar 1 000 RU skulle du kunna köra frågan 200 gånger per sekund. Om du försökte köra frågan när det inte fanns tillräckligt med dataflöde tillgängligt skulle Azure Cosmos DB returnera ett HTTP 429-fel. Något av det aktuella API:et för NoSQL SDK:er försöker automatiskt igen efter en kort stund. Begränsade begäranden tar längre tid, så att öka det etablerade dataflödet kan ge kortare svarstid för frågor. Du kan se det totala antalet begränsade begäranden på bladet Mått i Azure-portalen.
Öka MaxConcurrency
Parallella frågor fungerar genom att köra frågor mot flera partitioner parallellt. Men data från en enskild partitionerad samling hämtas seriellt med avseende på frågan. Så om du anger MaxConcurrency till antalet partitioner har du den bästa chansen att uppnå den mest högpresterande frågan, förutsatt att alla andra systemvillkor förblir desamma. Om du inte känner till antalet partitioner kan du ange MaxConcurrency (eller MaxDegreesOfParallelism i äldre SDK-versioner) till ett högt tal. Systemet väljer minimum (antal partitioner, användarindata) som maximal grad av parallellitet.
Öka MaxBufferedItemCount
Frågor är utformade för att hämta resultat i förväg medan den aktuella batchen med resultat bearbetas av klienten. Förhämtning hjälper till att förbättra den övergripande svarstiden för en fråga. Om du anger MaxBufferedItemCount begränsas antalet förhämtningsresultat. Om du anger det här värdet till det förväntade antalet returnerade resultat (eller ett högre tal) kan frågan få mest nytta av förhämtning. Om du anger värdet till -1 avgör systemet automatiskt antalet objekt som ska buffras.
Nästa steg
I följande artiklar finns information om hur du mäter RU:er per fråga, hämta körningsstatistik för att justera dina frågor med mera: