Concevoir une stratégie de partitionnement évolutive pour le stockage Table Azure
Cet article décrit le partitionnement d’une table dans le stockage Table Azure et les stratégies que vous pouvez utiliser pour garantir une scalabilité efficace.
Azure fournit un stockage cloud hautement disponible et hautement évolutif. Le système de stockage sous-jacent pour Azure est fourni par le biais d’un ensemble de services, notamment stockage Blob Azure, Stockage Table Azure, Stockage File d’attente Azure et Azure Files.
Le stockage Table Azure est conçu pour stocker des données structurées. Le service Stockage Azure prend en charge un nombre illimité de tables. Chaque table peut être mise à l’échelle à des niveaux massifs et fournir des téraoctets de stockage physique. Pour tirer le meilleur parti des tables, vous devez partitionner vos données de manière optimale. Cet article explore les stratégies que vous pouvez utiliser pour partitionner efficacement des données pour le stockage Table Azure.
Entités de table
Les entités de table représentent les unités de données stockées dans une table. Les entités de table sont similaires aux lignes d’une table de base de données relationnelle classique. Chaque entité définit une collection de propriétés. Chaque propriété est définie en tant que paire clé/valeur par son nom, sa valeur et le type de données de la valeur. Les entités doivent définir les trois propriétés système suivantes dans le cadre de la collection de propriétés :
PartitionKey : la propriété PartitionKey stocke des valeurs de chaîne qui identifient la partition à laquelle appartient une entité. Les partitions, comme nous l’aborderons plus loin, font partie intégrante de la scalabilité de la table. Les entités qui ont la même valeur PartitionKey sont stockées dans la même partition.
RowKey : la propriété RowKey stocke des valeurs de chaîne qui identifient de manière unique les entités au sein de chaque partition. PartitionKey et RowKey forment ensemble la clé primaire de l’entité.
Timestamp : la propriété Timestamp fournit la traçabilité pour une entité. Un horodatage est une valeur date/heure qui vous indique l’heure de la dernière modification de l’entité. Un horodatage est parfois appelé version de l’entité. Les modifications apportées aux horodatages sont ignorées, car le service de table conserve la valeur de cette propriété pendant toutes les opérations d’insertion et de mise à jour.
Clé primaire de table
La clé primaire d’une entité Azure se compose des propriétés PartitionKey et RowKey combinées . Les deux propriétés forment un index cluster unique au sein de la table. Les valeurs PartitionKey et RowKey peuvent avoir jusqu’à 1 024 caractères. Les chaînes vides sont également autorisées ; toutefois, les valeurs null ne sont pas autorisées.
L’index cluster trie par partitionKey dans l’ordre croissant, puis par RowKey dans l’ordre croissant. L'ordre de tri est appliqué dans les réponses aux requêtes. Des comparaisons lexicales sont utilisées lors de l'opération de tri. Une valeur de chaîne de « 111 » s’affiche avant la valeur de chaîne « 2 ». Dans certains cas, vous pouvez souhaiter que l’ordre de tri soit numérique. Pour trier dans un ordre numérique et croissant, vous devez utiliser des chaînes de longueur fixe et sans rembourrage. Dans l’exemple précédent, « 002 » s’affiche avant « 111 ».
Partitions de table
Les partitions représentent une collection d’entités avec les mêmes valeurs PartitionKey . Les partitions sont toujours servies à partir d’un serveur de partition. Chaque serveur de partition peut servir une ou plusieurs partitions. Un serveur de partitions a une limite quant au nombre d'entités qu'il peut traiter pour une partition pendant une période donnée. Plus précisément, une partition a une cible de scalabilité de 2 000 entités par seconde. Ce débit peut être plus élevé pendant une charge minimale sur le nœud de stockage, mais il est limité lorsque le nœud devient chaud ou actif.
Pour mieux illustrer le concept de partitionnement, la figure suivante montre un tableau qui contient un petit sous-ensemble de données pour les inscriptions aux événements de course à pied. La figure présente une vue conceptuelle du partitionnement où partitionKey contient trois valeurs différentes : le nom de l’événement combiné à trois distances (marathon complet, semi-marathon et 10 km). Cet exemple utilise deux serveurs de partition. Le serveur A contient les inscriptions pour le demi-marathon et les distances de 10 km. Le serveur B contient uniquement les distances complètes du marathon. Les valeurs RowKey sont affichées pour fournir un contexte, mais les valeurs ne sont pas significatives pour cet exemple.
Une table avec trois partitions
Extensibilité
Comme une partition est toujours traitée depuis un seul serveur de partitions et que chaque serveur de partitions peut traiter une ou plusieurs partitions, l'efficacité du traitement des entités est liée à l'intégrité du serveur. Les serveurs qui rencontrent un trafic élevé pour leurs partitions peuvent ne pas être en mesure de supporter un débit élevé. Par exemple, dans la figure précédente, si de nombreuses demandes de « Marathon__Half 2011 new-yorkais » sont reçues, le serveur A peut devenir trop chaud. Pour augmenter le débit du serveur, le système de stockage équilibre la charge des partitions avec d'autres serveurs. Le résultat est que trafic est distribué entre de nombreux autres serveurs. Pour optimiser l’équilibrage de charge du trafic, vous devez utiliser plus de partitions afin que le stockage Table Azure puisse distribuer les partitions à davantage de serveurs de partition.
Transactions de groupe d’entités
Une transaction de groupe d’entités est un ensemble d’opérations de stockage implémentées de manière atomique sur des entités qui ont la même valeur PartitionKey . Si une opération de stockage dans le groupe d’entités échoue, toutes les opérations de stockage dans l’entité sont restaurées. Une transaction de groupe d’entités ne comprend pas plus de 100 opérations de stockage et peut avoir une taille maximale de 4 Mio. Les transactions de groupe d’entités fournissent au stockage Table Azure une forme limitée de la sémantique ACID (atomicité, cohérence, isolation et durabilité) fournie par les bases de données relationnelles.
Les transactions de groupe d’entités améliorent le débit, car elles réduisent le nombre d’opérations de stockage individuelles qui doivent être soumises au stockage Table Azure. Les transactions de groupe d’entités offrent également un avantage économique. Une transaction de groupe d’entités est facturée en tant qu’opération de stockage unique, quel que soit le nombre d’opérations de stockage qu’elle contient. Étant donné que toutes les opérations de stockage dans une transaction de groupe d’entités affectent les entités qui ont la même valeur PartitionKey , la nécessité d’utiliser des transactions de groupe d’entités peut entraîner la sélection de la valeur PartitionKey .
Partitions de plage
Si vous utilisez des valeurs PartitionKey uniques pour vos entités, chaque entité appartient à sa propre partition. Si les valeurs uniques que vous utilisez augmentent ou diminuent en valeur, il est possible qu’Azure crée des partitions de plage. Les partitions de plage regroupent des entités qui ont des valeurs PartitionKey séquentielles et uniques pour améliorer les performances des requêtes de plage. Sans partitions de plage, une requête de plage doit traverser les limites de partition ou les limites du serveur, ce qui peut réduire les performances des requêtes. Considérez une application qui utilise le tableau suivant, qui a une valeur de séquence croissante pour PartitionKey :
PartitionKey | RowKey |
---|---|
"0001" | - |
"0002" | - |
"0003" | - |
"0004" | - |
"0005" | - |
"0006" | - |
Azure peut regrouper les trois premières entités dans une partition de plage. Si vous appliquez une requête de plage à la table qui utilise PartitionKey comme critère et demande des entités comprises entre « 0001 » et « 0003 », la requête peut s’exécuter efficacement, car les entités sont traitées à partir d’un serveur de partition unique. Il n’y a aucune garantie quand et comment une partition de plage sera créée.
L’existence de partitions de plage pour votre table peut affecter les performances de vos opérations d’insertion si vous insérez des entités qui ont des valeurs PartitionKey croissantes ou décroissantes. L’insertion d’entités qui ont des valeurs PartitionKey croissantes est appelée modèle d’ajout uniquement. L’insertion d’entités qui ont des valeurs décroissantes est appelée modèle de prépend uniquement. Envisagez de ne pas utiliser ces types de modèles, car le débit global de vos demandes d’insertion est limité par un serveur de partition unique. En effet, si des partitions de plage existent, la première et la dernière partition (plage) contiennent respectivement les valeurs PartitionKey la moins élevée et la plus grande. Par conséquent, l’insertion d’une nouvelle entité, dont la valeur PartitionKey est séquentiellement inférieure ou supérieure, cible l’une des partitions de fin. La figure suivante montre un ensemble possible de partitions de plage basées sur l’exemple précédent. Si un ensemble d’entités « 0007 », « 0008 » et « 0009 » était inséré, elles seraient affectées à la dernière partition (orange).
Ensemble de partitions de plage
Il est important de noter qu’il n’y a aucun effet négatif sur les performances si les opérations d’insertion utilisent des valeurs PartitionKey plus dispersées.
Analyser des données
Contrairement à une table d’une base de données relationnelle que vous pouvez utiliser pour gérer des index, les tables du stockage Table Azure ne peuvent avoir qu’un seul index. Un index dans stockage Table Azure se compose toujours des propriétés PartitionKey et RowKey .
Dans une table Azure, vous n’avez pas le luxe d’optimiser les performances de votre table en ajoutant d’autres index ou en modifiant une table existante après son déploiement. Vous devez analyser les données lors de la conception de votre table. Les aspects les plus importants à prendre en compte pour une scalabilité optimale et pour l’efficacité des requêtes et des insertions sont les valeurs PartitionKey et RowKey . Cet article explique comment choisir partitionkey , car il est directement lié à la façon dont les tables sont partitionnées.
Dimensionnement des partitions
Le dimensionnement des partitions est relatif au nombre d'entités que contient une partition. Comme nous l’abordons dans Scalabilité, le fait d’avoir plus de partitions vous permet d’obtenir un meilleur équilibrage de charge. La granularité de la valeur PartitionKey affecte la taille des partitions. Au niveau le plus grosseur, si une seule valeur est utilisée en tant que PartitionKey, toutes les entités se trouvent dans une partition unique qui est très grande. Au plus haut niveau de granularité, partitionKey peut contenir des valeurs uniques pour chaque entité. Le résultat est qu’il existe une partition pour chaque entité. Le tableau suivant présente les avantages et les inconvénients de la plage de granularités :
Granularité de PartitionKey | Taille de partition | Avantages | Inconvénients |
---|---|---|---|
Valeur unique | Petit nombre d'entités | Les transactions par lots sont possibles avec n’importe quelle entité. Toutes les entités sont locales et servies à partir du même nœud de stockage. |
|
Valeur unique | Grand nombre d'entités | Des transactions de groupe d’entités peuvent être possibles avec n’importe quelle entité. Pour plus d’informations sur les limites des transactions de groupe d’entités, consultez Exécution de transactions de groupe d’entités. | L'évolutivité est limitée. Le débit est limité par la performance d'un seul serveur. |
Valeurs multiples | Partitions multiples Les tailles de partition dépendent de la distribution des entités. |
Les transactions par lots sont possibles sur certaines entités. Le partitionnement dynamique est possible. Les requêtes à requête unique sont possibles (aucun jeton de continuation). L’équilibrage de charge sur d’autres serveurs de partition est possible. |
Une répartition très inégale des entités entre les partitions peut limiter les performances des partitions plus volumineuses et plus actives. |
Valeurs uniques | De nombreuses petites partitions | La table est hautement évolutive. Les partitions de plage peuvent améliorer les performances des requêtes de plage entre partitions. |
Les requêtes qui impliquent des plages peuvent nécessiter des visites sur plusieurs serveurs. Les transactions par lots ne sont pas possibles. Les modèles append-only ou prepend-only peuvent affecter le débit d’insertion. |
Le tableau montre comment la mise à l’échelle est affectée par les valeurs PartitionKey . Il est recommandé de privilégier les partitions plus petites, car elles offrent un meilleur équilibrage de charge. Les partitions plus volumineuses peuvent être appropriées dans certains scénarios, et elles ne sont pas nécessairement désavantageuses. Par exemple, si votre application ne nécessite pas de scalabilité, une seule grande partition peut être appropriée.
Déterminer les requêtes
Les requêtes récupèrent des données dans les tables. Lorsque vous analysez les données d’une table dans le stockage Table Azure, il est important de déterminer les requêtes que l’application utilisera. Si une application a plusieurs requêtes, vous devrez peut-être les hiérarchiser, même si vos décisions peuvent être subjectives. Dans de nombreux cas, les requêtes dominantes sont discernables des autres requêtes. En termes de performance, les requêtes peuvent être classées en différentes catégories. Étant donné qu’une table n’a qu’un seul index, les performances des requêtes sont généralement liées aux propriétés PartitionKey et RowKey . Le tableau suivant montre les différents types de requêtes et leurs évaluations de performances :
Type de requête | Correspondance PartitionKey | Correspondance RowKey | Évaluation des performances |
---|---|---|---|
Analyse des plages de valeurs des lignes | Exact | Partial | Mieux avec des partitions de plus petite taille. Mauvais avec les partitions qui sont très volumineuses. |
Analyse des plages de valeurs des partitions | Partiel | Partiel | Bon avec un petit nombre de serveurs de partition touchés. Pire avec plus de serveurs touchés. |
Analyse complète de la table | Partielle, aucune | Partielle, aucune | Pire avec un sous-ensemble de partitions en cours d’analyse. Pire avec toutes les partitions analysées. |
Notes
Le tableau définit les évaluations de la performance les unes par rapport aux autres. Le nombre et la taille des partitions peuvent finalement déterminer le fonctionnement de la requête. Par exemple, une analyse de plage de partitions pour une table qui a de nombreuses partitions volumineuses peut s’exécuter mal par rapport à une analyse de table complète pour une table contenant quelques petites partitions.
Les types de requêtes répertoriés dans le tableau précédent montrent une progression des meilleurs types de requêtes à utiliser vers les pires types, en fonction de leurs évaluations de performances. Les requêtes univoques sont les meilleurs types de requêtes à utiliser, car elles utilisent entièrement l'index cluster de la table. La requête de point suivante utilise les données de la table d’inscription des courses de pieds :
http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)
Si l'application utilise plusieurs requêtes, elles ne peuvent pas toutes être des requêtes univoques. En termes de performance, les requêtes de plages de données viennent après les requêtes univoques. Il existe deux types de requêtes de plage : l’analyse de plage de lignes et l’analyse de plage de partitions. L'analyse de plage de valeurs de lignes spécifie une seule partition. Étant donné que l’opération se produit sur un serveur de partition unique, les analyses de plage de lignes sont généralement plus efficaces que les analyses de plage de partition. Toutefois, un facteur clé dans les performances des analyses de plage de lignes est la sélection d’une requête. La sélectivité de la requête détermine combien de lignes doivent être parcourues pour trouver les lignes en correspondance. Des requêtes plus sélectives sont plus efficaces lors des analyses de plage de valeurs de lignes.
Pour évaluer les priorités de vos requêtes, tenez compte des exigences en matière de fréquence et de temps de réponse pour chaque requête. Les requêtes qui sont fréquemment exécutées peuvent être hiérarchisées plus haut. Toutefois, une requête importante mais rarement utilisée peut avoir des exigences de faible latence qui pourraient la classer plus haut dans la liste des priorités.
Choisir la valeur PartitionKey
Le cœur de la conception de toute table est sa scalabilité, les requêtes utilisées pour y accéder et les exigences en matière d’opérations de stockage. Les valeurs PartitionKey que vous choisissez déterminent la façon dont une table est partitionnée et le type de requêtes que vous pouvez utiliser. Les opérations de stockage, et en particulier les insertions, peuvent également affecter votre choix de valeurs PartitionKey . Les valeurs PartitionKey peuvent aller de valeurs uniques à des valeurs uniques. Ils peuvent également être créés à l’aide de plusieurs valeurs. Vous pouvez utiliser les propriétés d’entité pour former la valeur PartitionKey . L’application peut également calculer la valeur. Les sections suivantes traitent des considérations importantes.
Transactions de groupe d’entités
Les développeurs doivent d’abord déterminer si l’application utilisera des transactions de groupe d’entités (mises à jour par lots). Les transactions de groupe d’entités nécessitent que les entités aient la même valeur PartitionKey . En outre, étant donné que les mises à jour par lots concernent un groupe entier, les choix de valeurs PartitionKey peuvent être limités. Par exemple, une application bancaire qui gère des transactions en espèces doit les insérer dans la table selon le principe d'atomicité. Les transactions en espèces représentent à la fois le débit et le crédit et doivent être nettes à zéro. Cette exigence signifie que le numéro de compte ne peut pas être utilisé comme partie de la valeur PartitionKey , car chaque côté de la transaction utilise des numéros de compte différents. Au lieu de cela, un ID de transaction peut être un meilleur choix.
Partitions
Les nombres et tailles de partition affectent la scalabilité d’une table en cours de chargement. Elles sont également contrôlées par la précision des valeurs PartitionKey . Il peut être difficile de déterminer partitionKey en fonction de la taille de la partition, en particulier si la distribution des valeurs est difficile à prédire. Une bonne règle est d'utiliser plusieurs partitions de plus petite taille. De nombreuses partitions de table permettent au Stockage Table Azure de gérer plus facilement les nœuds de stockage à partir lesquels les partitions sont servies.
Le choix de valeurs uniques ou plus fines pour partitionKey entraîne des partitions plus petites, mais plus volumineuses. Cela est généralement favorable, car le système peut équilibrer la charge des nombreuses partitions pour répartir la charge entre de nombreuses partitions. Vous devez cependant considérer l'effet de ces nombreuses partitions sur les requêtes de plage de données interpartitions. Ces types de requêtes doivent visiter plusieurs partitions pour satisfaire une requête. Il est possible que les partitions soient distribuées sur de nombreux serveurs de partition. Si une requête dépasse la limite d'un serveur, des jetons de continuation doivent être retournés. Les jetons de continuation spécifient les valeurs PartitionKey ou RowKey suivantes pour récupérer le jeu de données suivant pour la requête. En d’autres termes, les jetons de continuation représentent au moins une demande supplémentaire adressée au service, ce qui peut dégrader les performances globales de la requête.
La sélectivité d'une requête est un autre facteur qui peut affecter sa performance. La sélectivité d'une requête est une mesure du nombre de lignes qui doivent être parcourues pour chaque partition. Plus une requête est sélective, plus elle est efficace pour retourner les lignes souhaitées. Les performances globales des requêtes de plage peuvent dépendre du nombre de serveurs de partition qui doivent être touchés ou de la façon dont la requête est sélective. Vous devez également éviter d’utiliser les modèles d’ajout uniquement ou d’ajout uniquement lorsque vous insérez des données dans votre table. Si vous utilisez ces modèles, malgré la création de partitions petites et nombreuses, vous pouvez limiter le débit de vos opérations d’insertion. Les modèles d’ajout uniquement et d’ajout uniquement sont abordés dans Partitions de plage.
Requêtes
Connaître les requêtes que vous allez utiliser peut vous aider à déterminer quelles propriétés sont importantes à prendre en compte pour la valeur PartitionKey . Les propriétés que vous utilisez dans les requêtes sont candidates pour la valeur PartitionKey . Le tableau suivant fournit des instructions générales sur la façon de déterminer la valeur PartitionKey :
Si l'entité… | Action |
---|---|
A une seule propriété clé | Utilisez-le comme PartitionKey. |
A deux propriétés clés | Utilisez l’un comme PartitionKey et l’autre comme RowKey. |
A plus de deux propriétés clés | Utilisez une clé composite de valeurs concaténées. |
S’il existe plusieurs requêtes également dominantes, vous pouvez insérer les informations plusieurs fois en utilisant différentes valeurs RowKey dont vous avez besoin. Votre application gère les lignes secondaires (ou tertiaires, etc.). Vous pouvez utiliser ce type de modèle pour répondre aux exigences de performances de vos requêtes. L’exemple suivant utilise les données de l’exemple d’inscription de course au pied. Il a deux requêtes dominantes :
- Requête par numéro de dossard
- Requête par âge
Pour traiter ces deux requêtes principales, insérez deux lignes comme transaction de groupe d'entités. Le tableau suivant présente les propriétés PartitionKey et RowKey pour ce scénario. Les valeurs RowKey fournissent un préfixe pour le dossard et l’âge afin que l’application puisse faire la distinction entre les deux valeurs.
PartitionKey | RowKey |
---|---|
Marathon de New York 2011__Complet | BIB:01234__John__M__55 |
Marathon de New York 2011__Complet | AGE:055__1234__John__M |
Dans cet exemple, une transaction de groupe d’entités est possible, car les valeurs PartitionKey sont identiques. La transaction de groupe fournit l’atomicité de l’opération d’insertion. Bien qu’il soit possible d’utiliser ce modèle avec des valeurs PartitionKey différentes, nous vous recommandons d’utiliser les mêmes valeurs pour bénéficier de cet avantage. Sinon, vous devrez peut-être écrire une logique supplémentaire pour garantir que les transactions atomiques utilisent des valeurs PartitionKey différentes.
Opérations de stockage
Les tables dans le stockage Table Azure peuvent rencontrer une charge non seulement à partir de requêtes. Ils peuvent également rencontrer une charge à partir d’opérations de stockage telles que les insertions, les mises à jour et les suppressions. Déterminez le type d’opérations de stockage que vous allez effectuer sur la table et à quelle vitesse. Si vous effectuez rarement ces opérations, vous n’aurez peut-être pas à vous en soucier. Toutefois, pour les opérations fréquentes telles que l’exécution de nombreuses insertions en peu de temps, vous devez prendre en compte la façon dont ces opérations sont servies en fonction des valeurs PartitionKey que vous choisissez. Les modèles en ajout uniquement et en ajout uniquement sont des exemples importants. Les modèles d’ajout uniquement et d’ajout uniquement sont abordés dans Partitions de plage.
Lorsque vous utilisez un modèle d’ajout uniquement ou de prépendation uniquement, vous utilisez des valeurs ascendantes ou décroissantes uniques pour partitionKey lors des insertions suivantes. Si vous combinez ce modèle avec des opérations d’insertion fréquentes, votre table ne pourra pas traiter les opérations d’insertion avec une grande scalabilité. La scalabilité de votre table est affectée, car Azure ne peut pas équilibrer la charge des demandes d’opération adressées à d’autres serveurs de partition. Dans ce cas, vous pouvez envisager d’utiliser des valeurs aléatoires, telles que des valeurs GUID. Ensuite, vos tailles de partition peuvent rester petites et maintenir l’équilibrage de charge pendant les opérations de stockage.
Test de contrainte de partition de table
Lorsque la valeur PartitionKey est complexe ou nécessite des comparaisons avec d’autres mappages PartitionKey , vous devrez peut-être tester les performances de la table. Le test doit examiner le fonctionnement d'une partition sous des charges intenses.
Pour effectuer un test de contrainte
- Créez une table de test.
- Chargez la table de test avec des données afin qu’elle contienne des entités qui ont la valeur PartitionKey que vous ciblez.
- Utilisez l’application pour simuler le pic de charge dans la table. Ciblez une partition unique à l’aide de la valeur PartitionKey de l’étape 2. Cette étape est différente pour chaque application, mais la simulation doit inclure toutes les requêtes et opérations de stockage requises. Vous devrez peut-être ajuster l’application afin qu’elle cible une seule partition.
- Examinez le débit des opérations GET ou PUT sur la table.
Pour examiner le débit, comparez les valeurs réelles à la limite spécifiée d'une seule partition sur un seul serveur. Les partitions sont limitées à 2 000 entités par seconde. Si le débit dépasse 2 000 entités par seconde pour une partition, le serveur peut s’exécuter trop à chaud dans un paramètre de production. Dans ce cas, les valeurs PartitionKey peuvent être trop grossières, de sorte qu’il n’y a pas suffisamment de partitions ou que les partitions sont trop volumineuses. Vous devrez peut-être modifier la valeur PartitionKey afin que les partitions soient distribuées entre d’autres serveurs.
Équilibrage de charge
L’équilibrage de charge au niveau de la couche de partition se produit lorsqu’une partition devient trop chaude. Lorsqu’une partition est trop chaude, la partition, en particulier le serveur de partition, fonctionne au-delà de sa scalabilité cible. Pour le stockage Azure, chaque partition a une cible de scalabilité de 2 000 entités par seconde. L’équilibrage de charge se produit également au niveau de la couche du système de fichiers distribué (DFS).
L’équilibrage de charge au niveau de la couche DFS traite de la charge d’E/S et n’entre pas dans le cadre de cet article. L’équilibrage de charge au niveau de la couche de partition ne se produit pas immédiatement après le dépassement de la cible d’extensibilité. Au lieu de cela, le système attend quelques minutes avant de commencer le processus d’équilibrage de charge. Ceci permet de garantir qu'une partition est réellement soumise à une charge intense. Il n’est pas nécessaire de créer des partitions avec une charge générée qui déclenche l’équilibrage de charge, car le système effectue automatiquement la tâche.
Si une table a été amorcée avec une certaine charge, le système peut être en mesure d’équilibrer les partitions en fonction de la charge réelle, ce qui entraîne une distribution sensiblement différente des partitions. Au lieu d’amorcer des partitions, envisagez d’écrire du code qui gère les erreurs de délai d’attente et de disponibilité du serveur. Les erreurs sont retournées lorsque le système est en cours d’équilibrage de charge. En gérant ces erreurs à l’aide d’une stratégie de nouvelle tentative, votre application peut mieux gérer les pics de charge. Les stratégies de nouvelle tentative sont présentées plus en détails dans la section suivante.
Lorsque l’équilibrage de charge se produit, la partition devient hors connexion pendant quelques secondes. Pendant la période hors connexion, le système réaffecte la partition à un autre serveur de partition. Il est important de noter que vos données ne sont pas stockées par les serveurs de partition. Au lieu de cela, les serveurs de partitions traitent des entités de la couche DFS. Étant donné que vos données ne sont pas stockées au niveau de la couche de partition, le déplacement de partitions vers différents serveurs est un processus rapide. Cette flexibilité limite considérablement le temps d’arrêt, le cas échéant, que votre application peut rencontrer.
Stratégie de nouvelle tentative
Il est important que votre application gère les échecs d’opération de stockage pour vous assurer que vous ne perdez aucune mise à jour de données. Certaines défaillances ne nécessitent pas de stratégie de nouvelle tentative. Par exemple, les mises à jour qui retournent une erreur non autorisée 401 ne bénéficient pas de la nouvelle tentative de l’opération, car il est probable que l’état de l’application ne change pas entre les nouvelles tentatives qui résolvent l’erreur 401. Toutefois, les erreurs telles que Server Busy ou Timeout sont liées aux fonctionnalités d’équilibrage de charge d’Azure qui fournissent une scalabilité de table. Lorsque les nœuds de stockage qui servent vos entités deviennent chauds, Azure équilibre la charge en déplaçant les partitions vers d’autres nœuds. Pendant ce temps, la partition peut être inaccessible, ce qui entraîne des erreurs d’occupation du serveur ou de délai d’attente. Finalement, la partition est réactivée et les mises à jour reprennent.
Une stratégie de nouvelle tentative est appropriée pour les erreurs d’occupation du serveur ou de délai d’attente. Dans la plupart des cas, vous pouvez exclure des erreurs de niveau 400 et quelques erreurs de niveau 500 de la logique de nouvelle tentative. Les erreurs qui peuvent être exclues incluent la version 501 Non implémentée et la version HTTP 505 non prise en charge. Ensuite, vous pouvez implémenter une stratégie de nouvelle tentative pour les erreurs de niveau 500 maximum, telles que Server Busy (503) et Timeout (504).
Vous pouvez choisir parmi trois stratégies de nouvelle tentative courantes pour votre application :
- Aucune nouvelle tentative : aucune nouvelle tentative n’est effectuée.
- Backoff fixe : l’opération est retentée N fois avec une valeur de backoff constante.
- Backoff exponentiel : l’opération est retentée N fois avec une valeur de backoff exponentielle.
La stratégie Pas de nouvelle tentative est un moyen simple (et de l'ordre de l'évitement) de gérer les échecs des opérations de mise à jour. Toutefois, une stratégie Sans nouvelle tentative n’est pas très utile. Le fait de n'imposer aucune nouvelle tentative fait prendre des risques évidents, avec des données qui ne sont pas stockées correctement après l'échec de certaines opérations. Une meilleure stratégie consiste à utiliser la stratégie Backoff fixe. qui offre la possibilité de réessayer des opérations avec la même durée d’interruption.
Toutefois, la stratégie n’est pas optimisée pour la gestion des tables hautement évolutives. Si de nombreux threads ou processus attendent la même durée, des collisions peuvent se produire. Une stratégie de nouvelle tentative recommandée est une stratégie qui utilise un backoff exponentiel où chaque nouvelle tentative est plus longue que la dernière tentative. Il est similaire à l’algorithme d’évitement de collision utilisé dans les réseaux informatiques, tels qu’Ethernet. L'interruption exponentielle utilise un facteur aléatoire pour fournir une variation supplémentaire à l'intervalle résultant. La valeur de l'interruption est ensuite contrainte à une limite minimale et à une limite maximale. La formule suivante peut être utilisée pour calculer la valeur de l'interruption suivante, à l'aide d'un algorithme exponentiel :
y = Rand(0,8z , 1,2z)(2x-1
y = Min(zmin + y, zmax
Où :
z = interruption par défaut en millisecondes
zmin = interruption minimale par défaut en millisecondes
zmax = interruption maximale par défaut en millisecondes
x = le nombre de nouvelles tentatives
y = la valeur de l'interruption en millisecondes
Les multiplicateurs 0,8 et 1,2 utilisés dans la fonction Rand (aléatoire) produisent une variance aléatoire de l’inverse par défaut dans ±20 % de la valeur d’origine. La plage ±20 % est acceptable pour la plupart des stratégies de nouvelles tentatives, et elle empêche d’autres collisions. La formule peut être implémentée à l’aide du code suivant :
int retries = 1;
// Initialize variables with default values
var defaultBackoff = TimeSpan.FromSeconds(30);
var backoffMin = TimeSpan.FromSeconds(3);
var backoffMax = TimeSpan.FromSeconds(90);
var random = new Random();
double backoff = random.Next(
(int)(0.8D * defaultBackoff.TotalMilliseconds),
(int)(1.2D * defaultBackoff.TotalMilliseconds));
backoff *= (Math.Pow(2, retries) - 1);
backoff = Math.Min(
backoffMin.TotalMilliseconds + backoff,
backoffMax.TotalMilliseconds);
Résumé
Une application dans stockage Table Azure peut stocker une quantité massive de données, car le stockage Table gère et réattribue les partitions sur de nombreux nœuds de stockage. Vous pouvez utiliser le partitionnement des données pour contrôler l'évolutivité des tables. Planifiez à l’avance lorsque vous définissez un schéma de table pour vous assurer que vous implémentez des stratégies de partitionnement efficaces. Plus précisément, analysez les exigences, les données et les requêtes de l’application avant de sélectionner PartitionKey Values . Chaque partition peut être réaffectée à différents nœuds de stockage à mesure que le système répond au trafic. Utilisez un test de contrainte de partition pour vous assurer que la table a les valeurs PartitionKey correctes . Ce test vous aide à déterminer quand les partitions sont trop chaudes et vous aide à effectuer les ajustements de partition nécessaires.
Pour vous assurer que votre application gère les erreurs intermittentes et que vos données sont conservées, utilisez une stratégie de nouvelle tentative avec backoff. La stratégie de nouvelles tentatives par défaut utilisée par la bibliothèque cliente stockage Azure a un backoff exponentiel qui évite les collisions et optimise le débit de votre application.