Maîtriser la gestion de la mémoire de Redis pour des performances optimales

Atteignez des performances Redis optimales en maîtrisant les techniques de gestion de la mémoire. Ce guide complet aborde des aspects cruciaux tels que la compréhension de l'empreinte mémoire de Redis, la surveillance avec `INFO memory` et `MEMORY USAGE`, et l'optimisation des structures de données. Apprenez à combattre la fragmentation avec la défragmentation active, à configurer des politiques d'éviction efficaces (`maxmemory`, `allkeys-lru`), et à tirer parti du lazy freeing pour des opérations plus fluides. Mettez en œuvre ces stratégies concrètes pour améliorer le débit de Redis, réduire la latence et assurer un caching et un stockage de données stables et performants.

36 vues

Maîtriser la gestion de la mémoire Redis pour des performances optimales

Redis est réputé pour sa rapidité fulgurante, largement due à son fonctionnement en mémoire. Cependant, pour véritablement libérer et maintenir des performances maximales, maîtriser la gestion de la mémoire Redis n'est pas seulement bénéfique, c'est essentiel. Une gestion inappropriée de la mémoire peut entraîner une augmentation de la latence, une réduction du débit, voire des plantages de serveur et une perte de données. Cet article explore les aspects critiques de la gestion de la mémoire Redis, couvrant les stratégies d'allocation, la compréhension de la fragmentation, l'optimisation des structures de données et la configuration des politiques d'éviction, le tout dans le but de vous aider à atteindre la stabilité et l'efficacité les plus élevées possibles.

Une gestion efficace de la mémoire dans Redis va au-delà du simple fait de disposer de suffisamment de RAM. Elle implique une compréhension approfondie de la manière dont Redis stocke les données, comment il consomme les ressources système et comment divers paramètres de configuration ont un impact sur son empreinte mémoire. En optimisant l'utilisation de la mémoire de votre instance Redis, vous pouvez améliorer considérablement sa réactivité, prolonger sa durée de vie opérationnelle et vous assurer qu'elle continue de servir vos applications de manière fiable sous des charges variables. Nous examinerons des techniques pratiques et des meilleures pratiques pour vous aider à affiner vos déploiements Redis.

Comprendre l'utilisation de la mémoire Redis

Redis utilise la mémoire système pour stocker toutes ses données. Lorsque vous utilisez SET pour définir une paire clé-valeur, Redis alloue de la mémoire à la fois pour la chaîne de la clé et pour la valeur, ainsi qu'un certain surcoût pour les structures de données internes. Comprendre les différentes composantes de l'utilisation de la mémoire est la première étape vers une gestion efficace :

  • Mémoire des données : C'est la mémoire consommée par vos données réelles (clés, valeurs et structures de données internes comme les dictionnaires pour mapper les clés aux valeurs). La taille dépend du nombre et de la taille de vos clés et valeurs, ainsi que des structures de données que vous choisissez (chaînes, hachages, listes, ensembles, ensembles ordonnés).
  • Mémoire de surcharge (Overhead) : Redis ajoute une certaine surcharge pour chaque clé (par exemple, des pointeurs, des métadonnées pour le suivi LRU/LFU, des informations d'expiration). Les petites structures de données peuvent être encodées de manière spéciale (par exemple, ziplist, intset) pour réduire cette surcharge, mais les plus grandes utiliseront des représentations plus génériques (et plus gourmandes en mémoire).
  • Mémoire tampon (Buffer) : Redis utilise des tampons de sortie client, des tampons de rattrapage de réplication et des tampons AOF. Des clients volumineux ou lents, ou une configuration de réplication chargée, peuvent consommer une quantité importante de mémoire tampon.
  • Mémoire Fork : Lorsque Redis effectue des opérations en arrière-plan comme la sauvegarde d'instantanés RDB ou la réécriture de fichiers AOF, il crée un processus enfant par fork. Ce processus enfant partage initialement la mémoire avec le parent via copie sur écriture (CoW). Cependant, toute écriture sur l'ensemble de données par le processus parent après le fork entraînera la duplication des pages, augmentant l'empreinte mémoire totale.

Surveiller la mémoire Redis

Surveiller régulièrement la mémoire Redis est crucial pour identifier les problèmes potentiels avant qu'ils ne s'aggravent. L'outil principal pour cela est la commande INFO memory, ainsi que MEMORY USAGE.

La commande INFO memory

redis-cli INFO memory

Métriques clés de INFO memory :

  • used_memory : Le nombre total d'octets alloués par Redis à l'aide de son allocateur (jemalloc, glibc, etc.). C'est la somme de la mémoire utilisée par vos données, les structures de données internes et les tampons temporaires.
  • used_memory_human : used_memory au format lisible par l'humain.
  • used_memory_rss : Resident Set Size (RSS), la quantité de mémoire consommée par le processus Redis telle que rapportée par le système d'exploitation. Cela inclut les allocations propres à Redis, plus la mémoire utilisée par la gestion de la mémoire du système d'exploitation, les bibliothèques partagées et la mémoire potentiellement fragmentée non encore restituée au SE.
  • mem_fragmentation_ratio : Ceci est used_memory_rss / used_memory. Un ratio idéal est légèrement supérieur à 1,0 (par exemple, 1,03-1,05). Un ratio significativement supérieur à 1,0 (par exemple, 1,5+) indique une fragmentation mémoire élevée. Un ratio inférieur à 1,0 suggère un échange de mémoire (swapping), ce qui constitue un problème de performance critique.
  • allocator_frag_bytes : Octets de fragmentation rapportés par l'allocateur de mémoire.
  • lazyfree_pending_objects : Nombre d'objets en attente d'être libérés de manière asynchrone.

La commande MEMORY USAGE

Pour inspecter l'utilisation de la mémoire des clés individuelles :

redis-cli MEMORY USAGE mykey
redis-cli MEMORY USAGE myhashkey SAMPLES 0 # Estimation pour les agrégats

Cette commande fournit une utilisation mémoire estimée pour une clé donnée, vous aidant à identifier les points de données volumineux ou stockés de manière inefficace.

Stratégies clés d'optimisation de la mémoire

L'optimisation de la mémoire dans Redis implique plusieurs étapes proactives, du choix des bons types de données à la gestion de la fragmentation.

1. Optimisation des structures de données

Redis propose diverses structures de données, chacune ayant ses propres caractéristiques de mémoire. Choisir la bonne et la configurer de manière appropriée peut réduire considérablement la consommation de mémoire.

  • Chaînes (Strings) : Les plus simples, mais attention aux grandes chaînes. Utiliser SET ou GET sur de très grandes chaînes (Mo) peut affecter les performances en raison de la surcharge de transfert réseau et mémoire.
  • Hachages, Listes, Ensembles, Ensembles ordonnés (Agrégats) : Redis tente d'économiser de la mémoire en encodant les petites structures de données agrégées de manière compacte (par exemple, ziplist pour les hachages/listes, intset pour les ensembles d'entiers). Ces encodages compacts sont très efficaces en mémoire, mais deviennent moins efficaces pour les structures plus grandes, passant à des tables de hachage ou des listes d'ordonnancement (skip lists) régulières.
    • Astuce : Gardez les membres individuels des agrégats petits. Pour les hachages, préférez de nombreux petits champs à quelques grands champs.
    • Configuration : Les directives hash-max-ziplist-entries, hash-max-ziplist-value, list-max-ziplist-entries, list-max-ziplist-value, set-max-intset-entries et zset-max-ziplist-entries/zset-max-ziplist-value dans redis.conf contrôlent quand Redis passe de l'encodage compact aux structures de données régulières. Ajustez-les soigneusement ; des valeurs trop grandes peuvent dégrader les performances pour certains schémas d'accès, tandis que des valeurs trop petites peuvent augmenter la mémoire.

2. Meilleures pratiques de conception des clés

Bien que les valeurs consomment généralement plus de mémoire, l'optimisation des noms de clés est également importante :

  • Clés courtes et descriptives : Des clés plus courtes économisent de la mémoire, surtout lorsque vous en avez des millions. Cependant, ne sacrifiez pas la clarté pour une brièveté extrême. Visez des noms de clés descriptifs mais concis.
    • Mauvais : user:1000:profile:details:email
    • Bon : user:1000:email (si vous ne stockez que l'e-mail)
  • Préfixage : Utilisez des préfixes cohérents (par exemple, user:, product:) à des fins d'organisation. Cela a un impact minimal sur la mémoire mais facilite la gestion.

3. Minimiser la surcharge (Overhead)

Chaque clé et chaque valeur ont une certaine surcharge interne. Réduire le nombre de clés, en particulier les petites, peut être efficace.

  • Utiliser HASH au lieu de plusieurs chaînes : Si vous avez de nombreux champs liés à une entité, stockez-les dans un seul HASH plutôt que dans plusieurs clés STRING. Cela réduit le nombre de clés de haut niveau et leur surcharge associée.
    • Exemple : Au lieu de user:1:name, user:1:email, user:1:age, utilisez une clé HASH user:1 avec les champs name, email, age.

4. Gestion de la fragmentation de la mémoire

La fragmentation de la mémoire se produit lorsque l'allocateur de mémoire est incapable de trouver des blocs contigus de la taille exacte requise, ce qui entraîne des espaces inutilisés. Cela peut faire que used_memory_rss soit significativement supérieur à used_memory.

  • Causes : Insertions et suppressions fréquentes de clés de tailles variables, en particulier après que l'allocateur de mémoire fonctionne depuis longtemps.
  • Détection : Un mem_fragmentation_ratio significativement supérieur à 1,0 (par exemple, 1,5-2,0) indique une fragmentation élevée.
  • Solutions :
    • Défragmentation active Redis 4.0+ : Redis peut défragmenter activement la mémoire sans redémarrage. Activez-la avec activedefrag yes dans redis.conf et configurez active-defrag-max-scan-time et active-defrag-cycle-min/max. Cela permet à Redis de déplacer les données, compactant ainsi la mémoire.
    • Redémarrage de Redis : La manière la plus simple, bien que disruptive, de défragmenter la mémoire est de redémarrer le serveur Redis. Cela libère toute la mémoire vers le système d'exploitation, et l'allocateur redémarre à zéro. Pour les instances persistantes, assurez-vous qu'un instantané RDB ou un fichier AOF est sauvegardé avant de redémarrer.
# Paramètres redis.conf pour la défragmentation active
activedefrag yes
active-defrag-ignore-bytes 100mb  # Ne pas défragmenter si la fragmentation est inférieure à 100 Mo
active-defrag-threshold-lower 10  # Démarrer la défrag si le ratio de fragmentation est > 10%
active-defrag-threshold-upper 100 # Arrêter la défrag si le ratio de fragmentation est > 100%
active-defrag-cycle-min 1         # Effort CPU minimum pour la défrag (1-100%)
active-defrag-cycle-max 20        # Effort CPU maximum pour la défrag (1-100%)

Politiques d'éviction : Gérer maxmemory

Lorsque Redis est utilisé comme cache, il est crucial de définir ce qui se passe lorsque la mémoire atteint une limite prédéfinie. La directive maxmemory dans redis.conf définit cette limite, et maxmemory-policy dicte la stratégie d'éviction.

maxmemory 2gb # Définir la mémoire maximale à 2 gigaoctets
maxmemory-policy allkeys-lru # Évincer les clés les moins récemment utilisées parmi toutes les clés

Options courantes pour maxmemory-policy :

  • noeviction : (Par défaut) Les nouvelles écritures sont bloquées lorsque maxmemory est atteint. Les lectures fonctionnent toujours. C'est bon pour le débogage mais généralement pas pour les caches de production.
  • allkeys-lru : Évince les clés les moins récemment utilisées (LRU) de tous les espaces de clés (clés avec ou sans expiration).
  • volatile-lru : Évince les clés LRU uniquement parmi les clés qui ont une expiration définie.
  • allkeys-lfu : Évince les clés les moins fréquemment utilisées (LFU) de tous les espaces de clés.
  • volatile-lfu : Évince les clés LFU uniquement parmi les clés qui ont une expiration définie.
  • allkeys-random : Évince aléatoirement des clés de tous les espaces de clés.
  • volatile-random : Évince aléatoirement des clés uniquement parmi celles qui ont une expiration définie.
  • volatile-ttl : Évince les clés avec le plus court Temps de Vie (TTL) uniquement parmi celles qui ont une expiration définie.

Choisir la bonne politique :

  • Pour la mise en cache générale, allkeys-lru ou allkeys-lfu sont souvent de bons choix, selon que la récence ou la fréquence est un meilleur indicateur d'utilité pour vos données.
  • Si vous utilisez principalement Redis pour la gestion de sessions ou des objets avec des expirations explicites, volatile-lru ou volatile-ttl pourraient être plus appropriés.

Avertissement : Si maxmemory-policy est défini sur noeviction et que maxmemory est atteint, les opérations d'écriture échoueront, entraînant des erreurs d'application.

Persistance et surcharge mémoire

Les mécanismes de persistance de Redis (RDB et AOF) interagissent également avec la mémoire :

  • Instantanés RDB : Lorsque Redis sauvegarde un fichier RDB, il crée un processus enfant par fork. Pendant le processus d'instantané, toute écriture sur l'ensemble de données Redis par le parent provoquera la duplication des pages mémoire en raison de la copie sur écriture (CoW). Cela peut temporairement doubler l'empreinte mémoire, en particulier sur les instances chargées avec des sauvegardes RDB fréquentes.
  • Réécriture AOF : De même, lorsque le fichier AOF est réécrit (par exemple, BGREWRITEAOF), un fork se produit, entraînant une duplication mémoire temporaire. Le tampon AOF lui-même consomme également de la mémoire.

Astuce : Planifiez les sauvegardes RDB et les réécritures AOF pendant les heures creuses si possible, ou assurez-vous que votre serveur dispose de suffisamment de RAM libre pour gérer la surcharge CoW.

Libération paresseuse (Lazy Freeing)

Redis 4.0 a introduit la libération paresseuse (suppression non bloquante) pour éviter de bloquer le serveur lors de la suppression de clés volumineuses ou de la purge de bases de données. Au lieu de récupérer la mémoire de manière synchrone, Redis peut placer la tâche de libération de mémoire dans un thread d'arrière-plan.

  • lazyfree-lazy-eviction yes : Libère la mémoire de manière asynchrone lors de l'éviction.
  • lazyfree-lazy-expire yes : Libère la mémoire de manière asynchrone lorsque les clés expirent.
  • lazyfree-lazy-server-del yes : Libère la mémoire de manière asynchrone lorsque DEL, RENAME, FLUSHALL, FLUSHDB sont appelés sur des clés/bases de données volumineuses.

Recommandation : Activez la libération paresseuse pour les instances chargées afin de réduire les pics de latence potentiels causés par la récupération synchrone de la mémoire.

Pipelinage et Mémoire

Le pipelinage (Pipelining), bien qu'étant principalement une technique d'optimisation réseau, peut indirectement influencer les performances mémoire en rendant le traitement des commandes plus efficace. En envoyant plusieurs commandes à Redis en un seul aller-retour, il réduit la latence réseau et la surcharge CPU par commande côté client et serveur. Cela permet à Redis de traiter plus d'opérations par seconde sans accumuler de grandes files d'attente de commandes, ce qui pourrait autrement entraîner une utilisation mémoire plus élevée dans les tampons client ou un traitement plus lent qui sollicite l'allocateur de mémoire au fil du temps.

Bien que le pipelinage ne gère pas directement l'allocation de mémoire, ses améliorations d'efficacité garantissent que Redis peut gérer un débit plus élevé avec moins de ressources gaspillées en surcharge de commande, permettant à l'allocateur de mémoire de fonctionner plus facilement sous la charge.

Conclusion

Maîtriser la gestion de la mémoire Redis est un processus continu qui a un impact significatif sur les performances et la stabilité de vos applications. En comprenant comment Redis utilise la mémoire, en surveillant diligemment son empreinte, en optimisant vos structures de données, en gérant efficacement la fragmentation et en configurant judicieusement les politiques d'éviction, vous pouvez garantir que vos instances Redis fonctionnent à une efficacité maximale.

Commencez toujours par une surveillance claire, puis appliquez une combinaison des meilleures pratiques de modélisation des données, des paramètres de configuration appropriés et une réflexion approfondie sur les stratégies de persistance et d'éviction. Examinez régulièrement vos modèles d'utilisation de la mémoire à mesure que votre application et vos données évoluent afin de maintenir un environnement Redis robuste et performant.