Prestandatips för Azure Cosmos DB och .NET SDK v2

GÄLLER FÖR: SQL API

Azure Cosmos DB är en snabb och flexibel distribuerad databas som skalar sömlöst med garanterad svarstid och dataflöde. Du behöver inte göra större arkitekturändringar eller skriva komplex kod för att skala databasen med Azure Cosmos DB. Det är lika enkelt att skala upp och ned som att göra ett enda API-anrop. Mer information finns i etablera containerdataflöde eller etablera databasdataflöde. Men eftersom Azure Cosmos DB nås via nätverkssamtal finns det optimeringar på klientsidan som du kan göra för att uppnå högsta prestanda när du använder .NET SDK SQL.

Om du försöker förbättra databasens prestanda bör du därför överväga följande alternativ:

Uppgradera till .NET V3 SDK

.NET v3 SDK släpps. Om du använder .NET v3 SDK finns följande information i prestandaguiden för .NET v3:

  • Standardvärdet är Direkt TCP-läge
  • Stream API-stöd
  • Stöd för anpassad serialiserare för att System.Text.JSPÅ-användning
  • Integrerat batch- och massstöd

Värdrekommendationer

För frågeintensiva arbetsbelastningar använder du Windows 64-bitars i stället för Linux Windows 32-bitars värdbearbetning

Vi rekommenderar Windows 64-bitars värdbearbetning för bättre prestanda. SDK:SQL innehåller en inbyggd ServiceInterop.dll för att parsa och optimera frågor lokalt. ServiceInterop.dll stöds endast på Windows x64-plattformen. För Linux och andra plattformar som inte stöds ServiceInterop.dll inte är tillgängliga görs ytterligare ett nätverkssamtal till gatewayen för att hämta den optimerade frågan. Följande typer av program använder 32-bitars värdbearbetning som standard. Om du vill ändra värdbearbetningen till 64-bitars bearbetning följer du dessa steg, baserat på typen av program:

  • För körbara program kan du ändra värdbearbetningen genom att ange plattformsmålet till x64 i fönstret Project Egenskaperfliken Skapa.

  • För VSTest-baserade testprojekt kan du ändra värdbearbetning genom att välja TestTest Inställningar Default Processor Architecture as X64 (Standardprocessorarkitektur för test som > > X64) Visual Studio testmenyn.

  • För lokalt distribuerade ASP.NET-webbprogram kan du ändra värdbearbetning genom att välja Använd 64-bitarsversionen av IIS Express för webbplatser och projekt under > VerktygSalternativ Projekt och lösningar > > Webbprojekt.

  • Om ASP.NET distribuerade webbappar i Azure kan du ändra värdbearbetningen genom att välja 64-bitarsplattformen i Programinställningar i Azure Portal.

Anteckning

Som standard är nya Visual Studio-projekt inställda på Valfri CPU. Vi rekommenderar att du ställer in projektet på x64 så att det inte växlar till x86. Ett projekt som är inställt på Valfri processor kan enkelt växla till x86 om ett beroende med endast x86 läggs till.
ServiceInterop.dll måste finnas i den mapp som SDK DLL-filen körs från. Detta bör bara vara ett problem om du kopierar DLL:er manuellt eller har anpassade build/deployment-system.

Aktivera skräpinsamling på serversidan (GC)

Att minska frekvensen för skräpinsamling kan hjälpa i vissa fall. I .NET anger du gcServer till true .

Skala ut klientarbetsbelastningen

Om du testar på höga dataflödesnivåer (mer än 50 000 RU/s) kan klientprogrammet bli flaskhalsen på grund av att datorn har ett tak för CPU- eller nätverksanvändning. Om du når den här punkten kan du fortsätta att skicka Azure Cosmos DB kontot ytterligare genom att skala ut klientprogrammen över flera servrar.

Anteckning

Hög CPU-användning kan orsaka längre svarstider och undantag för timeout för förfrågningar.

Nätverk

Anslutningsprincip: Använd direktanslutningsläge

Standardanslutningsläget för .NET V2 SDK är gateway. Du konfigurerar anslutningsläget under bygget av DocumentClient instansen med hjälp av ConnectionPolicy parametern . Om du använder direktläge måste du också ange med Protocol hjälp av ConnectionPolicy parametern . Mer information om olika anslutningsalternativ finns i artikeln om anslutningslägen.

Uri serviceEndpoint = new Uri("https://contoso.documents.net");
string authKey = "your authKey from the Azure portal";
DocumentClient client = new DocumentClient(serviceEndpoint, authKey,
new ConnectionPolicy
{
   ConnectionMode = ConnectionMode.Direct, // ConnectionMode.Gateway is the default
   ConnectionProtocol = Protocol.Tcp
});

Tillfällig portöverbelastning

Om du ser en hög anslutningsvolym eller hög portanvändning på dina instanser kontrollerar du först att dina klientinstanser är singletons. Med andra ord ska klientinstanserna vara unika under programmets livslängd.

När klienten körs på TCP-protokollet optimerar den för svarstid med hjälp av långlivade anslutningar i stället för HTTPS-protokollet, som avslutar anslutningarna efter 2 minuters inaktivitet.

I scenarier där du har gles åtkomst och om du märker ett högre antal anslutningar jämfört med åtkomst i gatewayläge kan du:

  • Konfigurera egenskapen ConnectionPolicy.PortReuseMode till PrivatePortPool (gäller ramverksversion>= 4.6.1 och .NET Core version >= 2.0): Med den här egenskapen kan SDK använda en liten pool med tillfälliga portar för olika Azure Cosmos DB-målslutpunkter.
  • Konfigurera egenskapen ConnectionPolicy.IdleConnectionTimeout måste vara större än eller lika med 10 minuter. De rekommenderade värdena är mellan 20 minuter och 24 timmar.

Anropa OpenAsync för att undvika startsvarstid vid första begäran

Som standard har den första begäran längre svarstid eftersom den måste hämta adressroutningstabellen. När du använder SDK V2 anropardu OpenAsync() en gång under initieringen för att undvika den här startfördröjningen på den första begäran. Anropet ser ut så här: await client.OpenAsync();

Anteckning

OpenAsync genererar begäranden för att hämta adressroutningstabellen för alla containrar i kontot. För konton som har många containrar men vars program har åtkomst till en delmängd av dem skulle det generera onödig trafik, vilket skulle göra OpenAsync initieringen långsam. Därför kanske OpenAsync det inte är användbart att använda i det här scenariot eftersom det gör programstarten långsammare.

För prestanda kan du samla klienter i samma Azure-region

När det är möjligt placerar du alla program Azure Cosmos DB i samma region som Azure Cosmos DB databasen. Här är en ungefärlig jämförelse: anrop till Azure Cosmos DB inom samma region slutförs inom 1 ms till 2 ms, men svarstiden mellan USA:s västra och östra kust är mer än 50 ms. Den här svarstiden kan variera från begäran till begäran, beroende på vilken väg begäran tar när den passerar från klienten till Azure-datacentergränsen. Du kan få kortast möjliga svarstid genom att se till att det anropande programmet finns i samma Azure-region som den etablerade Azure Cosmos DB slutpunkten. En lista över tillgängliga regioner finns i Azure-regioner.

Principen Azure Cosmos DB anslutning

Öka antalet trådar/uppgifter

Eftersom anrop till Azure Cosmos DB görs via nätverket kan du behöva variera graden av parallellitet för dina begäranden så att klientprogrammet ägnar minimal tid åt att vänta mellan begäranden. Om du till exempel använder .NET-aktivitetsparallellbiblioteket skapar du i den ordning som hundratals aktiviteter läser från eller skriver till Azure Cosmos DB.

Aktivera accelererat nätverk

För att minska svarstiden och CPU-jitter rekommenderar vi att du aktiverar accelererat nätverk på virtuella klientdatorer. Se Skapa en Windows virtuell dator med accelererat nätverk eller Skapa en virtuell Linux-dator med accelererat nätverk.

SDK-användning

Installera den senaste SDK:n

De Azure Cosmos DB-SDK:erna förbättras ständigt för att ge bästa möjliga prestanda. Se sidan Azure Cosmos DB SDK för att fastställa den senaste SDK:n och granska förbättringar.

Använda en singleton Azure Cosmos DB klient under hela programmets livslängd

Varje DocumentClient instans är trådsäker och utför effektiv anslutningshantering och cachelagring av adresser när den används i direktläge. För att möjliggöra effektiv anslutningshantering och bättre SDK-klientprestanda rekommenderar vi att du använder en enda instans per AppDomain under programmets livslängd.

Undvik att blockera anrop

Cosmos DB SDK bör utformas för att bearbeta många begäranden samtidigt. Asynkrona API:er gör att en liten pool med trådar kan hantera tusentals samtidiga begäranden genom att inte vänta på blockerande anrop. I stället för att vänta på att en långvarig synkron uppgift ska slutföras kan tråden arbeta med en annan begäran.

Ett vanligt prestandaproblem i appar som använder Cosmos DB SDK blockerar anrop som kan vara asynkrona. Många synkrona blockerande anrop leder till att trådpoolen blir utsvulten och att svarstiderna försämras.

Gör inte följande:

  • Blockera asynkron körning genom att anropa Task.Wait eller Task.Result.
  • Använd Task.Run för att göra ett synkront API asynkront.
  • Hämta lås i vanliga kodsökvägar. Cosmos DB .NET SDK presterar bäst när den är skapad för att köra kod parallellt.
  • Anropa Task.Run och vänta omedelbart på den. ASP.NET Core redan appkod på vanliga trådpooltrådar, så att anropa Task.Run resulterar bara i extra onödig schemaläggning av trådpool. Även om den schemalagda koden skulle blockera en tråd förhindrar Task.Run inte detta.
  • Använd inte ToList() som DocumentClient.CreateDocumentQuery(...) använder blockerande anrop för att synkront tömma frågan. Använd AsDocumentQuery() för att tömma frågan asynkront.

Gör så här:

En profilerare, till exempel PerfView,kan användas för att hitta trådar som ofta läggs till i trådpoolen. Händelsen Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start anger att en tråd har lagts till i trådpoolen.

Öka System.Net MaxConnections per värd när du använder gatewayläge

Azure Cosmos DB görs via HTTPS/REST när du använder gatewayläge. De utsätts för standardanslutningsgränsen per värdnamn eller IP-adress. Du kan behöva ange ett högre värde MaxConnections (100 till 1 000) så att klientbiblioteket kan använda flera samtidiga anslutningar för att Azure Cosmos DB. I .NET SDK 1.8.0 och senare är standardvärdet för ServicePointManager.DefaultConnectionLimit 50. Om du vill ändra värdet kan du ange Documents.Client.ConnectionPolicy.MaxConnectionLimit till ett högre värde.

Justera parallella frågor för partitionerade samlingar

SQL .NET SDK 1.9.0 och senare har stöd för parallella frågor, vilket gör att du kan köra frågor mot en partitionerad samling parallellt. Mer information finns i kodexempel som rör arbete med SDK:er. Parallella frågor är utformade för att ge bättre frågesvarstid och dataflöde än deras seriella motsvarighet. Parallella frågor innehåller två parametrar som du kan justera efter dina behov:

  • MaxDegreeOfParallelism styr det maximala antalet partitioner som kan efterfrågas parallellt.
  • MaxBufferedItemCount styr antalet förhämtningsresultat.

Justeringsgrad för parallellitet

Parallell fråga fungerar genom att fråga flera partitioner parallellt. Men data från en enskild partition hämtas seriellt med avseende på frågan. Inställningen i SDK V2 till antalet partitioner har den bästa chansen att uppnå den mest effektiva frågan, förutsatt att alla andra MaxDegreeOfParallelism systemvillkor förblir desamma. Om du inte vet antalet partitioner kan du ange graden av parallellitet till ett högt tal. Systemet väljer minimiantalet (antal partitioner, användarindata) som grad av parallellitet.

Parallella frågor ger störst nytta om data distribueras jämnt över alla partitioner med avseende på frågan. Om den partitionerade samlingen är partitionerad så att alla eller de flesta data som returneras av en fråga koncentreras i några partitioner (en partition är sämst), kommer dessa partitioner att flaskhalsa frågans prestanda.

Justera MaxBufferedItemCount

Parallell fråga är utformat för att hämta resultat i förväg medan den aktuella resultatbatchen bearbetas av klienten. Den här förhämtningen hjälper till att förbättra den totala svarstiden för en fråga. Parametern MaxBufferedItemCount begränsar antalet förhämtningsresultat. Ange till det förväntade antalet resultat som returneras (eller ett högre tal) så att frågan kan få maximalt med MaxBufferedItemCount förhämtning.

Förhämtning fungerar på samma sätt oavsett graden av parallellitet och det finns en enda buffert för data från alla partitioner.

Implementera backoff vid RetryAfter intervals

Under prestandatestning bör du öka belastningen tills en liten frekvens av begäranden begränsas. Om begäranden begränsas bör klientprogrammet backa av vid begränsning för det server angivna återförsöksintervallet. Om du respekterar backoffen får du minimal väntetid mellan återförsöken.

Stöd för återförsöksprincip ingår i dessa SDK:er:

Mer information finns i RetryAfter.

I version 1.19 och senare av .NET SDK finns det en mekanism för att logga ytterligare diagnostikinformation och felsöka problem med svarstider, som du ser i följande exempel. Du kan logga diagnostiksträngen för begäranden som har högre läsfördröjning. Den avbildade diagnostiksträngen hjälper dig att förstå hur många gånger du har fått 429 fel för en viss begäran.

ResourceResponse<Document> readDocument = await this.readClient.ReadDocumentAsync(oldDocuments[i].SelfLink);
readDocument.RequestDiagnosticsString 

Cachedokument-URI:er för kortare läsfördröjning

Cachelagra dokument-URI:er när det är möjligt för bästa läsprestanda. Du måste definiera logik för att cachelagra resurs-ID:t när du skapar en resurs. Uppslag som baseras på resurs-ID:er är snabbare än namnbaserade uppslag, så att cachelagra dessa värden förbättrar prestandan.

Justera sidstorleken för frågor/läsfeeds för bättre prestanda

När du gör en massläsning av dokument med hjälp av funktionen för läsfeed (till exempel ) eller när du utfärdar en SQL-fråga returneras resultatet segmenterat om resultatuppsättningen är ReadDocumentFeedAsync för stor. Som standard returneras resultat i segment om 100 objekt eller 1 MB, beroende på vilken gräns som inträffar först.

Om du vill minska antalet nätverksresor som krävs för att hämta alla tillämpliga resultat kan du öka sidstorleken genom att använda x-ms-max-item-count för att begära upp till 1 000 huvuden. Om du bara behöver visa några få resultat, till exempel om ditt användargränssnitt eller program-API endast returnerar 10 resultat i taget, kan du också minska sidstorleken till 10 för att minska det dataflöde som används för läsningar och frågor.

Anteckning

Egenskapen maxItemCount ska inte bara användas för sidnumrering. Den används huvudsakligen för att förbättra frågeprestanda genom att minska det maximala antalet objekt som returneras på en enda sida.

Du kan också ange sidstorleken med hjälp av tillgängliga Azure Cosmos DB-SDK:er. Med egenskapen MaxItemCount i FeedOptions kan du ange det maximala antalet objekt som ska returneras i uppräkningsåtgärden. När maxItemCount är inställt på -1 hittar SDK automatiskt det optimala värdet, beroende på dokumentets storlek. Exempel:

IQueryable<dynamic> authorResults = client.CreateDocumentQuery(documentCollection.SelfLink, "SELECT p.Author FROM Pages p WHERE p.Title = 'About Seattle'", new FeedOptions { MaxItemCount = 1000 });

När en fråga körs skickas resulterande data i ett TCP-paket. Om du anger ett för lågt värde för är antalet resor som krävs för att skicka data i maxItemCount TCP-paketet högt, vilket påverkar prestanda. Så om du inte är säker på vilket värde som ska anges för egenskapen är det bäst att ställa in det på -1 och låta maxItemCount SDK välja standardvärdet.

Öka antalet trådar/uppgifter

Se Öka antalet trådar/uppgifter i nätverksavsnittet i den här artikeln.

Indexeringsprincip

Utesluta sökvägar som inte används från indexering för att få snabbare skrivning

Med Azure Cosmos DB-indexeringsprincipen kan du också ange vilka dokumentsökvägar som ska inkluderas i eller undantas från indexering med hjälp av indexeringssökvägar (IndexingPolicy.IncludedPaths och IndexingPolicy.ExcludedPaths). Indexeringssökvägar kan förbättra skrivprestanda och minska indexlagringen för scenarier där frågemönster är kända i förväg. Det beror på att indexeringskostnaderna korrelerar direkt med antalet unika sökvägar som indexeras. Den här koden visar till exempel hur du undantar en hel del av dokumenten (ett underträd) från indexering med jokertecknet "*":

var collection = new DocumentCollection { Id = "excludedPathCollection" };
collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
collection = await client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri("db"), collection);

Mer information finns i Azure Cosmos DB indexeringsprinciper.

Genomströmning

Mäta och justera för lägre användning av enheter för begäran/sekund

Azure Cosmos DB erbjuder en omfattande uppsättning databasåtgärder. Dessa åtgärder omfattar relations- och hierarkiska frågor medudf:er, lagrade procedurer och utlösare, som alla arbetar med dokumenten i en databassamling. Kostnaden för var och en av dessa åtgärder varierar beroende på vilken processor, IO och minne som krävs för att slutföra åtgärden. I stället för att tänka på och hantera maskinvaruresurser kan du tänka på en enhet för programbegäran (RU) som ett enda mått för de resurser som krävs för att utföra olika databasåtgärder och hantera en programbegäran.

Dataflödet etableras baserat på antalet enheter för programbegäran som angetts för varje container. Förbrukningen av enheter för begäran utvärderas som en frekvens per sekund. Program som överskrider frekvensen för etablerade enheter för programbegäran för containern är begränsade tills frekvensen sjunker under den etablerade nivån för containern. Om ditt program kräver ett högre dataflöde kan du öka dataflödet genom att etablera ytterligare enheter för programbegäran.

En frågas komplexitet påverkar hur många enheter för programbegäran som används för en åtgärd. Antalet predikat, predikatens typ, antalet FFS och storleken på källdatauppsättningen påverkar alla kostnaden för frågeåtgärder.

Om du vill mäta omkostnaderna för en åtgärd (skapa, uppdatera eller ta bort) kontrollerar du huvudet x-ms-request-charge (eller motsvarande egenskap i eller RequestCharge ResourceResponse\<T> i .NET SDK) för att mäta antalet enheter för programbegäran som förbrukas av FeedResponse\<T> åtgärderna:

// Measure the performance (Request Units) of writes
ResourceResponse<Document> response = await client.CreateDocumentAsync(collectionSelfLink, myDocument);
Console.WriteLine("Insert of document consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
IDocumentQuery<dynamic> queryable = client.CreateDocumentQuery(collectionSelfLink, queryString).AsDocumentQuery();
while (queryable.HasMoreResults)
    {
        FeedResponse<dynamic> queryResponse = await queryable.ExecuteNextAsync<dynamic>();
        Console.WriteLine("Query batch consumed {0} request units", queryResponse.RequestCharge);
    }

Begärandeavgiften som returneras i det här huvudet är en bråkdel av det etablerade dataflödet (det vill säga 2 000 RU:er/s). Om den föregående frågan till exempel returnerar dokument på 1 000 1 KB är kostnaden för åtgärden 1 000. Så inom en sekund respekterar servern bara två sådana begäranden innan hastighetsbegränsningen för senare begäranden. Mer information finns i Enheter för begäran och kalkylatorn för enheter för begäran.

Hantera hastighetsbegränsning/begärandefrekvens för stor

När en klient försöker överskrida det reserverade dataflödet för ett konto, finns det ingen prestandaförsämring på servern och ingen användning av dataflödeskapacitet utöver den reserverade nivån. Servern avslutar begäran förebyggande med RequestRateTooLarge (HTTP-statuskod 429). Den returnerar ett x-ms-retry-after-ms-huvud som anger hur lång tid, i millisekunder, som användaren måste vänta innan begäran försöker igen.

HTTP Status 429,
Status Line: RequestRateTooLarge
x-ms-retry-after-ms :100

SDK:erna fångar alla implicit det här svaret, respekterar det server angivna återförsökshuvudet och försöker sedan igen. Om ditt konto inte används samtidigt av flera klienter lyckas nästa återförsök.

Om du har fler än en klient som arbetar konsekvent över begärandefrekvensen kanske standardantalet för återförsök som för närvarande är inställt på 9 internt av klienten inte räcker till. I det här fallet kastar klienten en DocumentClientException med statuskod 429 till programmet.

Du kan ändra standardantalet för återförsök genom att ange RetryOptionsConnectionPolicy -instansen. Som standard returneras DocumentClientException med statuskod 429 efter en kumulativ väntetid på 30 sekunder om begäran fortsätter att fungera över begärandehastigheten. Det här felet returnerar även när det aktuella antalet återförsök är mindre än det maximala antalet återförsök, oavsett om det aktuella värdet är standardvärdet 9 eller ett användardefinierat värde.

Det automatiserade återförsöksbeteendet hjälper till att förbättra återhämtning och användbarhet för de flesta program. Men det kanske inte är det bästa beteendet när du utför prestandamått, särskilt när du mäter svarstid. Svarstiden som observeras av klienten ökar kraftigt om experimentet når serverbegränsningen och gör så att klient-SDK:n försöker igen tyst. För att undvika toppar i svarstiden under prestandaexperiment mäter du den avgift som returneras av varje åtgärd och ser till att begärandena fungerar under den reserverade förfrågningsfrekvensen. Mer information finns i Enheter för begäran.

Designa för mindre dokument för högre dataflöde

Kostnaden för begäran (det vill säga kostnaden för bearbetning av begäran) för en viss åtgärd korrelerar direkt med dokumentets storlek. Åtgärder för stora dokument kostar mer än åtgärder på små dokument.

Nästa steg

Ett exempelprogram som används för att utvärdera Azure Cosmos DB för scenarier med höga prestanda på några klientdatorer finns i Prestanda- och skalningstestning med Azure Cosmos DB.

Mer information om hur du utformar ditt program för skalning och höga prestanda finns i Partitionering och skalning i Azure Cosmos DB.