Conseils de mise en cache

La mise en cache est une technique courante qui vise à améliorer les performances et l’extensibilité d’un système. La mise en cache copie temporairement les données fréquemment consultées dans le stockage situé plus près de l’application que la source d’origine. Cette approche peut améliorer considérablement les temps de réponse pour les applications clientes en fournissant des données plus rapidement.

La mise en cache est la plus efficace lorsqu’une instance cliente lit à plusieurs reprises les mêmes données, en particulier si toutes les conditions suivantes s’appliquent au magasin de données d’origine :

  • Le magasin de données reste relativement statique.
  • Il est lent par rapport à la vitesse du cache.
  • Il est soumis à un niveau élevé de contention.
  • Il est assez loin des clients que la latence du réseau est importante.

Mise en cache dans les applications distribuées

Les applications distribuées implémentent généralement l’une ou l’autre des stratégies suivantes lors de la mise en cache des données :

  • Utilisez un cache privé, où vous conservez des données localement sur l’ordinateur qui exécute une application ou un service.

  • Utilisez un cache partagé, qui sert de source commune auquel plusieurs processus et machines peuvent accéder.

Dans les deux cas, vous pouvez effectuer la mise en cache côté client et côté serveur. Le processus qui fournit l’interface utilisateur d’un système, tel qu’un navigateur web ou une application de bureau, gère la mise en cache côté client. La mise en cache côté serveur est effectuée à distance par le processus qui fournit les services métier.

Mise en cache privée

Le type de cache le plus simple est un magasin en mémoire. Il est conservé dans l’espace d’adressage d’un seul processus et le code qui s’exécute dans ce processus accède directement au cache. Ce type de cache est rapide à accéder. Il peut également fournir un moyen efficace de stocker des quantités modestes de données statiques. La quantité de mémoire disponible sur l’ordinateur limite généralement la taille d’un cache.

Si vous devez mettre en cache plus d’informations que ce qui est physiquement possible en mémoire, vous pouvez écrire des données mises en cache dans le système de fichiers local. L’accès aux données à partir du système de fichiers prend plus de temps que si les données sont stockées en mémoire, mais elles doivent toujours être plus rapides et plus fiables que la récupération de données sur un réseau.

Si vous avez plusieurs instances d’une application qui utilise ce modèle en cours d’exécution simultanément, chaque instance d’application a son propre cache indépendant qui contient sa propre copie des données.

Considérez un cache comme un instantané des données d’origine à un moment donné dans le passé. Si ces données ne sont pas statiques, il est probable que différentes instances d’application contiennent différentes versions des données dans leurs caches. Par conséquent, la même requête effectuée par ces instances peut retourner des résultats différents, comme illustré dans le diagramme suivant.

Diagramme montrant les résultats de l’utilisation d’un cache en mémoire dans différentes instances d’une application.

Diagramme montrant l’incohérence du cache entre plusieurs instances d’application connectées à une base de données SQL partagée. À droite est une base de données SQL représentée sous forme de cylindre. En haut à gauche se trouve un grand cercle étiqueté « instance d’application A », et en bas à gauche se trouve un cercle de taille similaire étiqueté « instance d’application B ». À l’intérieur de l’instance d’application A se trouve une icône en forme d’engrenage représentant le processus d’application. Sous l’icône en forme d’engrenage se trouve un tableau quadrillé représentant une mémoire cache. À l'extérieur de la limite figure une étiquette indiquant « le cache est un instantané des données à l'instant X ». À l'intérieur de l'instance d'application B se trouve une autre icône en forme d'engrenage. Sous l’icône en forme d’engrenage se trouve un tableau quadrillé représentant une mémoire cache. En dehors de la limite, un cache de lecture d’étiquette est un instantané des données au moment Y. À partir de la base de données SQL, une ligne s’étend vers l’instance d’application A, l’instance d’application étiquetée A récupère les données au moment X et les met en cache en mémoire. Une deuxième ligne s’étend de la base de données SQL vers l’instance d’application B, l’instance d’application étiquetée B récupère les données au moment Y et les met en cache en mémoire. Entre ces deux lignes d’annotation se trouve une note indiquant que les informations de la base de données changent entre l’instant X et l’instant Y.

Mise en cache partagée

En utilisant un cache partagé, vous pouvez vous assurer que toutes les instances d’application voient la même vue des données mises en cache. Le cache se trouve dans un emplacement distinct, qui est généralement hébergé dans le cadre d’un service distinct, comme illustré dans le diagramme suivant.

Diagramme montrant les résultats des applications à l’aide d’un cache partagé.

Diagramme montrant comment un service de cache partagé résout l’incohérence du cache entre plusieurs instances d’application connectées à une base de données SQL. À l’extrême droite est une base de données SQL représentée sous forme de cylindre. Au centre se trouve une boîte intitulée « service de cache partagé », contenant un tableau en grille qui représente le cache partagé. À l’extrême gauche se trouve une icône d’engrenage étiquetée instance d’application A. En dessous, il existe une deuxième icône d’engrenage intitulée instance d’application B. Une flèche noire pointe de la base de données SQL vers la grille du service de cache partagé, indiquant que la base de données remplit le cache partagé. À partir du service de cache partagé, deux flèches bleues s’étendent vers l’extérieur, une bascule vers l’instance d’application A et une flèche vers l’instance d’application B. À l’extrême gauche, un crochet courbé s’étend verticalement entre les deux instances d’application, avec une étiquette adjacente lisant « Les deux instances d’application voient les mêmes données mises en cache ». Cette étiquette et ce crochet mettent en évidence l’avantage clé de l’architecture du cache partagé : contrairement à un cache en mémoire par instance, les instances d’application A et B récupèrent les données du même cache centralisé.

Un avantage important de l’approche de mise en cache partagée est l’extensibilité. De nombreux services de cache partagé sont implémentés à l’aide d’un cluster de serveurs et utilisent des logiciels pour distribuer les données sur le cluster de manière transparente. Une instance d’application envoie une demande au service de cache. L’infrastructure sous-jacente détermine l’emplacement des données mises en cache dans le cluster. Vous pouvez facilement mettre à l’échelle le cache en ajoutant d’autres serveurs.

Il existe deux inconvénients principaux de l’approche de mise en cache partagée :

  • Le cache est plus lent à accéder, car il n’est plus conservé localement avec chaque instance d’application.
  • L’implémentation d’un service de cache distinct peut ajouter de la complexité à la solution.

Considérations relatives à l’utilisation de la mise en cache

Les sections suivantes décrivent les considérations relatives à la conception et à l’utilisation d’un cache.

Décider quand mettre en cache des données

La mise en cache peut considérablement améliorer les performances, l’extensibilité et la disponibilité. Plus vous disposez de données et plus le nombre d’utilisateurs qui doivent accéder à ces données, plus les avantages de la mise en cache sont importants. La mise en cache réduit la latence et la contention associées à la gestion de grands volumes de requêtes simultanées dans le magasin de données d’origine.

Par exemple, une base de données peut prendre en charge un nombre limité de connexions simultanées. Toutefois, la récupération de données à partir d’un cache partagé, plutôt que la base de données sous-jacente, permet à une application cliente d’accéder à ces données même si le nombre de connexions disponibles est actuellement épuisé. En outre, si la base de données devient indisponible, les applications clientes peuvent continuer à utiliser les données dans le cache.

Envisagez de mettre en cache les données qui sont lues fréquemment mais modifiées rarement. Par exemple, les données de cache qui ont une proportion plus élevée d’opérations de lecture que d’opérations d’écriture. Toutefois, n’utilisez pas le cache comme source de référence pour les informations critiques. Au lieu de cela, enregistrez toutes les modifications importantes apportées à un magasin de données persistant. Si le cache n’est pas disponible, votre application peut continuer à fonctionner à l’aide du magasin de données et vous ne perdez pas d’informations importantes.

Déterminer comment mettre en cache efficacement les données

Pour utiliser efficacement un cache, déterminez les données les plus appropriées à mettre en cache et mettez-les en cache au bon moment. Vous pouvez ajouter des données au cache lorsqu’une application la récupère pour la première fois. L’application doit extraire les données une seule fois à partir du magasin de données, puis l’accès ultérieur peut être satisfait à l’aide du cache.

Vous pouvez également remplir partiellement ou entièrement un cache avec des données à l’avance, généralement au démarrage de l’application. Cette approche est appelée amorçage. Toutefois, il est peut-être déconseillé d’implémenter l’amorçage pour un cache volumineux, car cette approche peut imposer une charge soudaine et élevée sur le magasin de données d’origine lorsque l’application commence à s’exécuter.

Analysez les modèles d’utilisation pour décider s’il faut remplir entièrement ou partiellement un cache et choisir les données à mettre en cache. Par exemple, vous pouvez précharger le cache avec les données statiques du profil utilisateur pour les clients qui utilisent l’application tous les jours, mais pas pour les clients qui ne l’utilisent qu’une fois par semaine.

La mise en cache fonctionne généralement correctement avec les données immuables ou qui changent rarement. Les exemples incluent des informations de référence telles que des informations de produit et de tarification dans une application de commerce électronique ou des ressources statiques partagées coûteuses à construire. Chargez certaines ou toutes ces données dans le cache au démarrage de l’application pour réduire la demande en ressources et améliorer les performances. Vous pouvez également avoir un processus en arrière-plan qui met régulièrement à jour les données de référence dans le cache pour vous assurer qu’il est up-to-date. Ou bien, le processus en arrière-plan peut actualiser le cache lorsque les données de référence changent.

La mise en cache est moins utile pour les données dynamiques, bien qu’il existe certaines exceptions. Pour plus d’informations, consultez la section Mettre en cache des données hautement dynamiques plus loin dans cet article. Lorsque les données d’origine changent régulièrement, les informations mises en cache deviennent obsolètes rapidement ou la surcharge de synchronisation du cache avec le magasin de données d’origine réduit l’efficacité de la mise en cache.

Un cache n’a pas besoin d’inclure les données complètes d’une entité. Par exemple, si un élément de données représente un objet à valeurs multiples, tel qu’un client bancaire qui a un nom, une adresse et un solde de compte, certains de ces éléments peuvent rester statiques, tels que le nom et l’adresse. D’autres éléments, tels que le solde du compte, peuvent être plus dynamiques. Dans ces situations, il peut être utile de mettre en cache les parties statiques des données et de récupérer (ou calculer) uniquement les informations restantes si nécessaire.

Effectuez des tests de performance et une analyse de l’utilisation afin de déterminer si le préremplissage du cache, le chargement à la demande, ou une combinaison des deux, est approprié. Basez la décision sur la volatilité et le modèle d’utilisation des données. L’utilisation du cache et l’analyse des performances sont importantes dans les applications qui rencontrent des charges lourdes et doivent être hautement évolutives. Par exemple, dans des scénarios hautement évolutifs, vous pouvez précharger le cache afin de réduire la charge sur le stockage de données aux heures de pointe.

La mise en cache peut également être utilisée pour éviter les calculs répétés pendant l’exécution de l’application. Si une opération transforme des données ou effectue un calcul compliqué, elle peut enregistrer les résultats de l’opération dans le cache. Si le même calcul est requis par la suite, l’application peut récupérer les résultats à partir du cache.

Une application peut modifier des données qui se trouvent dans un cache. Toutefois, considérez le cache comme un magasin de données temporaire qui pourrait disparaître à tout moment. Ne stockez pas de données précieuses dans le cache uniquement ; veillez également à conserver les informations dans le magasin de données d’origine. Cette approche réduit le risque de perte de données si le cache devient indisponible.

Maintenir la cohérence du cache lors des écritures

Lorsque votre application modifie les données qu’elle met également en cache, déterminez la façon dont le cache reste cohérent avec le système d’enregistrement. Deux approches sont courantes :

  • Invalider en écriture : Écrivez la modification dans le magasin de données, puis supprimez l’entrée de cache affectée. La lecture suivante remplit le cache à partir du magasin de données. Cette approche permet de simplifier le chemin d’écriture, mais un lecteur peut rencontrer une absence de cache ou voir brièvement les données obsolètes immédiatement après une écriture. Le modèleCache-Aside décrit cette approche.

  • Écriture directe : Mettez à jour le magasin de données et le cache dans le cadre de la même opération d’écriture et retournez la réponse d’écriture uniquement une fois les deux mises à jour réussies. Cette approche donne aux lecteurs la valeur mise à jour immédiatement après une écriture réussie, au coût d’une latence d’écriture plus élevée et une logique de coordination plus élevée.

    Pour un exemple de bout en bout qui utilise Azure Functions pour coordonner des mises à jour write-through vers Azure SQL Database et Azure Managed Redis, consultez Mise en cache write-through avec Azure Managed Redis et Azure SQL Database.

Utilisez la mise en cache en écriture immédiate uniquement pour les chemins d'accès à forte lecture qui nécessitent une cohérence en lecture après écriture. Pour les données rarement lues après l’écriture ou qui changent constamment, invalidez l’écriture ou contournez le cache.

Mettre en cache des données hautement dynamiques

Le stockage rapide des informations dans un magasin de données persistant peut imposer une surcharge sur le système. Par exemple, considérez un appareil qui signale continuellement l’état ou une autre mesure. Si une application choisit de ne pas mettre en cache ces données en fonction du fait que les informations mises en cache sont généralement obsolètes, la même considération peut être vraie lors du stockage et de la récupération de ces informations à partir du magasin de données. Dans le temps nécessaire à l’enregistrement et à l’extraction de ces données, il peut changer.

Dans une situation telle que celle-ci, tenez compte des avantages du stockage des informations dynamiques directement dans le cache plutôt que dans le magasin de données persistant. Si les données ne sont pas critiques et ne nécessitent pas d’audit, cela n’a pas d’importance si la modification occasionnelle est perdue.

Gérer l’expiration des données dans un cache

Dans la plupart des cas, un cache contient des données qui sont une copie de données à partir du magasin de données d’origine. Les données du magasin de données d’origine peuvent changer une fois que vous l’avez mis en cache, ce qui peut rendre les données mises en cache obsolètes. De nombreux systèmes de mise en cache vous permettent de configurer le cache pour expirer les données, ce qui réduit la période pendant laquelle les données peuvent être obsolètes.

Une fois les données mises en cache expirées, le cache le supprime. L’application récupère ensuite de nouvelles données à partir du magasin de données d’origine et peut remplacer les données expirées dans le cache. Vous pouvez définir une stratégie d’expiration par défaut lorsque vous configurez le cache. Dans de nombreux services de cache, vous pouvez également spécifier la période d’expiration des objets individuels lorsque vous les stockez par programmation dans le cache. Certains caches vous permettent de spécifier la période d’expiration comme valeur absolue ou comme valeur glissante si l’élément n’est pas accessible dans un délai spécifié. Ce paramètre remplace toute stratégie d’expiration à l’échelle du cache, mais uniquement pour les objets spécifiés.

Note

Tenez compte de la période d’expiration du cache et des objets qu’il contient avec soin. Si vous le faites trop court, les objets expirent trop rapidement et vous réduisez les avantages de l’utilisation du cache. Si vous allongez trop la période, vous risquez que les données deviennent périmées.

Si vous autorisez les données à rester dans le cache pendant longtemps, le cache peut se remplir. Dans ce cas, toutes les demandes d’ajout de nouveaux éléments au cache peuvent entraîner la suppression forcée de certains éléments dans un processus appelé éviction. Les services de cache suppriment généralement les données au moins récemment utilisées (LRU), mais vous pouvez généralement remplacer cette stratégie et empêcher les éléments d’être supprimés. Toutefois, si vous adoptez cette approche, vous risquez de dépasser la mémoire disponible dans le cache. Une application qui tente d’ajouter un élément à un cache complet échoue avec une exception.

Certaines implémentations de mise en cache peuvent fournir d’autres stratégies d’éviction. Les types de stratégies d’éviction sont les suivants :

  • Stratégie la plus récente utilisée : supprime les éléments les plus récemment utilisés du cache dans l’attente que les données ne seront plus nécessaires.
  • Une politique de premier entré, premier sorti : supprime d’abord les données les plus anciennes du cache en premier.
  • Stratégie de suppression explicite : supprime les éléments du cache en fonction d’un événement déclencheur, tel que les données en cours de modification.

Invalider les données dans un cache côté client

Les données conservées dans un cache côté client sont considérées comme extérieures au contrôle du service qui fournit les données au client. Un service ne peut pas forcer directement un client à ajouter ou supprimer des informations d’un cache côté client.

Cette limitation signifie qu’un client qui utilise un cache mal configuré peut continuer à utiliser des informations obsolètes. Par exemple, si les stratégies d’expiration du cache ne sont pas correctement implémentées, un client peut utiliser des informations obsolètes mises en cache localement lorsque les informations de la source de données d’origine changent.

Si vous créez une application web qui sert des données via une connexion HTTP, vous pouvez forcer implicitement un client web, tel qu’un navigateur ou un proxy web, pour extraire les informations les plus récentes. Vous pouvez déclencher cette actualisation en modifiant l’URI chaque fois que vous mettez à jour la ressource. Les clients web utilisent généralement l’URI d’une ressource comme clé dans le cache côté client. Par conséquent, si l’URI change, le client web ignore les versions précédemment mises en cache d’une ressource et récupère la nouvelle version à la place.

Gérer la concurrence dans un cache

Souvent, vous concevez des caches à partager par plusieurs instances d’une application. Chaque instance d’application peut lire et modifier des données dans le cache, de sorte que les mêmes problèmes d’accès concurrentiel qui surviennent avec n’importe quel magasin de données partagé s’appliquent également à un cache. Dans une situation où une application doit modifier les données conservées dans le cache, vous devrez peut-être vous assurer que les mises à jour effectuées par une instance de l’application ne remplacent pas les modifications apportées par une autre instance.

En fonction de la nature des données et de la probabilité de collisions, adoptez l’une des deux approches de concurrence :

  • Optimiste : Avant que l’application met à jour les données, elle vérifie si les données dans le cache ont été modifiées depuis qu’elles ont été récupérées. Si les données sont toujours identiques, l’application apporte la modification. Sinon, l’application décide s’il faut le mettre à jour. La logique métier qui détermine cette décision est propre à l’application. Cette approche convient aux situations où les mises à jour sont peu fréquentes ou où les collisions sont peu susceptibles de se produire.

  • Pessimiste : Lorsque l’application récupère les données, elle verrouille les données dans le cache pour empêcher une autre instance de la modifier. Ce processus garantit que les collisions ne peuvent pas se produire, mais elles peuvent également bloquer d’autres instances qui doivent traiter les mêmes données. La concurrence pessimiste peut affecter l’extensibilité d’une solution et est recommandée uniquement pour les opérations de courte durée. Cette approche peut être appropriée pour les situations où les collisions sont plus probables, en particulier si une application met à jour plusieurs éléments dans le cache et doit s’assurer que ces modifications sont appliquées de manière cohérente.

Implémenter la haute disponibilité et la scalabilité, et améliorer les performances

Évitez d’utiliser un cache comme référentiel principal de données. Le magasin de données d’origine, à partir duquel le cache est rempli, sert ce rôle. Le magasin de données d’origine garantit la persistance des données.

Veillez à ne pas introduire de dépendances critiques sur la disponibilité d’un service de cache partagé dans vos solutions. Une application doit pouvoir continuer à fonctionner si le cache partagé n’est pas disponible. L’application ne doit pas répondre ou échouer en attendant que le service de cache reprend.

Par conséquent, l’application doit être prête à détecter la disponibilité du service de cache et à revenir au magasin de données d’origine si le cache est inaccessible. Le modèleCircuit-Breaker est utile pour gérer ce scénario. Le service qui fournit le cache peut être récupéré et, une fois qu’il est disponible, le cache peut être rerempli à mesure que les données sont lues à partir du magasin de données d’origine, en suivant une stratégie telle que le modèleCache-Aside.

Toutefois, l’extensibilité du système peut être affectée si l’application revient au magasin de données d’origine lorsque le cache est temporairement indisponible. Pendant la restauration du cache, le stockage de données d’origine peut être submergé par des requêtes de données, ce qui entraîne des dépassements de délai et des échecs de connexion.

Envisagez d’implémenter un cache local privé dans chaque instance d’une application, ainsi que le cache partagé auquel toutes les instances d’application accèdent. Lorsque l’application récupère un élément, elle peut d’abord vérifier dans son cache local, puis dans le cache partagé et enfin dans le magasin de données d’origine. Le cache local peut être rempli à l’aide des données dans le cache partagé ou dans la base de données si le cache partagé n’est pas disponible.

Cette approche nécessite une configuration minutieuse pour empêcher le cache local de devenir trop obsolète en ce qui concerne le cache partagé. Toutefois, le cache local agit comme mémoire tampon si le cache partagé est inaccessible, comme illustré dans le diagramme suivant.

Diagramme montrant la structure d’un cache partagé qui peut utiliser un cache privé local comme mémoire tampon.

Diagramme montrant une architecture de mise en cache hybride. À l’extrême droite est une base de données SQL représentée sous forme de cylindre. Dans la zone supérieure droite se trouve un grand cercle étiqueté service de cache partagé, contenant une table de grille représentant le cache partagé. Une flèche pointe vers la gauche de la base de données SQL dans la grille du service de cache partagé, indiquant que la base de données remplit le cache partagé. En haut à gauche se trouve un grand cercle intitulé « instance d’application A ». À l’intérieur du cercle, une icône d’engrenage se trouve en haut, et en dessous se trouve un tableau en grille représentant un cache privé local. Une flèche noire double tête connecte l’icône d’engrenage à la grille du cache local, indiquant une relation en lecture-écriture entre le processus d’application et son cache local. En bas à gauche se trouve une grande instance d’application étiquetée en cercle B, avec la même structure interne. À partir du service de cache partagé, deux flèches bleues s’étendent vers l’extérieur : l’une est orientée en diagonale vers le haut, en direction de l’icône d’engrenage de l’instance d’application A, et l’autre est orientée en diagonale vers le bas, en direction de l’icône d’engrenage de l’instance d’application B. Une ligne noire rectangulaire forme un chemin de secours qui part de la base de données SQL, longe les bords supérieur et droit, puis descend et suit le bord inférieur, rejoignant de nouveau les grilles de cache locales dans les deux instances d’application. Une étiquette au centre inférieur du diagramme lit, si le service de cache partagé n’est pas disponible, la logique d’application remplit le cache local à partir de la base de données. À l’extrême gauche, un crochet courbé s’étend entre les instances d’application A et B, avec une lecture d’étiquette adjacente, si le service de cache partagé n’est pas disponible, les instances d’application peuvent continuer à fonctionner à l’aide de caches locaux et privés.

Pour prendre en charge les grands caches qui contiennent des données relativement durables, certains services de cache offrent une option de haute disponibilité qui implémente la reprise automatique si le cache devient indisponible. Cette approche implique généralement la réplication des données mises en cache stockées sur un serveur de cache principal vers un serveur de cache secondaire et le basculement vers le serveur secondaire si le serveur principal échoue ou si la connectivité est perdue.

Pour réduire la latence qui provient de l’écriture dans plusieurs destinations, la réplication vers le serveur secondaire peut se produire de manière asynchrone lorsque les données sont écrites dans le cache sur le serveur principal. Cette approche entraîne la possibilité que certaines informations mises en cache soient perdues en cas d’échec, mais la proportion de ces données doit être faible, par rapport à la taille globale du cache.

Si un cache partagé est volumineux, il peut être utile de partitionner les données mises en cache entre les nœuds afin de réduire les chances de contention et d’améliorer l’extensibilité. De nombreux caches partagés prennent en charge la possibilité d’ajouter et de supprimer dynamiquement des nœuds et de rééquilibrer les données entre les partitions. Cette approche peut impliquer le clustering, dans lequel la collection de nœuds est présentée aux applications clientes en tant que cache unique. Toutefois, en interne, les données sont dispersées entre les nœuds à la suite d’une stratégie de distribution prédéfinie qui équilibre uniformément la charge. Pour plus d’informations, consultez le modèle de partitionnement.

Le clustering peut également augmenter la disponibilité du cache. En cas d’échec d’un nœud, le reste du cache est toujours accessible. Le clustering est fréquemment utilisé avec la réplication et le basculement. Chaque nœud peut être répliqué et le réplica peut être rapidement mis en ligne en cas d’échec du nœud.

De nombreuses opérations de lecture et d’écriture sont susceptibles d’impliquer des valeurs ou des objets de données uniques. Toutefois, il peut être nécessaire de stocker ou de récupérer rapidement de grands volumes de données. Par exemple, l’amorçage d’un cache peut impliquer l’écriture des centaines, voire de milliers d’éléments dans le cache. Une application peut également avoir besoin de récupérer un grand nombre d’éléments connexes à partir du cache dans le cadre de la même requête.

De nombreux caches à grande échelle fournissent des opérations de traitement par lots à ces fins. Cette fonctionnalité permet à une application cliente de empaqueter un grand volume d’éléments dans une seule requête, ce qui réduit la surcharge liée à l’exécution d’un grand nombre de petites requêtes.

Mise en cache et cohérence éventuelle

Pour que le modèle Cache-Aside fonctionne, l’instance de l’application qui remplit le cache doit avoir accès à la version la plus récente et cohérente des données. Dans un système qui implémente la cohérence éventuelle (par exemple, un magasin de données répliqué), cette condition peut ne pas être vraie.

Une instance d’une application peut modifier un élément de données et invalider la version mise en cache de cet élément. Une autre instance de l’application peut tenter de lire cet élément depuis un cache, ce qui provoque un défaut de cache. Il lit ensuite les données du magasin de données et les ajoute au cache. Toutefois, si le magasin de données n’est pas entièrement synchronisé avec les autres réplicas, l’instance de l’application peut lire et remplir le cache avec l’ancienne valeur.

Un cache distribué introduit une autre couche à ce problème. Le théorème CAP indique qu’un système distribué peut fournir au maximum deux de trois garanties : cohérence, disponibilité et tolérance de partition. Étant donné que les partitions réseau sont inévitables dans les environnements cloud, vous devez choisir entre cohérence et disponibilité. La plupart des caches distribués, notamment Redis, hiérarchisent la disponibilité et la tolérance de partition par rapport à une cohérence forte. Cette priorité signifie que les lectures depuis une réplique du cache peuvent renvoyer des données obsolètes pendant une partition réseau ou immédiatement après une écriture effectuée sur un autre nœud. Lorsque vous concevez votre stratégie de mise en cache, déterminez la quantité d’obsolescence que votre application peut tolérer et définir les valeurs de durée de vie (TTL) en conséquence. Pour les données qui doivent être actuelles, utilisez des TTL plus courtes ou ignorez entièrement le cache et lisez à partir du magasin de données source.

Pour plus d’informations sur la gestion de la cohérence des données dans les systèmes distribués, consultez Considérations relatives aux données pour les microservices.

Protéger les données mises en cache

Quel que soit le service de cache que vous utilisez, envisagez de protéger les données du cache contre l’accès non autorisé. Deux préoccupations principales sont les suivantes :

  • Confidentialité des données dans le cache.
  • Confidentialité des données à mesure qu’elles circulent entre le cache et l’application qui utilise le cache.

Pour protéger les données dans le cache, le service de cache peut implémenter un mécanisme d’authentification qui nécessite que les applications spécifient les détails suivants :

  • Quelles identités peuvent accéder aux données dans le cache.
  • Quelles opérations de lecture et d’écriture ces identités sont autorisées à effectuer.

Pour réduire la surcharge associée à la lecture et à l’écriture de données, une fois qu’une identité a accordé un accès en écriture ou en lecture au cache, cette identité peut utiliser n’importe quelle donnée dans le cache.

Si vous devez restreindre l’accès aux sous-ensembles des données mises en cache, utilisez l’une des approches suivantes :

  • Fractionnez le cache en partitions à l’aide de différents serveurs de cache. Accordez uniquement aux identités l'accès aux partitions qu'elles sont autorisées à utiliser.

  • Chiffrez les données dans chaque sous-ensemble à l’aide de clés différentes. Fournissez les clés de chiffrement uniquement aux identités qui doivent avoir accès à chaque sous-ensemble. Une application cliente peut toujours être en mesure de récupérer toutes les données dans le cache, mais elle ne peut déchiffrer que les données pour lesquelles elle possède les clés.

Vous devez également protéger les données au fur et à mesure qu’elles circulent dans et hors du cache. Dépend des fonctionnalités de sécurité fournies par l’infrastructure réseau que les applications clientes utilisent pour se connecter au cache. Si le cache est implémenté à l’aide d’un serveur sur site au sein de la même organisation qui héberge les applications clientes, l’isolation du réseau lui-même peut ne pas nécessiter de prendre plus de mesures. Si le cache se trouve à distance et nécessite une connexion TCP ou HTTP sur un réseau public tel qu’Internet, envisagez d’implémenter TLS.

Implémenter la mise en cache à l’aide de Azure Redis managé

Les sections restantes de cet article décrivent comment implémenter les modèles de mise en cache à l’aide de Azure Managed Redis. Azure Managed Redis est un service Redis managé que vous pouvez utiliser comme cache partagé entre les instances d’application. Il prend en charge la mise en cache clé-valeur, les structures de données telles que les ensembles, les ensembles triés et les listes, ainsi que la persistance facultative pour garantir la durabilité lors des redémarrages.

Pour plus d’informations sur les niveaux disponibles, la planification de la capacité, la mise en réseau et les détails des fonctionnalités, consultez la documentation Azure Managed Redis.

Connecter et configurer des applications clientes

Redis prend en charge les applications clientes dans de nombreux langages de programmation. Pour les applications .NET, plusieurs bibliothèques clientes sont disponibles, chacune adaptée à différentes charges de travail Redis. Votre choix de bibliothèque dépend de l’utilisation de Redis strictement en tant que cache ou en tant que plateforme de données multimodèle.

Pour vous connecter à un serveur Redis, utilisez la méthode statique Connect de la ConnectionMultiplexer classe. La connexion créée par cette méthode est créée pour une utilisation tout au long de la durée de vie de l’application cliente. Plusieurs threads simultanés peuvent utiliser la même connexion. Ne vous reconnectez pas et ne vous déconnectez pas à chaque opération Redis, car cela peut dégrader les performances.

Pour obtenir des exemples de connexion spécifiques au langage, consultez Utiliser Azure Redis managé dans .NET Core.

Choisir une bibliothèque de client .NET

Lorsque vous utilisez Azure Redis managé pour la mise en cache, utilisez les bibliothèques de .NET suivantes :

  • StackExchange.Redis : Client Redis de bas niveau qui offre des performances élevées. Utilisez-le lorsque vous avez besoin d’un accès direct aux commandes Redis, aux opérations atomiques, aux transactions, au pipeline ou au script Lua.

  • Microsoft. Extensions.Caching.StackExchangeRedis : Fournit une intégration IDistributedCache pour ASP.NET Core. Utilisez-le pour la mise en cache simple de clé-valeur où les valeurs sont stockées sous forme de tableaux d’octets opaques. Cette abstraction n’expose pas de structures de données Redis avancées.

Ces bibliothèques fournissent les primitives requises pour générer des modèles de mise en cache courants, mais l’application doit implémenter la logique de mise en cache elle-même.

Implémenter des modèles de mise en cache

La façon la plus simple d’utiliser Redis pour la mise en cache consiste à stocker des valeurs sous des clés à l’aide du modèle clé-valeur. Les valeurs peuvent être des chaînes ou des données binaires de longueur arbitraire, ce qui rend Redis adapté à la mise en cache d’objets sérialisés, de données de configuration, d’état de session ou de résultats précomputés.

Concevez votre keyspace avec soin et utilisez des clés significatives (mais pas prolixes). Par exemple, utilisez des clés structurées comme customer:100 (au lieu de simplement 100) pour représenter la clé pour le client qui a l’ID 100. Ce schéma vous permet de distinguer les valeurs qui stockent différents types de données. Par exemple, vous pouvez également utiliser la clé orders:100 pour représenter la clé pour l’ordre dont l’ID est 100.

Bien que les chaînes soient l’approche de mise en cache la plus courante, Redis prend en charge un ensemble complet de types de données natifs, tels que les hachages, les listes, les ensembles triés et les flux, qui permettent des modèles de mise en cache plus flexibles. Pour plus d’informations sur les types de données Redis, consultez la documentation Redis sur les types de données.

Implémenter le modèle Cache-Aside

Comme décrit dans Déterminer comment mettre en cache efficacement les données, une approche courante consiste à charger des données dans le cache à la demande. L'exemple suivant vérifie d'abord le cache, récupère les données à partir de la source de données en cas d'absence dans le cache, puis stocke le résultat pour les requêtes suivantes :

var config = new ConfigurationOptions();
// ... configure endpoint, credentials, TLS, etc.
ConnectionMultiplexer redisHostConnection = ConnectionMultiplexer.Connect(config);
IDatabase cache = redisHostConnection.GetDatabase();

async Task<string> RetrieveItemAsync(string itemKey)
{
    // Attempt to retrieve the item from the Redis cache
    string itemValue = await cache.StringGetAsync(itemKey);

    // If the value returned is null, the item was not found in the cache
    // So retrieve the item from the data source and add it to the cache
    if (itemValue is null)
    {
        itemValue = await GetItemFromDataSourceAsync(itemKey);
        await cache.StringSetAsync(itemKey, itemValue);
    }

    return itemValue;
}

Effectuer des opérations atomiques et par lots

Lorsque plusieurs clients ou instances d’application partagent un cache, vous devez empêcher les mises à jour simultanées de endommager les données. Les stratégies générales d’accès concurrentiel sont décrites dans La gestion de l’accès concurrentiel dans un cache plus haut dans cet article. Redis fournit plusieurs mécanismes qui implémentent ces stratégies.

  • Opérations à clé unique atomiques : Utilisez des commandes pour mettre à jour une valeur en une seule étape, éliminant les conditions de concurrence qui se produisent quand GET et SET sont émises séparément.

    • INCR, INCRBY, DECR, DECRBY incrémentent ou décrémentent de manière atomique une valeur numérique. Dans StackExchange.Redis, utilisez IDatabase.StringIncrementAsync et IDatabase.StringDecrementAsync. Ces commandes sont utiles pour les compteurs, les limiteurs de débit et le suivi des quotas où plusieurs clients mettent à jour simultanément la même clé.

    • GETSET attribue atomiquement une nouvelle valeur à une clé et renvoie la valeur précédente. Dans StackExchange.Redis, utilisez IDatabase.StringGetSetAsync:

      string oldValue = await cache.StringGetSetAsync("data:counter", 0);
      
  • Opérations à plusieurs clés :MGET et MSET lire ou écrire plusieurs valeurs de chaîne dans un seul aller-retour, ce qui réduit la surcharge réseau lorsque vous devez utiliser plusieurs clés à la fois. Les méthodes IDatabase.StringGetAsync et IDatabase.StringSetAsync sont surchargées pour prendre en charge cette fonctionnalité :

    // Create a list of key-value pairs
    var keysAndValues =
        new KeyValuePair<RedisKey, RedisValue>[]
        {
            new("data:key1", "value1"),
            new("data:key99", "value2"),
            new("data:key322", "value3")
        };
    
    // Store the list of key-value pairs in the cache
    await cache.StringSetAsync(keysAndValues);
    ...
    // Find all values that match a list of keys
    RedisKey[] keys = ["data:key1", "data:key99", "data:key322"];
    // Values should contain { "value1", "value2", "value3" }
    RedisValue[] values = await cache.StringGetAsync(keys);
    
  • Transactions (concurrence optimiste) : vous pouvez utiliser la commande WATCH pour surveiller une ou plusieurs clés avant de démarrer une transaction avec MULTI/EXEC . Si des modifications de clé sont détectées avant le démarrage de la transaction, Redis annule la transaction et le client peut réessayer. La bibliothèque StackExchange prend en charge les transactions via l’interface ITransaction .

    Vous créez un ITransaction objet à l’aide de la IDatabase.CreateTransaction méthode. Vous appelez des commandes à la transaction à l’aide des méthodes fournies par l’objet ITransaction .

    L’interface ITransaction fournit l’accès à un ensemble de méthodes similaires aux méthodes accessibles par l’interface IDatabase , sauf que toutes les méthodes sont asynchrones. Cela signifie qu’elles sont effectuées uniquement lorsque la ITransaction.Execute méthode est appelée. La valeur retournée par la ITransaction.Execute méthode indique si la transaction a été créée avec succès (true) ou si elle a échoué (false).

    L’extrait de code suivant montre un exemple qui incrémente et décrémente deux compteurs dans le cadre de la même transaction :

    ITransaction transaction = cache.CreateTransaction();
    
    var tx1 = transaction.StringIncrementAsync("data:counter1");
    var tx2 = transaction.StringDecrementAsync("data:counter2");
    
    bool result = await transaction.ExecuteAsync();
    
    Console.WriteLine($"Transaction {(result ? "succeeded" : "failed")}");
    
    if (result)
    {
        long increment = await tx1;
        long decrement = await tx2;
    
        Console.WriteLine($"Result of increment: {increment}");
        Console.WriteLine($"Result of decrement: {decrement}");
    }
    

    Les transactions Redis sont différentes des transactions dans les bases de données relationnelles. La Execute méthode met en file d’attente toutes les commandes qui composent la transaction à exécuter, et si une commande n’est pas valide, la transaction s’arrête. Si toutes les commandes sont mises en file d’attente, chaque commande s’exécute de façon asynchrone. Si une commande échoue, les autres continuent de traiter. Si vous devez vérifier qu’une commande s’est terminée correctement, récupérez les résultats à l’aide de la Result propriété de la tâche correspondante, comme indiqué dans l’exemple précédent.

  • Scripts Lua. Pour les mises à jour multistep qui doivent être atomiques sur plusieurs clés, vous pouvez exécuter un script Lua sur le serveur. Redis exécute l’intégralité du script en tant qu’opération unique sans entrelacer d’autres commandes.

    Note

    Dans les déploiements en cluster, toutes les clés impliquées dans une transaction ou un script Lua doivent résider dans le même emplacement de hachage. Utilisez des hashtags tels que customer:{123}:name ou customer:{123}:email pour regrouper des clés liées.

Effectuer des opérations de cache en mode fire-and-forget

Lorsqu’une mise à jour du cache n’affecte pas la correction de l’application, par exemple l’incrémentation d’un compteur d’affichage ou l’actualisation d’une statistique non critique, vous pouvez ignorer l’attente de la réponse du serveur. Dans une opération de mise en cache de type fire-and-forget, votre application lance une tâche en arrière-plan et poursuit son exécution sans attendre que la tâche soit terminée. Redis prend en charge les opérations fire-and-forget, qui réduisent la latence des allers-retours côté client grâce aux indicateurs de commande suivants :

await cache.StringSetAsync("data:key1", 99);
...
cache.StringIncrement("data:key1", flags: CommandFlags.FireAndForget);

Spécifier des clés expirant automatiquement

Les stratégies d’expiration décrites dans Gérer l’expiration des données dans un cache sont implémentées dans Redis via des TTL par clé. Lorsque vous stockez un élément dans un cache Redis, vous pouvez spécifier un délai d’expiration après lequel l’élément est automatiquement supprimé. Vous pouvez également interroger la durée d’expiration d’une clé à l’aide de la TTL commande. Cette commande est disponible pour les applications StackExchange via la IDatabase.KeyTimeToLive méthode.

L’extrait de code suivant montre comment définir un délai d’expiration de 20 secondes sur une clé et interroger la durée de vie restante de la clé :

// Add a key with an expiration time of 20 seconds
await cache.StringSetAsync("data:key1", 99, TimeSpan.FromSeconds(20));
...
// Query how much time a key has left to live
// If the key has already expired, the KeyTimeToLive function returns null
TimeSpan? expiry = cache.KeyTimeToLive("data:key1");

Vous pouvez également définir l’expiration sur une date et une heure spécifiques à l’aide de la EXPIREAT commande, qui est disponible dans la bibliothèque StackExchange comme KeyExpireAsync méthode. Il prend un DateTime paramètre :

await cache.StringSetAsync("data:key1", 99);
await cache.KeyExpireAsync("data:key1",
    new DateTime(2026, 9, 1, 0, 0, 0, DateTimeKind.Utc));

Tip

Vous pouvez supprimer manuellement un élément du cache à l’aide de la DEL commande, disponible via la bibliothèque StackExchange comme IDatabase.KeyDeleteAsync méthode.

Lorsque Redis atteint sa limite de mémoire, il supprime les clés en fonction d’une stratégie d’éviction configurée. La stratégie par défaut est volatile-lru, qui évince la clé la moins récemment utilisée ayant un TTL défini. D’autres stratégies incluent allkeys-lru, volatile-randomet noeviction (ce qui provoque l’échec des opérations d’écriture lorsque la mémoire est pleine). Choisissez une stratégie d’éviction en fonction de si votre application utilise des TTL de manière cohérente et si vous souhaitez protéger les clés qui n’ont pas d’expiration. Pour plus d’informations, consultez Gestion de la mémoire.

Corréler les éléments mis en cache

Lorsque vous mettez en cache des éléments associés, vous devez souvent les trouver par relation plutôt que par clé primaire seule. Par exemple, vous pouvez mettre en cache des billets de blog et avoir besoin de répondre à des requêtes telles que « quels billets partagent la balise Y ? » ou « quelles balises appartiennent au billet X ? »

Dans Azure Managed Redis, l’approche recommandée consiste à utiliser RedisJSON et RediSearch. Stockez chaque élément mis en cache sous la forme d’un document JSON avec ses métadonnées, puis créez un index RediSearch sur les champs que vous devez interroger. RediSearch gère les recherches inversées, le filtrage basé sur les balises, les requêtes de plage et la recherche en texte intégral sans exiger que votre application conserve des structures d’index distinctes.

Pour des scénarios plus simples, vous pouvez également utiliser des ensembles Redis pour générer manuellement des index avant et inversés. Stockez un ensemble par publication (contenant ses étiquettes) et un ensemble par étiquette (contenant les ID des publications) :

foreach (BlogPost post in posts)
{
    string postTagsKey = $"blog:posts:{post.Id}:tags";
    await cache.SetAddAsync(
        postTagsKey, post.Tags.Select(s => (RedisValue)s).ToArray());

    foreach (var tag in post.Tags)
    {
        await cache.SetAddAsync($"tag:{tag}:blog:posts", post.Id);
    }
}

Vous pouvez ensuite interroger des balises pour un billet à l’aide SetMembersAsyncde , rechercher des balises courantes entre les publications à l’aide SetCombineAsync(SetOperation.Intersect, ...)de , ou rechercher toutes les publications pour une balise donnée. Le compromis est que votre application doit maintenir à la fois les ensembles avant et inverse, ce qui ajoute de la complexité au fur et à mesure que le nombre de relations augmente.

Rechercher des éléments récemment consultés

De nombreuses applications doivent suivre les éléments les plus récemment consultés ou affichés. Par exemple, un site de blogs peut afficher les billets de lecture les plus récents à un visiteur de retour. Les listes Redis offrent un moyen efficace d’implémenter des modèles de mise en cache basés sur la recency. Les éléments peuvent être ajoutés à l’une ou l’autre extrémité de la liste à l’aide de LPUSH ou RPUSH, et supprimés à l’aide de LPOP ou RPOP. Permet LTRIM de limiter la longueur de la liste et d’empêcher la croissance de la mémoire illimitée.

Implémenter un classement

Redis Ensembles Triés (ZSETs) maintiennent des classements ordonnés en associant chaque élément à une note numérique. Redis gère automatiquement l’ordre. ZADD est O(log N), et les requêtes de plage telles que ZRANGE et ZREVRANGE sont O(log N + M), où M est le nombre d’éléments retournés, de sorte que les jeux triés restent efficaces même avec des nombres d’éléments volumineux.

Ajouter des éléments à un classement

L’exemple suivant montre comment ajouter un billet de blog et son score à un classement à l’aide de la ZADD commande via SortedSetAddAsync:

var db = connection.GetDatabase();
string redisKey = "blog:post_rankings";

BlogPost blogPost = ...; // The blog post being ranked

await db.SortedSetAddAsync(redisKey, blogPost.Title, blogPost.Score);
Récupérer les éléments classés

Vous pouvez récupérer des éléments par ordre croissant de score à l’aide de SortedSetRangeByRankWithScoresAsync :

var entries = await db.SortedSetRangeByRankWithScoresAsync(redisKey);

foreach (var entry in entries)
{
    Console.WriteLine($"{entry.Element}: {entry.Score}");
}

Note

SortedSetRangeByRankAsync retourne uniquement les valeurs de membre, et non les scores.

Récupérer les N premiers éléments

Pour obtenir les éléments avec les notes les plus élevées, tels que les 10 meilleurs articles, utilisez l’ordre décroissant :

foreach (var post in await cache.SortedSetRangeByRankWithScoresAsync(
                               redisKey, 0, 9, Order.Descending))
{
    Console.WriteLine(post);
}
Récupérer des éléments par plage de scores

Vous pouvez également interroger des éléments en fonction des limites de score plutôt que du classement :

foreach (var post in await cache.SortedSetRangeByScoreWithScoresAsync(
                               redisKey, 5000, 100000))
{
    Console.WriteLine(post);
}

Pour empêcher un classement de croître indéfiniment, supprimez les anciennes entrées à l’aide SortedSetRemoveRangeByRankAsync ou utilisez des clés limitées au temps (par exemple, des classements quotidiens ou hebdomadaires). Vous pouvez mettre à jour les scores de manière atomique à l’aide de SortedSetIncrementAsync (ZINCRBY).

Mettre en cache l’état de session et la sortie HTML

Vous pouvez utiliser Azure Redis managé pour stocker l’état de session et les données de cache de sortie pour les applications ASP.NET Core et ASP.NET. Lorsque vous conservez les données de session et affichez la sortie dans un cache Redis partagé, les applications s’exécutant sur plusieurs instances, telles que dans Azure App Service, Azure Kubernetes Service (AKS), Azure Container Apps, ou groupes de machines virtuelles identiques, peuvent gérer des expériences utilisateur cohérentes sans nécessiter d’affinité serveur.

Tip

Pour des performances optimales, déployez votre application et votre instance Redis managée Azure dans la même région Azure.

ASP.NET Core

ASP.NET Core applications utilisent l’abstraction IDistributedCache et le middleware de session. Azure Managed Redis s'intègre à IDistributedCache par le biais du paquet Microsoft.Extensions.Caching.StackExchangeRedis.

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "<your-cache-name>.<region>.redis.azure.net:10000";
    options.InstanceName = "app-cache:";
});

builder.Services.AddSession();

Le middleware de mise en cache de sortie d’ASP.NET Core peut également utiliser Redis comme magasin distribué, ce qui permet aux applications de partager des fragments ou pages générés entre toutes les instances. Pour plus d’informations, consultez ASP.NET fournisseur de cache de sortie core pour Redis.

Intégration de .NET Aspire

Les applications .NET Aspire peuvent utiliser le Aspire.Hosting.Azure.Redis package pour déclarer une ressource Redis managée Azure dans l’hôte de l’application. Les projets consommateurs reçoivent automatiquement la configuration de connexion grâce à l'injection de dépendances, ce qui supprime la gestion manuelle des chaînes de connexion à travers les services.

// App host: declare the Azure Managed Redis resource
var cache = builder.AddAzureManagedRedis("cache");

builder.AddProject<Projects.ProductService>()
    .WithReference(cache);

Les services consommant inscrivent le cache distribué de la même façon que n’importe quel autre IDistributedCache fournisseur. Pour plus d’informations, consultez Prise en main de l’intégration de Redis.

Haute disponibilité, scalabilité et partitionnement

Chaque instance Redis managée Azure utilise la réplication principale/réplica. Le service surveille l'intégrité des nœuds et promeut automatiquement un réplica si le nœud principal échoue. Étant donné que la réplication est asynchrone, une petite quantité de données récemment écrites peut être perdue pendant un basculement inattendu. Pour connaître les stratégies générales derrière la réplication, le basculement et la mise en cache en couches, consultez Implémenter la haute disponibilité et la scalabilité, et améliorer les performances plus haut dans cet article.

Vous pouvez combiner un cache en mémoire local avec Azure Managed Redis pour réduire la latence et fournir un secours si le cache partagé est temporairement inaccessible. Le modèle Circuit-Breaker et le modèle Cache-Aside aident à gérer cette approche en couches.

Pour les charges de travail qui dépassent la capacité d'un nœud unique, Azure Managed Redis prend en charge le partitionnement (sharding) des données sur plusieurs nœuds Redis. Avec les deux stratégies de clustering, les données sont automatiquement partitionnées entre les nœuds à l'aide d'un hachage clé-vers-fragment, avec basculement et resynchronisation automatiques, ainsi qu'un repartitionnement en ligne (montée en charge et réduction de capacité). Azure Managed Redis prend en charge deux stratégies de clustering :

  • Stratégie de clustering OSS (par défaut) : Les clients communiquent directement avec la partition appropriée et suivent la sémantique du cluster Redis OSS, y compris les redirections MOVED et ASK. Les clients prenant en charge le cluster, tels que StackExchange.Redis, gèrent ces redirections automatiquement. Cette stratégie fournit la surcharge de routage la plus faible.

  • Stratégie de clustering Redis Enterprise : Un proxy fournit un routage transparent via un point de terminaison unique. Les clients n’ont pas besoin d’implémenter une logique prenant en charge le cluster ou de gérer les réponses MOVED/ASK. Cette stratégie offre une intégration de client plus simple, mais introduit une petite quantité de surcharge de routage.

Azure Managed Redis prend également en charge le mode nonclustered, qui utilise une seule paire principale/réplica sans partitionnement. Ce mode convient aux charges de travail plus petites qui ne nécessitent pas de scale-out horizontal.

Note

Les modèles de partitionnement personnalisés, tels que le hachage côté client ou les proxys non Microsoft, sont généralement nécessaires uniquement dans les déploiements Redis auto-gérés sur des machines virtuelles ou Kubernetes. Le clustering Azure Managed Redis gère automatiquement le routage, le basculement et le repartitionnement.

Géoréplication active

Pour la disponibilité multirégion, Azure Managed Redis prend en charge la géoréplication active, qui lie des instances entre Azure régions en un seul groupe de réplication. Chaque instance peut gérer les lectures et les écritures, et les modifications sont synchronisées automatiquement. Votre application est chargée de rediriger le trafic vers une instance saine lors d’une défaillance régionale. Pour plus d’informations, consultez la page Configurer une géoréplication active.

Persistance des données

Par défaut, les données mises en cache dans Azure Managed Redis sont conservées en mémoire et peuvent être perdues si un nœud redémarre ou bascule. Pour les charges de travail où la reconstruction du cache à partir du magasin de données source serait lente ou coûteuse, Azure Managed Redis prend en charge la persistance facultative des données :

  • Les captures instantanées RDB (base de données Redis) créent des instantanés périodiques à un point dans le temps enregistrés sur un disque managé. RDB a un effet de performances minimal pendant les opérations normales, mais les données écrites depuis la dernière capture instantanée peuvent être perdues.

  • AOF (Fichier en écriture seule) journalise chaque opération d’écriture sur le disque. AOF réduit la perte de données potentielle à environ une seconde d’écritures, mais produit des fichiers plus volumineux et peut réduire le débit d’écriture.

Vous pouvez utiliser RDB et AOF ensemble. Redis charge l'instantané RDB au démarrage, puis rejoue le journal AOF pour assurer une récupération presque complète.

Important

La persistance améliore la durabilité par rapport aux défaillances de nœud, mais ce n’est pas un mécanisme de sauvegarde ou de récupération d’urgence. Pour les données critiques, conservez toujours la copie faisant autorité dans votre magasin de données source et utilisez le modèleCache-Aside pour remplir à nouveau le cache.

Pour plus d’informations sur la configuration, consultez Configurer la persistance des données.

Protéger les données mises en cache dans Azure Managed Redis

Les conseils de Protection des données mises en cache décrivent les problèmes de contrôle d’accès et de transit. Azure Managed Redis vous aide à résoudre ces problèmes de la manière suivante :

  • Utilisez l’authentification Microsoft Entra ID comme mécanisme de contrôle d’accès principal et suivez le principe de privilège minimum lors de l’octroi de l’accès.

  • Utilisez des points de terminaison privés pour restreindre l’accès réseau afin que le trafic ne traverse pas l’Internet public.

  • Azure Managed Redis chiffre les données en transit à l’aide de TLS et chiffre les données au repos.

Considérations relatives à la sérialisation

Lorsque vous stockez des objets .NET dans Redis en tant que valeurs de chaîne, vous devez les sérialiser. Lorsque vous choisissez un format de sérialisation, tenez compte des compromis entre les performances, l’interopérabilité, la gestion des versions et la taille de la charge utile. Il n’existe aucun sérialiseur le plus rapide unique pour tous les scénarios. Les benchmarks dépendent fortement du contexte et peuvent ne pas refléter votre charge de travail réelle.

Si votre niveau Redis managé Azure prend en charge RedisJSON, vous pouvez stocker des objets en tant que documents JSON natifs et interroger des champs individuels sans désérialiser toute la valeur :

public static class RedisJsonExtensions
{
    public static async Task<T?> GetAsync<T>(
        this IDatabase cache,
        string key,
        string path = "$")
    {
        var result = await cache.ExecuteAsync("JSON.GET", key, path);

        if (result.IsNull)
            return default;

        return JsonSerializer.Deserialize<T>(result!);
    }

    public static async Task SetAsync<T>(
        this IDatabase cache,
        string key,
        T value,
        TimeSpan? expiry = null,
        string path = "$")
    {
        var json = JsonSerializer.Serialize(value);

        // Store JSON document
        await cache.ExecuteAsync("JSON.SET", key, path, json);

        // Apply TTL if provided
        if (expiry.HasValue)
        {
            await cache.KeyExpireAsync(key, expiry);
        }
    }

    public static async Task<bool> ExpireAsync(
        this IDatabase cache,
        string key,
        TimeSpan expiry)
    {
        return await cache.KeyExpireAsync(key, expiry);
    }
}

Lorsque vous sérialisez des valeurs en tant que chaînes Redis à la place, les options de format courantes sont les suivantes :

  • JSON - Format lisible par l’homme qui offre une prise en charge multiplateforme étendue. Pas le format le plus compact, mais un bon choix lorsque les éléments mis en cache sont retournés directement aux clients HTTP, car il évite une étape de désérialisation et de resérialisation supplémentaires.

  • MessagePack : format binaire compact qui n’a aucune exigence de schéma. Produit des charges utiles plus petites que JSON avec une surcharge de sérialisation inférieure.

  • Mémoires tampons de protocole (protobuf) : format binaire basé sur un schéma qui produit des charges utiles compactes. Nécessite des .proto fichiers de définition et une étape de compilation pour générer du code spécifique au langage.

  • BSON : format binaire qui étend JSON avec d’autres types, tels que des dates et des données binaires brutes. Les charges utiles sont comparables en taille au format JSON. Un choix pratique lorsque votre application utilise déjà BSON ailleurs, par exemple avec MongoDB.

Les modèles suivants peuvent être pertinents lorsque vous implémentez la mise en cache dans vos applications :

  • modèleCache-Aside : ce modèle décrit comment charger des données à la demande dans un cache à partir d’un magasin de données. Il permet également de maintenir la cohérence entre les données dans le cache et les données dans le magasin de données d’origine.

  • Modèle de partitionnement : ce modèle fournit des informations sur l’implémentation du partitionnement horizontal pour améliorer la scalabilité lors du stockage et de l’accès à de grands volumes de données.