Leistungstipps für Azure Cosmos DB und .NET

GILT FÜR: NoSQL

Azure Cosmos DB ist eine schnelle und flexible verteilte Datenbank mit nahtloser Skalierung, garantierter Latenz und garantiertem Durchsatz. Die Skalierung Ihrer Datenbank mit Azure Cosmos DB erfordert weder aufwendige Änderungen an der Architektur noch das Schreiben von komplexem Code. Zentrales Hoch- und Herunterskalieren ist ebenso problemlos möglich wie ein einzelner API-Aufruf. Weitere Informationen finden Sie unter Bereitstellen von Standarddurchsatz (manuell) für einen Azure Cosmos-Container und Bereitstellen von Standarddurchsatz (manuell) für eine Datenbank in Azure Cosmos DB.

Da der Zugriff auf Azure Cosmos DB über Netzwerkaufrufe erfolgt, können Sie clientseitige Optimierungen vornehmen, um bei Verwendung des SQL .NET SDK eine optimale Leistung zu erzielen.

Wenn Sie versuchen möchten, die Datenbankleistung zu verbessern, sollten Sie die Optionen in den folgenden Abschnitten beachten.

Hostingempfehlungen

Aktivieren der serverseitigen Garbage Collection

In einigen Fällen kann es hilfreich sein, die Häufigkeit zu verringern, mit der die Garbage Collection ausgeführt wird. Legen Sie gcServer in .NET auf true fest.

Erweitern Ihrer Clientworkload

Wenn Sie auf einem hohen Durchsatzniveau oder bei Raten mit mehr als 50.000 Anforderungseinheiten pro Sekunde (RU/s) testen, kann sich die Clientanwendung als Engpass für die Workloads erweisen. Dies liegt daran, dass der Computer unter Umständen die CPU- oder Netzwerkauslastung deckelt. Wenn dieser Punkt erreicht wird, können Sie das Azure Cosmos DB-Konto weiter auslasten, indem Sie Ihre Clientanwendungen auf mehrere Server horizontal hochskalieren.

Hinweis

Eine hohe CPU-Auslastung kann zu erhöhter Latenz und Ausnahmen des Typs „Anforderungstimeout“ führen.

Metadatenvorgänge

Überprüfen Sie das Vorhandensein einer Datenbank und/oder eines Containers nicht durch den Aufruf von Create...IfNotExistsAsync und/oder Read...Async im langsamsten Pfad und/oder vor der Durchführung eines Elementvorgangs. Die Überprüfung sollte nur beim Start der Anwendung durchgeführt werden, wenn dies erforderlich ist und Sie eine Löschung erwarten (andernfalls ist keine Überprüfung erforderlich). Diese Metadatenvorgänge verursachen eine zusätzliche End-to-End-Latenz, unterliegen keiner SLA und separaten Einschränkungen und sind nicht wie Datenvorgänge skalierbar.

Protokollierung und Nachverfolgung

In einigen Umgebungen ist .NET DefaultTraceListener aktiviert. DefaultTraceListener führt in Produktionsumgebungen zu Leistungsproblemen, die eine hohe CPU-Auslastung und E/A-Engpässe bewirken. Stellen Sie sicher, dass DefaultTraceListener für Ihre Anwendung deaktiviert ist, indem Sie ihn in Produktionsumgebungen aus TraceListeners entfernen.

Bei den neuesten SDK-Versionen (höher als 3.23.0) wird er bei Erkennung automatisch entfernt. Bei älteren Versionen können Sie ihn wie folgt entfernen:

if (!Debugger.IsAttached)
{
    Type defaultTrace = Type.GetType("Microsoft.Azure.Cosmos.Core.Trace.DefaultTrace,Microsoft.Azure.Cosmos.Direct");
    TraceSource traceSource = (TraceSource)defaultTrace.GetProperty("TraceSource").GetValue(null);
    traceSource.Listeners.Remove("Default");
    // Add your own trace listeners
}

Netzwerk

Verbindungsrichtlinie: Verwenden des direkten Verbindungsmodus

Als Standardverbindungsmodus des .NET V3 SDK wird der direkte Modus mit TCP-Protokoll verwendet. Sie konfigurieren den Verbindungsmodus beim Erstellen der CosmosClient-Instanz in CosmosClientOptions. Weitere Informationen zu verschiedenen Konnektivitätsoptionen finden Sie im Artikel zu den Konnektivitätsmodi.

string connectionString = "<your-account-connection-string>";
CosmosClient client = new CosmosClient(connectionString,
new CosmosClientOptions
{
    ConnectionMode = ConnectionMode.Gateway // ConnectionMode.Direct is the default
});

Kurzfristige Portauslastung

Wenn Sie ein hohes Verbindungsaufkommen oder eine hohe Portauslastung Ihrer Instanzen feststellen, überprüfen Sie zunächst, ob die Clientinstanzen Singletons sind. Anders ausgedrückt: Die Clientinstanzen müssen für die Lebensdauer der Anwendung eindeutig sein.

Bei der Ausführung über das TCP-Protokoll optimiert der Client die Latenz mithilfe langlebiger Verbindungen. Im Gegensatz dazu werden beim HTTPS-Protokoll Verbindungen nach zwei Minuten Inaktivität getrennt.

Wenn Sie in Szenarien mit geringem Zugriff eine im Vergleich zum Zugriff im Gatewaymodus höhere Anzahl von Verbindungen feststellen, haben Sie folgende Möglichkeiten:

  • Legen Sie die Eigenschaft CosmosClientOptions.PortReuseMode auf PrivatePortPool fest (gültig ab Frameworkversion 4.6.1 und ab .NET Core-Version 2.0). Über diese Eigenschaft kann das SDK einen kleinen Pool von kurzlebigen Ports für verschiedene Azure Cosmos DB-Zielendpunkte verwenden.
  • Konfigurieren Sie die Eigenschaft CosmosClientOptions.IdleTcpConnectionTimeout so, dass sie mindestens zehn Minuten beträgt. Empfohlene Werte reichen von 20 Minuten bis 24 Stunden.

Platzieren von Clients in derselben Azure-Region, um die Leistung zu verbessern

Platzieren Sie nach Möglichkeit sämtliche Anwendungen, die Azure Cosmos DB aufrufen, in der gleichen Region wie die Azure Cosmos DB-Datenbank. Damit Sie einen ungefähren Vergleich haben: Azure Cosmos DB-Aufrufe aus derselben Region werden normalerweise innerhalb von ca. 1 bis 2 ms abgeschlossen, während die Latenz zwischen West- und Ostküste der USA mehr als 50 ms beträgt. Diese Latenz variiert ggf. von Anforderung zu Anforderung und ist abhängig von der Route, die die Anforderung zwischen dem Client und der Grenze des Azure-Rechenzentrums nimmt.

Die geringste Latenz erzielen Sie, wenn sich die aufrufende Anwendung in der gleichen Azure-Region wie der bereitgestellte Azure Cosmos DB-Endpunkt befindet. Eine Liste mit den verfügbaren Regionen finden Sie unter Azure-Regionen.

Anordnen von Clients in derselben Region

Erhöhen der Anzahl von Threads/Aufgaben

Da Azure Cosmos DB-Aufrufe über das Netzwerk erfolgen, müssen Sie eventuell den Parallelitätsgrad Ihrer Anforderungen variieren, um die Wartezeit für die Clientanwendung zwischen Anforderungen auf ein Minimum zu reduzieren. Erstellen Sie beispielsweise bei Verwendung der Task Parallel Library von .NET mehrere Hundert Aufgaben für Lese- und Schreibvorgänge in Azure Cosmos DB.

Aktivieren des beschleunigten Netzwerkbetriebs zur Verringerung von Latenz und CPU-Jitter

Es wird empfohlen, die Anweisungen zum Aktivieren des beschleunigten Netzwerkbetriebs in Ihrer Azure-VM unter Windows oder Linux (für Anweisungen jeweils klicken) zu befolgen, um die Leistung zu maximieren.

Ohne den beschleunigten Netzwerkbetrieb können E/A-Vorgänge zwischen Ihrer Azure-VM und anderen Azure-Ressourcen unnötigerweise über einen Host und einen virtuellen Switch zwischen der VM und dessen Netzwerkkarte weitergeleitet werden. Wenn sich der Host und der virtuelle Switch inline auf dem Datenpfad befinden, sorgt dies beim Kommunikationskanal nicht nur für eine längere Wartezeit und Jitter, sondern auch die CPU-Zyklen von der VM werden reduziert. Bei beschleunigtem Netzwerkbetrieb erstellt die VM ohne Vermittler direkt Schnittstellen mit der Netzwerkkarte. Alle Netzwerkrichtliniendetails, die zuvor vom Host und dem virtuellen Switch verarbeitet wurden, werden jetzt über die Hardware der Netzwerkkarte verarbeitet, und der Host und der virtuelle Switch werden umgangen. Im Allgemeinen sind eine kürzere und konsistentere Wartezeit, ein höherer Durchsatz und eine geringere CPU-Auslastung zu erwarten, wenn Sie den beschleunigten Netzwerkbetrieb aktivieren.

Einschränkungen: Der beschleunigte Netzwerkbetrieb muss vom Betriebssystem der VM unterstützt werden und kann nur aktiviert werden, wenn die VM beendet und ihre Zuordnung aufgehoben wurde. Die VM kann nicht mit dem Azure Resource Manager bereitgestellt werden. In App Service ist der beschleunigte Netzwerkbetrieb nicht aktiviert.

Weitere Informationen finden Sie in den Anweisungen für Windows bzw. Linux.

SDK-Verwendung

Installieren des neuesten SDKs

Azure Cosmos DB-SDKs werden ständig verbessert, um eine optimale Leistung zu ermöglichen. Informationen zum neuesten SDK und zu den Verbesserungen finden Sie unter Azure Cosmos DB .NET SDK v3 für SQL-API: Download und Versionshinweise.

Verwenden von Stream-APIs

.NET SDK V3 enthält Stream-APIs, die Daten ohne Serialisierung empfangen und zurückgeben können.

Anwendungen der mittleren Ebene, die die Antworten aus dem SDK nicht direkt nutzen, sondern an andere Logikschichten weiterleiten, können von den Stream-APIs profitieren. Beispiele zur Handhabung von Streams finden Sie in den Beispielen für die Elementverwaltung.

Verwenden eines Singleton-Azure Cosmos DB-Clients für die Lebensdauer der Anwendung

Bei Verwendung des direkten Modus ist jede Instanz von CosmosClient threadsicher und verfügt über eine effiziente Verbindungsverwaltung und Adressenzwischenspeicherung. Zur Ermöglichung einer effizienten Verbindungsverwaltung und einer besseren Leistung des SDK-Clients empfiehlt es sich, für die Lebensdauer der Anwendung pro AppDomain eine einzelne Instanz für jedes Konto zu verwenden, mit dem Ihre Anwendung interagiert.

Informationen zu mehrinstanzenfähigen Anwendungen, die mehrere Konten nutzen, finden Sie in den entsprechenden bewährten Methoden.

Bei der Arbeit mit Azure Functions sollten die Instanzen ebenfalls den bestehenden Richtlinien folgen und eine einzelne Instanz beibehalten.

Vermeiden von blockierenden Aufrufen

Das Azure Cosmos DB SDK sollte so konzipiert werden, dass es viele Anforderungen gleichzeitig verarbeiten kann. Asynchrone APIs ermöglichen die parallele Verarbeitung Tausender Anforderungen in einem kleinen Pool von Threads, indem nicht auf blockierende Aufrufe gewartet wird. Anstatt darauf zu warten, dass eine synchrone Aufgabe mit langer Laufzeit abgeschlossen wird, kann der Thread eine andere Anforderung bearbeiten.

Ein häufiges Leistungsproblem in Apps, die das Azure Cosmos DB SDK verwenden, sind blockierende Aufrufe, die asynchron sein könnten. Viele synchrone blockierende Aufrufe führen zu einem Threadmangel im Pool und zu längeren Antwortzeiten.

Vermeiden Sie Folgendes:

  • Blockieren der asynchronen Ausführung durch Aufrufen von Task.Wait oder Task.Result.
  • Verwenden von Task.Run, um eine synchrone API als asynchron festzulegen.
  • Abrufen von Sperren in allgemeinen Codepfaden. Das Azure Cosmos DB .NET SDK liefert die beste Leistung, wenn der Code parallel ausgeführt wird.
  • Aufrufen von Task.Run und sofortiges Warten darauf. ASP.NET Core führt App-Code bereits in normalen Threads im Threadpool aus, daher führt ein Aufruf von „Task.Run“ nur zu einer unnötigen zusätzlichen Reservierung des Threadpools. Auch wenn der geplante Code einen Thread blockieren würde, wird dies durch „Task.Run“ nicht verhindert.
  • Verwenden Sie nicht „ToList()“ in Container.GetItemLinqQueryable<T>(). Es nutzt blockierende Aufrufe, um die Abfrage synchron zu entladen. Verwenden Sie ToFeedIterator(), um die Abfrage asynchron zu entladen.

Empfohlene Vorgehensweise:

  • Rufen Sie die .NET-APIs von Azure Cosmos DB asynchron auf.
  • Die gesamte Aufrufliste ist asynchron, um von async/await-Mustern zu profitieren.

Über einen Profiler wie z. B. PerfView können Threads ermittelt werden, die dem Threadpool häufig hinzugefügt werden. Das Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start-Ereignis gibt einen Thread an, der dem Threadpool hinzugefügt wurde.

Deaktivieren der Inhaltsantwort bei Schreibvorgängen

Legen Sie für Workloads mit hohen Erstellungsnutzlasten die Anforderungsoption EnableContentResponseOnWrite auf false fest. Der Dienst gibt die erstellte oder aktualisierte Ressource nicht mehr an das SDK zurück. Normalerweise verfügt die Anwendung über das zu erstellende Objekt, sodass sie den Dienst nicht benötigt, um es zurückzugeben. Die Headerwerte sind nach wie vor zugänglich wie etwa eine Anforderungsgebühr. Die Deaktivierung der Inhaltsantwort kann die Leistung verbessern, da das SDK keinen Speicher mehr zuweisen oder den Hauptteil der Antwort nicht mehr serialisieren muss. Dies reduziert auch die Auslastung der Netzwerkbandbreite, um die Leistung weiter zu steigern.

ItemRequestOptions requestOptions = new ItemRequestOptions() { EnableContentResponseOnWrite = false };
ItemResponse<Book> itemResponse = await this.container.CreateItemAsync<Book>(book, new PartitionKey(book.pk), requestOptions);
// Resource will be null
itemResponse.Resource

Aktivieren der Massenbearbeitung zum Optimieren des Durchsatzes anstelle der Wartezeit

Aktivieren Sie die Massenbearbeitung für Szenarien, in denen die Workload einen hohen Durchsatz erfordert und die Wartezeit nicht so wichtig ist. Weitere Informationen zum Aktivieren des Features für die Massenbearbeitung und zu den Szenarien, in denen es verwendet werden sollte, finden Sie unter Einführung zur Unterstützung der Massenbearbeitung im .NET SDK.

Erhöhen von System.Net MaxConnections pro Host bei Verwendung des Gatewaymodus

Azure Cosmos DB-Anforderungen erfolgen im Gatewaymodus über HTTPS/REST. Sie unterliegen dem Standardverbindungslimit pro Hostname oder IP-Adresse. Unter Umständen müssen Sie einen höheren Wert für MaxConnections festlegen (100 bis 1.000), damit die Clientbibliothek mehrere Verbindungen mit Azure Cosmos DB gleichzeitig nutzen kann. In .NET SDK 1.8.0 und höher ist der Standardwert für ServicePointManager.DefaultConnectionLimit „50“. Zum Ändern des Werts können Sie Documents.Client.ConnectionPolicy.MaxConnectionLimit auf einen höheren Wert festlegen.

Erhöhen der Anzahl von Threads/Aufgaben

Siehe Erhöhen der Anzahl von Threads/Aufgaben im Abschnitt „Netzwerk“ dieses Artikels.

Abfragevorgänge

Informationen zu Abfragevorgängen finden Sie in den Leistungstipps für Abfragen.

Indizierungsrichtlinie

Beschleunigen von Schreibvorgängen durch Ausschließen nicht verwendeter Pfade von der Indizierung

Die Indizierungsrichtlinie von Azure Cosmos DB ermöglicht auch die Verwendung von Indizierungspfaden („IndexingPolicy.IncludedPaths“ und „IndexingPolicy.ExcludedPaths“), um anzugeben, welche Dokumentpfade in die Indizierung ein- bzw. ausgeschlossen werden sollen.

Die alleinige Indizierung der erforderlichen Pfade kann die Schreibleistung verbessern, die verbrauchten RUs für Schreibvorgänge reduzieren und die Indexspeicherung für Szenarien verringern, in denen die Abfragemuster im Voraus bekannt sind. Dies liegt daran, dass der Indizierungsaufwand direkt mit der Anzahl der indizierten eindeutigen Pfade zusammenhängt. Der folgenden Code zeigt beispielsweise, wie Sie einen gesamten Abschnitt der Dokumente (eine Unterstruktur) mit dem Platzhalter „*“ von der Indizierung ausschließen:

var containerProperties = new ContainerProperties(id: "excludedPathCollection", partitionKeyPath: "/pk" );
containerProperties.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
containerProperties.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
Container container = await this.cosmosDatabase.CreateContainerAsync(containerProperties);

Weitere Informationen finden Sie unter Indizierungsrichtlinien für Azure Cosmos DB.

Throughput

Messen und Optimieren zum Senken der erforderlichen Anforderungseinheiten pro Sekunde

Azure Cosmos DB bietet einen umfangreichen Satz von Datenbankvorgängen. Diese Vorgänge umfassen relationale und hierarchische Abfragen mit UDF-Dateien (Universal Disk Format), gespeicherte Prozeduren und Trigger, die alle in den Dokumenten innerhalb einer Datenbanksammlung ausgeführt werden.

Die Kosten im Zusammenhang mit diesen Vorgängen variieren in Abhängigkeit vom CPU-, E/A- und Speicheraufwand, der für den jeweiligen Vorgang erforderlich ist. Anstatt sich Gedanken über Hardwareressourcen und deren Verwaltung zu machen, können Sie sich eine Anforderungseinheit als alleinige Maßeinheit für die Ressourcen vorstellen, die für das Durchführen der verschiedenen Datenbankvorgänge und das Ausführen einer Anwendungsanforderung erforderlich sind.

Der Durchsatz wird basierend auf der für jeden Container festgelegten Anzahl von Anforderungseinheiten bereitgestellt. Der Verbrauch von Anforderungseinheiten wird als Rate der Einheiten pro Sekunde bemessen. Anwendungen, die die bereitgestellte Rate von Anforderungseinheiten für ihre Container überschreiten, werden begrenzt, bis die Rate wieder unter das bereitgestellte Niveau für den Container fällt. Wenn Ihre Anwendung einen höheren Durchsatz erfordert, können Sie ihn durch Bereitstellung zusätzlicher Anforderungseinheiten erhöhen.

Die Komplexität einer Abfrage wirkt sich darauf aus, wie viele Anforderungseinheiten für einen Vorgang verbraucht werden. Die Anzahl von Prädikaten, die Art der Prädikate, die Anzahl von UDF-Dateien und die Größe des Quelldatasets beeinflussen die Kosten von Abfragevorgängen.

Untersuchen Sie zum Ermitteln des Aufwands für einen Vorgang (Erstellen, Aktualisieren oder Löschen) den Header x-ms-request-charge (oder die entsprechende RequestCharge-Eigenschaft in ResourceResponse<T> oder FeedResponse<T> im .NET SDK), um die Anzahl von Anforderungseinheiten zu ermitteln, die von diesen Vorgängen verbraucht werden:

// Measure the performance (Request Units) of writes
ItemResponse<Book> response = await container.CreateItemAsync<Book>(myBook, new PartitionKey(myBook.PkValue));
Console.WriteLine("Insert of item consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
FeedIterator<Book> queryable = container.GetItemQueryIterator<ToDoActivity>(queryString);
while (queryable.HasMoreResults)
    {
        FeedResponse<Book> queryResponse = await queryable.ExecuteNextAsync<Book>();
        Console.WriteLine("Query batch consumed {0} request units", queryResponse.RequestCharge);
    }

Bei der in diesem Header zurückgegebenen Anforderungsbelastung handelt es sich um einen Bruchteil Ihres bereitgestellten Durchsatzes (d. h. 2.000 RU/s). Falls die obige Abfrage also beispielsweise 1.000 Dokumente mit einer Größe von 1 KB zurückgibt, fallen für den Vorgang Kosten in Höhe von 1.000 an. Somit werden vom Server innerhalb einer Sekunde nur zwei solcher Anforderungen berücksichtigt, und für weitere Anforderungen wird die Rate begrenzt. Weitere Informationen finden Sie unter Anforderungseinheiten sowie unter dem Rechner für Anforderungseinheiten.

Behandeln von Ratenbeschränkungen/zu hohen Anforderungsraten

Wenn ein Client versucht, den für ein Konto reservierten Durchsatz zu überschreiten, wird die Serverleistung nicht beeinträchtigt, und es wird kein über die reservierte Kapazität hinausgehender Durchsatz in Anspruch genommen. Der Server beendet die Anforderung vorab mit RequestRateTooLarge (HTTP-Statuscode 429). Er gibt einen Header vom Typ x-ms-retry-after-ms zurück, der die Zeitspanne in Millisekunden angibt, die der Benutzer warten muss, bevor er die Anforderung erneut versuchen kann.

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

Alle SDKs fangen diese Antwort implizit ab, berücksichtigen den vom Server angegebenen Header vom Typ „retry-after“ und wiederholen die Anforderung. Wenn nicht mehrere Clients gleichzeitig auf Ihr Konto zugreifen, wird die nächste Wiederholung erfolgreich ausgeführt.

Wenn insgesamt mehrere Clients beständig die Anforderungsrate überschreiten, reicht die standardmäßige Wiederholungsanzahl (vom Client intern auf 9 festgelegt) möglicherweise nicht aus. In diesem Fall löst der Client eine CosmosException mit dem Statuscode 429 für die Anwendung aus.

Sie können die Standardanzahl von Wiederholungsversuchen ändern, indem Sie die RetryOptions für die CosmosClientOptions-Instanz festlegen. Die CosmosException-Klasse mit dem Statuscode 429 wird standardmäßig nach einer kumulierten Wartezeit von 30 Sekunden zurückgegeben, wenn die Anforderung weiterhin die Anforderungsrate übersteigt. Dieser Fehler wird auch dann zurückgegeben, wenn die aktuelle Wiederholungsanzahl unter der maximalen Wiederholungsanzahl liegt – ganz gleich, ob es sich dabei um den Standardwert (9) oder um einen benutzerdefinierten Wert handelt.

Das automatisierte Wiederholungsverhalten trägt dazu bei, die Resilienz und Benutzerfreundlichkeit für die meisten Anwendungen zu verbessern. Dies stellt jedoch möglicherweise nicht das beste Verhalten dar, wenn Sie Leistungsbenchmarks durchführen, und insbesondere nicht beim Messen der Latenz. Die Wartezeit für den Client nimmt stark zu, wenn das Experiment die Serverdrosselung erreicht und damit die automatische Wiederholung durch das Client-SDK auslöst. Ermitteln Sie zur Vermeidung von Latenzspitzenwerten bei Leistungsexperimenten die von den einzelnen Vorgängen zurückgegebene Belastung, und stellen Sie sicher, dass die Anforderungen die reservierte Anforderungsrate nicht überschreiten.

Weitere Informationen finden Sie unter Anforderungseinheiten.

Verwenden eines auf kleinere Dokumente ausgerichteten Designs für einen höheren Durchsatz

Der Anforderungsaufwand (also die Kosten für die Anforderungsverarbeitung) eines bestimmten Vorgangs hängt direkt mit der Größe des Dokuments zusammen. Vorgänge für große Dokumente sind teurer als Vorgänge für kleine Dokumente.

Nächste Schritte

Eine Beispielanwendung zur Evaluierung von Azure Cosmos DB für Hochleistungsszenarien auf einigen Clientcomputern finden Sie unter Leistungs- und Skalierungstests mit Azure Cosmos DB.

Weitere Informationen zum Entwerfen einer auf Skalierung und hohe Leistung ausgelegten Anwendung finden Sie unter Partitionieren und Skalieren in Azure Cosmos DB.