Felsöka 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 brett 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 Portal. När du kör frågan i Data Explorer visas frågemåtten bredvid fliken Resultat:

Hämta frågemått

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 behövde utföra en genomsökning.

Läs 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 antalet 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 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 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 JOINoch de flesta systemfunktioner. De önskade sökvägarna som anges i indexprincipen ska matcha egenskaperna i JSON-dokumenten.

Anteckning

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:

Ursprunglig

Fråga:

SELECT *
FROM c
WHERE c.description = "Malabar spinach, cooked"

Indexeringsprincip:

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/description/*"
        }
    ]
}

RU-avgift: 409,51 RU:er

Optimerad

Uppdaterad indexeringsprincip:

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": []
}

RU-avgift: 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 förloppet för indextransformering.

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 dra 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änsligt = sant)
  • Innehåller
  • RegexMatch
  • EndsWith

Tänk till exempel på frågan nedan med CONTAINS. CONTAINS kommer att använda index, men ibland, även efter att du har lagt till relevant index, kan du fortfarande observera 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. Detta 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 är 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 referera 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 för en aggregeringsfunktion ensam. I det här exemplet måste frågemotorn läsa in alla dokument som matchar c.foodGroup = "Sausages and Luncheon Meats" filtret så att RU-avgiften förväntas bli 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 :

Ursprunglig

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.

Överväg 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 objekt med taggar, näringsämnen och serveringsmatriser för varje matchande dokument innan något filter tillämpas. Satsen WHERE 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 1 x 10 x 10 x 10 (det vill säga 1 000) tupplar. 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 näringsämnena och serveringsmatriserna. 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 behovet av begärandeenhet och datalagring ö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 som lagras (mer än cirka 100 GB) har du förmodligen en tillräckligt stor container för att se en betydande minskning av ru-avgifterna för frågor.

Om du till exempel skapar en container med partitionsnyckeln foodGroup behöver följande frågor bara 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 bara de relevanta fysiska partitionerna och kommer inte att "dra 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 på 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. Filtret för icke-likhet måste definieras sist i det sammansatta indexet. Läs mer om sammansatta index.

Här är 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 Portal.

Ö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 minimivärdet (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 det här 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ämtar körningsstatistik för att finjustera dina frågor med mera: