Conseils sur les performances pour Azure Cosmos DB et le kit SDK .NET v2

S’APPLIQUE À : NoSQL

Azure Cosmos DB est une base de données distribuée rapide et flexible qui peut être mise à l’échelle en toute transparence avec une latence et un débit garantis. Vous n’avez pas à apporter de modifications d’architecture majeures ou écrire de code complexe pour mettre à l’échelle votre base de données avec Azure Cosmos DB. La réduction et l’augmentation de l’échelle est aussi simple que le passage d’un appel d’API. Pour en savoir plus, voir Approvisionner le débit d’un conteneur ou Approvisionner le débit d’une base de données. Toutefois, étant donné qu’Azure Cosmos DB est accessible par le biais d’appels réseau, vous pouvez apporter des optimisations côté client de manière à atteindre des performances de pointe quand vous utilisez le Kit de développement logiciel (SDK) SQL .NET.

Par conséquent, si vous essayez d’améliorer les performances de votre base de données, envisagez les options suivantes :

Mise à niveau vers le kit SDK .NET v3

Le kit SDK .NET v3 est sorti. Si vous utilisez le kit SDK .NET v3, reportez-vous au guide des performances .NET v3 pour obtenir les informations suivantes :

  • Mode TCP direct par défaut
  • Prise en charge de l’API Stream
  • Prise en charge d’un sérialiseur personnalisé pour autoriser l’utilisation de System.Text.JSON
  • Prise en charge intégrée du traitement par lots et en bloc

Recommandations relatives à l’hébergement

Activer garbage collection (GC) côté serveur

Réduire la fréquence de garbage collection peut aider dans certains cas. Dans .NET, définissez gcServer sur true.

Effectuer une montée en charge de votre charge de travail cliente

Si vous effectuez des tests à des niveaux de débit élevé (plus de 50 000 RU/s), l’application cliente peut devenir un goulet d’étranglement en raison du plafonnement sur l’utilisation du processeur ou du réseau. Si vous atteignez ce point, vous pouvez continuer à augmenter le compte Azure Cosmos DB en augmentant la taille des instances de vos applications clientes sur plusieurs serveurs.

Notes

Une utilisation élevée de l'UC peut entraîner une latence accrue et des exceptions en termes de délai d’expiration des requêtes.

Opérations sur les métadonnées

Ne pas vérifier qu’une base de données et/ou une collection existe en appelant Create...IfNotExistsAsync et/ou Read...Async dans le chemin chaud et/ou avant de faire une opération d’éléments. La validation doit être effectuée uniquement au démarrage de l’application si cela est nécessaire : si vous pensez qu’elle sera supprimée (sinon, elle n’est pas nécessaire). Ces opérations de métadonnées génèrent une latence de bout en bout supplémentaire, n’ont pas de SLA et ont leurs propres limitations distinctes qui ne se mettent pas à l’échelle comme les opérations de données.

Journalisation et suivi

Pour certains environnements .NET DefaultTraceListener est activé. DefaultTraceListener pose des problèmes de performance dans les environnements de production, provoquant des goulots d’étranglement importants au niveau du processeur et des E/S. Vérifiez et assurez-vous que DefaultTraceListener est désactivé pour votre application en le supprimant des TraceListeners dans les environnements de production.

Les dernières versions du kit de développement logiciel (SDK) (supérieures à 2.16.2) le suppriment automatiquement dès qu’elles le détectent. Avec les versions antérieures, vous pouvez le supprimer avec :

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

Mise en réseau

Stratégie de connexion : Utiliser le mode de connexion directe

Le mode de connexion par défaut du kit de développement logiciel (SDK) .NET V2 est la passerelle. Vous configurez le mode de connexion pendant la construction de l’instance DocumentClient à l’aide du paramètre ConnectionPolicy. Si vous utilisez le mode direct, vous devez également définir le Protocol à l’aide du paramètre ConnectionPolicy. Pour en savoir plus sur les différentes options de connectivité, consultez l’article sur les modes de connectivité.

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
});

Épuisement des ports éphémères

Si vous êtes confronté à un volume de connexion élevé ou à une utilisation élevée des ports sur vos instances, vérifiez d’abord que vos instances clientes sont des singletons. En d’autres termes, les instances clientes doivent être uniques pour la durée de vie de l’application.

En cas d’exécution sur le protocole TCP, le client optimise la latence en utilisant des connexions à long terme par opposition au protocole HTTPS, qui met fin aux connexions après 2 minutes d’inactivité.

Dans les scénarios où vous disposez de peu d’accès et que vous remarquez un nombre de connexions supérieur par rapport au mode de passerelle, vous pouvez :

  • Configurer la propriété ConnectionPolicy.PortReuseMode sur PrivatePortPool (valable avec la version de Framework >= 4.6.1 et .NET Core version >= 2.0) : cette propriété permet au SDK d’utiliser un petit pool de ports éphémères pour différents points de terminaison de destination Azure Cosmos DB.
  • Configurez la propriété ConnectionPolicy.Idleconnectiontimeout, afin qu’elle soit supérieure ou égale à 10 minutes. Les valeurs recommandées sont comprises entre 20 minutes et 24 heures.

Appel d’OpenAsync pour éviter la latence de démarrage lors de la première requête

Par défaut, la première requête a une latence plus élevée, car elle doit extraire la table de routage d’adresses. Lorsque vous utilisez le Kit de développement logiciel (SDK) V2, appelez OpenAsync() une fois pendant l’initialisation afin d’éviter cette latence de démarrage lors de la première requête. L’appel ressemble à ceci : await client.OpenAsync();

Notes

OpenAsync génère des requêtes afin d’obtenir la table de routage d’adresses pour tous les conteneurs du compte. Pour les comptes qui ont de nombreux conteneurs, mais dont l’application accède à un sous-ensemble, OpenAsync génère un volume de trafic inutile, ce qui ralentit l’initialisation. Ainsi, l’utilisation de OpenAsync peut s’avérer superflue dans ce scénario, car elle ralentit le démarrage de l’application.

Pour des performances optimales, colocaliser les clients dans la même région Azure

Dans la mesure du possible, placez toutes les applications qui appellent Azure Cosmos DB dans la même région que la base de données Azure Cosmos DB. Voici une comparaison approximative : les appels à Azure Cosmos DB dans la même région s’effectuent en 1 à 2 ms, mais la latence entre les côtes Ouest et Est des États-Unis est supérieure à 50 ms. Cette latence peut varier d’une requête à l’autre, en fonction de l’itinéraire utilisé par la requête lorsqu’elle passe du client à la limite du centre de données Azure. Vous pouvez obtenir la latence la plus faible possible en veillant à ce que l’application appelante soit située dans la même région Azure que le point de terminaison Azure Cosmos DB configuré. Pour obtenir la liste des régions disponibles, voir Régions Azure.

Stratégie de connexion Azure Cosmos DB

Augmenter le nombre de threads/tâches

Étant donné que les appels à Azure Cosmos DB sont effectués sur le réseau, vous devrez peut-être modifier le degré de parallélisme de vos requêtes, afin que l’application cliente attende un temps minimal entre les requêtes. Par exemple, si vous utilisez la bibliothèque parallèle de tâches .NET, créez des centaines de tâches de lecture ou d’écriture dans Azure Cosmos DB.

Activer la mise en réseau accélérée

Pour réduire la latence et l’instabilité du processeur, nous vous recommandons d’activer la mise en réseau accélérée sur les machines virtuelles clientes. Consultez les articles Créer une machine virtuelle Windows avec mise en réseau accélérée ou Créer une machine virtuelle Linux avec mise en réseau accélérée.

Utilisation du Kit de développement logiciel (SDK)

Installation du kit de développement logiciel (SDK) le plus récent

Les SDK Azure Cosmos DB sont constamment améliorés pour fournir des performances optimales. Consultez les pages du SDK Azure Cosmos DB pour déterminer quel est le SDK le plus récent et passer en revue les améliorations.

Utiliser un client Azure Cosmos DB singleton pour la durée de vie de votre application

Chaque instance de DocumentClient est thread-safe et effectue une gestion des connexions efficace et une mise en cache d’adresses quand le mode direct est sélectionné. Pour permettre une gestion efficace des connexions et améliorer les performances du client SDK, nous vous recommandons d’utiliser une seule instance par AppDomain pour la durée de vie de l’application.

Évitez les appels bloquants

Le kit SDK Azure Cosmos DB doit être conçu pour traiter plusieurs requêtes simultanément. Les API asynchrones permettent à un petit pool de threads de gérer des milliers de requêtes simultanées sans attendre lors d’appels bloquants. Au lieu d’attendre la fin d’une tâche synchrone de longue durée, le thread peut travailler sur une autre requête.

Un problème de performances courant dans les applications utilisant le kit SDK Azure Cosmos DB bloque les appels qui pourraient être asynchrones. De nombreux appels bloquants synchrones conduisent à une défaillance du pool de threads et à des temps de réponse qui se dégradent.

À ne pas faire :

  • Bloquer l’exécution asynchrone en appelant Task.Wait ou Task.Result.
  • Utiliser Task.Run pour rendre une API synchrone asynchrone.
  • Acquérir des verrous dans les chemins de code courants. Le kit SDK .NET Azure Cosmos DB est le plus performant lorsqu’il est conçu pour exécuter du code en parallèle.
  • Appeler Task.Run et l’attendre tout de suite. ASP.NET Core exécute déjà le code d’application sur des threads de pool de threads normaux, donc l’appel de Task.Run entraîne uniquement une planification de pool de threads superflue. Même si le code planifié bloquait un thread, Task.Run ne l’empêcherait pas.
  • Utiliser ToList() sur DocumentClient.CreateDocumentQuery(...), lequel utilise des appels bloquants pour vider la requête de façon synchrone. Utiliser AsDocumentQuery() pour vider la requête de façon asynchrone.

À faire :

  • Appeler les API .NET Azure Cosmos DB de façon asynchrone.
  • L’ensemble de la pile des appels est asynchrone afin de tirer parti des modèles async/await.

Un profileur, tel que PerfView, peut être utilisé pour rechercher les threads ajoutés fréquemment au pool de threads. L’événement Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start indique qu’un thread a été ajouté au pool de threads.

Augmentation de System.Net MaxConnections par hôte lors de l’utilisation du mode passerelle

Les requêtes d’Azure Cosmos DB sont effectuées via le protocole HTTPS/REST lorsque vous utilisez le mode passerelle. Elles sont soumises à la limite de connexion par défaut par nom d’hôte ou adresse IP. Vous devrez peut-être définir MaxConnections sur une valeur plus élevée (100 à 1 000) afin que la bibliothèque cliente puisse utiliser plusieurs connexions simultanées à Azure Cosmos DB. Dans le Kit de développement logiciel (SDK) .NET 1.8.0 et versions ultérieures, la valeur par défaut de ServicePointManager. DefaultConnectionLimit est 50. Pour modifier cette valeur, vous pouvez définir Documents.Client.ConnectionPolicy.MaxConnectionLimit sur une valeur supérieure.

Implémentation d’interruption à des intervalles de RetryAfter

Lors du test de performances, vous devez augmenter la charge jusqu’à une limite d’un petit nombre de requêtes. Si les requêtes sont limitées, l’application cliente doit s’interrompre à la limitation pour l’intervalle de nouvelle tentative spécifié sur le serveur. Le respect de l’interruption garantit un temps d’attente minimal entre chaque tentative.

La prise en charge des stratégies de nouvelle tentative est incluse dans ces Kits de développement logiciel (SDK) :

Pour plus d’informations, consultez la page RetryAfter.

Dans la version 1.19 ou version ultérieure du Kit de développement logiciel (SDK) .NET, un mécanisme permet de consigner des informations de diagnostic supplémentaires et de résoudre les problèmes de latence, comme indiqué dans l’exemple suivant. Vous pouvez consigner la chaîne de diagnostic de requêtes ayant une latence de lecture supérieure. La chaîne de diagnostic capturée vous permet de connaître le nombre de fois où vous avez reçu des erreurs 429 pour une requête donnée.

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

Mise en cache d’URI de document pour une latence de lecture plus faible

Effectuez une mise en cache des URI de document dès que possible pour garantir la meilleure lecture. Vous devez définir la logique pour mettre en cache l’ID de ressource lorsque vous créez une ressource. Les recherches par ID de ressource sont plus rapides que les recherches par nom. La mise en cache de ces valeurs améliore donc les performances.

Augmenter le nombre de threads/tâches

Consultez Augmenter le nombre de threads/tâches dans la section Mise en réseau de cet article.

Opérations de requête

Pour les opérations de requête, consultez les conseils en matière de performances pour les requêtes.

Stratégie d’indexation

Exclusion des chemins d’accès inutilisés de l’indexation pour des écritures plus rapides

La stratégie d’indexation d’Azure Cosmos DB vous permet également de spécifier les chemins d’accès de document à inclure ou exclure de l’indexation en utilisant les chemins d’accès d’indexation (IndexingPolicy.IncludedPaths et IndexingPolicy.ExcludedPaths). Les chemins d’accès d’indexation peuvent améliorer les performances d’écriture et réduire le stockage des index pour les scénarios dans lesquels les modèles de requête sont connus au préalable. Cela est dû au fait que les coûts d’indexation correspondent directement au nombre de chemins d’accès uniques indexés. Par exemple, ce code montre comment exclure une section entière des documents (appelée sous-arborescence) de l’indexation à l’aide du caractère générique « * » :

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);

Pour plus d’informations, consultez Stratégies d’indexation d’Azure Cosmos DB.

Débit

Mesurer et optimiser de façon à réduire l’utilisation des unités de requête par seconde

Azure Cosmos DB offre un ensemble complet d’opérations de base de données. Ces opérations incluent les requêtes hiérarchiques et relationnelles avec les fonctions définies par l’utilisateur, les procédures stockées et les déclencheurs, qui fonctionnent tous au niveau des documents d’une collection de base de données. Le coût associé à chacune de ces opérations varie en fonction du processeur, des E/S et de la mémoire nécessaires à l’exécution de l’opération. Plutôt que de vous soucier de la gestion des ressources matérielles, vous pouvez considérer une unité de requête (RU) comme une mesure unique des ressources nécessaires à l’exécution des opérations de base de données et à la réponse à la requête de l’application.

Le débit est approvisionné en fonction du nombre d’unités de requête défini pour chaque conteneur. La consommation d’unités de requête est évaluée en fonction d’un taux par seconde. Les applications qui dépassent le taux d’unités de requête configuré pour le conteneur associé sont limitées jusqu’à ce que le taux soit inférieur au niveau configuré pour le conteneur. Si votre application requiert un niveau de débit plus élevé, vous pouvez augmenter le débit en approvisionnant des unités de requête supplémentaires.

La complexité d’une requête a une incidence sur le nombre d’unités de requête consommées pour une opération. Le nombre de prédicats, la nature des prédicats, le nombre de fonctions définies par l’utilisateur et la taille du jeu de données sources ont tous une influence sur le coût des opérations de requête.

Pour mesurer les frais de l’opération (création, mise à jour ou suppression), inspectez l’en-tête x-ms-request-charge (ou la propriété RequestCharge équivalente dans ResourceResponse\<T> ou FeedResponse\<T> dans le SDK .NET) afin de déterminer le nombre d’unités de requête consommées par les opérations :

// 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);
    }

Les frais de la requête retournée dans cet en-tête correspondent à une fraction du débit configuré (c’est-à-dire 2 000 RU/seconde). Par exemple, si la requête ci-dessus renvoie 1 000 documents de 1 Ko, le coût de l’opération est de 1 000. Ainsi, en une seconde, le serveur honore uniquement deux requêtes de ce type avant de limiter le taux des requêtes ultérieures. Pour plus d’informations, consultez Unités de requête et la calculatrice d’unités de requête.

Gestion de la limite de taux/du taux de requête trop importants

Lorsqu’un client tente de dépasser le débit réservé pour un compte, les performances au niveau du serveur ne sont pas affectées et la capacité de débit n’est pas utilisée au-delà du niveau réservé. Le serveur met fin à la requête de manière préventive avec RequestRateTooLarge (code d’état HTTP 429). Il renvoie un en-tête x-ms-retry-after-ms qui indique la durée, en millisecondes, pendant laquelle l’utilisateur doit attendre avant de retenter la requête.

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

Les kits de développement logiciel (SDK) interceptent tous implicitement cette réponse, respectent l’en-tête retry-after spécifiée par le serveur, puis relancent la requête. La tentative suivante réussira toujours, sauf si plusieurs clients accèdent simultanément à votre compte.

Si plusieurs de vos clients opèrent simultanément et systématiquement au-delà du taux de requête, le nombre de nouvelles tentatives par défaut actuellement défini sur 9 en interne par le client ne suffira peut-être pas. Dans ce cas, le client envoie à l’application une DocumentClientException avec le code d’état 429.

Vous pouvez modifier le nombre de nouvelles tentatives par défaut en définissant les RetryOptions sur l’instance ConnectionPolicy. Par défaut, la DocumentClientException avec le code d’état 429 est retournée après un temps d’attente cumulé de 30 secondes si la requête continue à fonctionner au-dessus du taux de requête. Cette erreur est renvoyée même lorsque le nombre actuel de nouvelles tentatives est inférieur au nombre maximal de nouvelles tentatives, que la valeur actuelle soit la valeur par défaut 9 ou une valeur définie par l’utilisateur.

Le comportement de nouvelle tentative automatisée contribue à améliorer la résilience et la convivialité pour la plupart des applications. Mais cela peut ne pas être le meilleur comportement lorsque vous effectuez des benchmarks des performances, en particulier lorsque vous mesurez la latence. La latence client observée atteindra un pic si l’expérience atteint la limite de serveur et oblige le kit de développement logiciel (SDK) client à effectuer une nouvelle tentative en silence. Pour éviter des pics de latence lors des expériences de performances, mesurez la charge renvoyée par chaque opération et assurez-vous que les requêtes fonctionnent en dessous du taux de requête réservé. Pour plus d’informations, consultez Unités de requête.

Pour un débit plus élevé, concevez en prévision des documents plus petits

Les frais de requête (à savoir, le coût de traitement de requête) d’une opération donnée sont directement liés à la taille du document. Les opérations sur les documents volumineux coûtent plus cher que les opérations sur les petits documents.

Étapes suivantes

Pour obtenir un exemple d’application permettant d’évaluer Azure Cosmos DB lors de scénarios hautes performances sur quelques ordinateurs clients, consultez Test des performances et de la mise à l’échelle avec Azure Cosmos DB.

Pour en savoir plus sur la conception de votre application pour une mise à l’échelle et de hautes performances, consultez Partitionnement, clés de partition et mise à l’échelle dans Cosmos DB.