Dépannage des commandes Redis lentes : une liste de contrôle des performances

Une liste de contrôle pratique pour trouver les commandes Redis lentes avec SLOWLOG, MONITOR, les outils de latence, la complexité des commandes et des correctifs plus sûrs.

Dépannage des commandes Redis lentes : une liste de contrôle des performances

Les commandes Redis lentes commencent généralement comme des commandes normales qui dépassent leurs hypothèses. Un appel SMEMBERS était inoffensif lorsque l'ensemble comptait 200 membres. Une requête de tableau de bord était correcte lorsqu'elle chargeait 50 clés. Un script Lua était rapide jusqu'à ce qu'un client crée une forme de données beaucoup plus grande que tous les autres.

La question utile n'est pas seulement "quelle commande est lente ?" mais "quelle forme de données a rendu cette commande lente, et pourquoi l'application demande-t-elle à Redis de faire autant de travail en une seule fois ?"

Comprendre les performances de Redis

Les performances de Redis sont généralement exceptionnelles en raison de sa nature en mémoire. Cependant, plusieurs facteurs peuvent contribuer à la latence des commandes :

  • Complexité des commandes : Certaines commandes sont intrinsèquement plus gourmandes en ressources que d'autres (par exemple, KEYS sur un grand ensemble de données vs GET).
  • Taille et structure des données : Les grandes listes, ensembles ou ensembles triés, ou les structures de données complexes, peuvent avoir un impact sur les performances des commandes qui les manipulent.
  • Latence réseau : Bien qu'il ne s'agisse pas directement d'un problème de commande, une latence réseau élevée entre le client et le serveur peut donner l'impression que les commandes sont lentes.
  • Charge du serveur : Une utilisation élevée du processeur, une mémoire insuffisante ou d'autres processus sur le serveur Redis peuvent dégrader les performances.
  • Commandes bloquantes : Certaines opérations peuvent bloquer la boucle d'événements Redis, affectant toutes les commandes suivantes.

Identification des commandes lentes avec SLOWLOG

La commande SLOWLOG est le mécanisme intégré de Redis pour enregistrer les commandes qui dépassent un temps d'exécution spécifié. C'est votre outil principal pour identifier de manière proactive les commandes problématiques.

Comment fonctionne SLOWLOG

Redis maintient un tampon circulaire qui stocke des informations sur les commandes qui ont pris plus de temps que le seuil configuré slowlog-log-slower-than (en microsecondes). Le seuil par défaut est généralement de 10 millisecondes (10000 microsecondes). Lorsque ce tampon est plein, les entrées les plus anciennes sont supprimées.

Sous-commandes clés de SLOWLOG

  • SLOWLOG GET [count] : Récupère les dernières count entrées du journal lent. Si count est omis, il récupère toutes les entrées.
  • SLOWLOG LEN : Renvoie la longueur actuelle du journal lent (nombre d'entrées).
  • SLOWLOG RESET : Efface les entrées du journal lent. Utilisez cette commande avec prudence, car elle supprime définitivement les données enregistrées.

Exemple d'utilisation de SLOWLOG

Supposons que vous soupçonniez que certaines commandes prennent trop de temps. Vous pouvez vérifier le journal lent comme suit :

# Connectez-vous à votre instance Redis
redis-cli

# Obtenez les 5 dernières commandes lentes
127.0.0.1:6379> SLOWLOG GET 5

La sortie ressemblera à ceci :

1) 1) (integer) 18
   2) (integer) 1678886400
   3) (integer) 15000
   4) 1) "KEYS"
      2) "*"

2) 1) (integer) 17
   2) (integer) 1678886390
   3) (integer) 12000
   4) 1) "SMEMBERS"
      2) "my_large_set"

...

Explication de la sortie :

  1. ID d'entrée : Un identifiant unique pour l'entrée du journal lent.
  2. Horodatage : L'horodatage Unix au moment où la commande a été exécutée.
  3. Temps d'exécution : La durée (en microsecondes) que la commande a prise pour s'exécuter.
  4. Commande et arguments : La commande elle-même et ses arguments.

Dans l'exemple ci-dessus, KEYS * a pris 15000 microsecondes (15 ms) et SMEMBERS my_large_set a pris 12000 microsecondes (12 ms). Ceux-ci seraient considérés comme lents si votre slowlog-log-slower-than est défini sur 10000 microsecondes.

Configuration de slowlog-log-slower-than

Vous pouvez modifier dynamiquement le seuil slowlog-log-slower-than à l'aide de la commande CONFIG SET :

127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 50000  # Enregistrer les commandes plus lentes que 50 ms

Pour rendre cette modification persistante après les redémarrages de Redis, vous devez modifier le fichier redis.conf et redémarrer le serveur Redis, ou utiliser CONFIG REWRITE pour enregistrer les modifications dans le fichier de configuration.

Surveillance des commandes en temps réel avec MONITOR

Alors que SLOWLOG fournit une vue historique, MONITOR offre un flux en temps réel de toutes les commandes exécutées par le serveur Redis. C'est inestimable pour le débogage pendant une période spécifique de performances lentes ou pour comprendre les modèles de trafic de commandes.

Comment fonctionne MONITOR

Lorsque vous activez MONITOR, Redis envoie une réponse au client MONITOR pour chaque commande qu'il reçoit et traite. Cela peut générer un volume très élevé de sortie, en particulier sur les instances Redis occupées. Par conséquent, il est généralement recommandé d'utiliser MONITOR avec parcimonie et uniquement lors du débogage actif.

Exemple d'utilisation de MONITOR

À partir d'une session redis-cli distincte, exécutez la commande MONITOR :

# Connectez-vous à votre instance Redis dans un *autre* terminal
redis-cli

# Commencez la surveillance
127.0.0.1:6379> MONITOR

Maintenant, toute commande exécutée dans une autre session redis-cli ou par votre application apparaîtra dans la sortie MONITOR. Par exemple, si vous exécutez SET mykey myvalue dans un autre client, vous verrez :

1678887000.123456 [0 127.0.0.1:54321] "SET" "mykey" "myvalue"

Utilisation de MONITOR pour le débogage

  1. Reproduire le problème : Lorsque vous remarquez un ralentissement, démarrez immédiatement MONITOR dans une session redis-cli dédiée.
  2. Déclencher l'opération lente : Demandez à votre application d'effectuer l'action que vous soupçonnez être à l'origine du ralentissement.
  3. Analyser la sortie : Observez les commandes dans le flux MONITOR. Recherchez :
    • Les commandes qui mettent du temps à apparaître (bien que MONITOR lui-même n'affiche pas le temps d'exécution, vous pouvez le déduire en chronométrant les commandes manuellement ou en observant les retards).
    • Des commandes inhabituelles ou inattendues en cours d'exécution.
    • Un volume élevé de commandes qui pourrait surcharger le serveur.
  4. Arrêter la surveillance : Appuyez sur Ctrl+C pour quitter la commande MONITOR.

Important : N'exécutez pas MONITOR dans un environnement de production pendant des périodes prolongées, car cela peut avoir un impact significatif sur les performances de Redis en raison de la surcharge liée à l'envoi de chaque commande au client.

Causes courantes des commandes lentes et comment les corriger

Sur la base des informations recueillies à partir de SLOWLOG et MONITOR, voici les coupables courants et leurs solutions :

1. Commande KEYS

  • Problème : La commande KEYS parcourt l'ensemble de l'espace de clés pour trouver les clés correspondant à un modèle. Sur les bases de données avec des millions de clés, cela peut prendre beaucoup de temps et bloquer le serveur Redis, affectant tous les autres clients.
  • Solution : Évitez KEYS sur les grands espaces de clés de production. Utilisez SCAN lorsque vous avez besoin d'une itération incrémentielle des clés. SCAN renvoie un sous-ensemble de clés correspondant à un modèle à chaque appel, ce qui réduit le risque de bloquer le serveur pendant une longue période.
      # Au lieu de KEYS user:*
      redis-cli -h <host> -p <port> SCAN 0 MATCH user:* COUNT 100
    
    Vous devrez appeler SCAN plusieurs fois, en utilisant le curseur renvoyé par l'appel précédent, jusqu'à ce que le curseur revienne à 0.

2. Scripts complexes (scripts Lua)

  • Problème : Les scripts Lua de longue durée ou inefficaces exécutés via EVAL ou EVALSHA peuvent bloquer le serveur. Bien que Redis exécute les scripts de manière atomique, un seul script long peut monopoliser la boucle d'événements.
  • Solution : Optimisez vos scripts Lua. Décomposez la logique complexe en scripts plus petits et gérables. Analysez les performances des scripts. Assurez-vous que les boucles dans les scripts sont efficaces et se terminent correctement. Évaluez vos scripts pour comprendre leur temps d'exécution.

3. Opérations sur les grandes structures de données

  • Problème : Les commandes comme SMEMBERS sur un ensemble avec des millions de membres, LRANGE sur une très longue liste, ou ZRANGE sur un énorme ensemble trié peuvent être lentes.
  • Solution : Évitez de récupérer des structures de données volumineuses entières. Utilisez plutôt des commandes itératives ou traitez les données par morceaux :
    • Ensembles : Utilisez SSCAN au lieu de SMEMBERS.
    • Listes : Utilisez LRANGE avec des valeurs start et stop plus petites pour récupérer les données par pages.
    • Ensembles triés : Utilisez ZRANGE avec LIMIT ou ZSCAN.

4. Commandes nécessitant une itération de clés (moins courantes mais possibles)

  • Problème : Bien que moins courantes, les commandes qui pourraient implicitement itérer sur les clés en raison de leur nature pourraient être lentes si l'espace de clés est grand.
  • Solution : Consultez la référence de commande Redis pour la commande spécifique et comprenez sa complexité. Envisagez des structures de données ou des approches alternatives si une commande spécifique s'avère être un goulot d'étranglement.

5. Commandes bloquantes (rares dans Redis moderne)

  • Problème : Les anciennes versions de Redis avaient certaines commandes qui pouvaient bloquer le serveur. La plupart d'entre elles ont été traitées ou remplacées.
  • Solution : Assurez-vous d'utiliser une version récente de Redis. Consultez la documentation Redis pour toute opération de blocage connue spécifique à votre version.

Décidez d'abord si Redis est lent ou si le client attend

Quand quelqu'un dit "Redis est lent", cela peut signifier plusieurs choses différentes. Le serveur peut mettre trop de temps à exécuter une commande. Le client peut attendre sur le réseau. Un pool de connexions peut être épuisé. Un proxy TLS peut être surchargé. Une réponse volumineuse peut prendre plus de temps à transférer que la commande à exécuter.

SLOWLOG enregistre uniquement le temps d'exécution des commandes à l'intérieur de Redis. Il n'inclut pas le temps de transfert réseau, le temps de mise en file d'attente du client ou le temps passé à attendre une connexion d'un pool d'applications. C'est pourquoi un journal lent propre ne prouve pas toujours que les utilisateurs imaginent une latence.

Comparez trois vues :

redis-cli --latency -h <host> -p <port>
redis-cli --latency-history -h <host> -p <port>
redis-cli SLOWLOG GET 10

Si la latence est élevée mais que SLOWLOG est vide, examinez le réseau, les pools de clients, la saturation du processeur du serveur, l'activité de fork, la persistance ou les réponses volumineuses. Si SLOWLOG montre des commandes coûteuses répétées, commencez par la conception des commandes et des structures de données.

Dans les applications, ajoutez une temporisation autour des appels Redis à la limite du client. Enregistrez la famille de commandes, le modèle de clé, le temps écoulé et si le client a attendu une connexion de pool. Ne journalisez pas les secrets ou les charges utiles complètes. Une petite quantité de temporisation structurée répond généralement si le délai se situe à l'intérieur de Redis ou avant même que la commande ne l'atteigne.

Utilisez la complexité des commandes comme test de détection

Les commandes Redis sont rapides lorsqu'elles touchent une petite quantité de données limitée. Elles deviennent risquées lorsqu'elles analysent un grand espace de clés, renvoient une grande collection ou effectuent un travail proportionnel à une grande valeur.

Avant de blâmer le matériel, vérifiez la complexité de la commande dans la référence de commande Redis pour votre version. Vous n'avez pas besoin de mémoriser chaque étiquette de complexité, mais la forme compte :

  • GET user:123 est limité par la taille d'une valeur.
  • HGET profile:123 email est limité par une recherche de hachage.
  • SMEMBERS followers:celebrity renvoie l'ensemble complet.
  • KEYS * analyse tout l'espace de clés.
  • LRANGE queue 0 -1 renvoie la liste entière.
  • ZREMRANGEBYSCORE peut supprimer un grand nombre de membres d'un ensemble trié.

Le modèle risqué est généralement "donne-moi tout". Cela peut fonctionner pendant des mois, puis échouer lorsqu'un ensemble passe de centaines de membres à des millions. Redis n'est pas soudainement devenu lent ; les données ont franchi le point où une commande illimitée est devenue visible.

Remplacements plus sûrs pour les modèles lents courants

Remplacez les commandes d'espace de clés entier et de collection entière par des modèles incrémentiels.

Pour la découverte de clés, utilisez SCAN :

redis-cli --scan --pattern 'user:*'

Pour les ensembles, utilisez SSCAN :

SSCAN active_users 0 COUNT 500

Pour les hachages, utilisez HSCAN :

HSCAN user:123:settings 0 COUNT 200

Pour les ensembles triés, préférez les plages avec des limites explicites :

ZRANGE leaderboard 0 99 WITHSCORES
ZRANGEBYSCORE events 1716600000 1716686400 LIMIT 0 500

Pour les listes, paginez avec des plages limitées :

LRANGE recent_jobs 0 99

SCAN est incrémentiel, mais ce n'est pas une opération magique gratuite. Il peut renvoyer des doublons et ne donne pas un instantané parfaitement cohérent pendant que les clés changent. C'est bon pour la maintenance, la migration et la découverte en arrière-plan. Ce n'est généralement pas la bonne primitive pour un chemin de requête orienté utilisateur qui a besoin d'une liste précise en temps réel.

Les réponses volumineuses peuvent être le coût réel

Une commande peut s'exécuter rapidement et toujours nuire à votre application si elle renvoie trop de données. SMEMBERS sur un grand ensemble, HGETALL sur un grand hachage, ou MGET sur des milliers de grandes valeurs peuvent prendre du temps à sérialiser la réponse et à l'envoyer sur le réseau. Ce coût peut ne pas apparaître clairement comme un simple temps d'exécution de commande.

Surveillez la sortie réseau et la mémoire du client pendant l'opération lente. Si une seule requête renvoie des dizaines ou des centaines de mégaoctets, reconcevez le modèle d'accès. Stockez les données récapitulatives séparément. Paginez le résultat. Utilisez un index d'ensemble trié et récupérez uniquement la tranche visible. Évitez de placer de grands documents dans Redis lorsque l'application a généralement besoin d'un seul champ.

Un exemple pratique : si un tableau de bord affiche les 50 derniers travaux, ne stockez pas chaque ID de travail dans une liste et n'appelez pas LRANGE jobs 0 -1 avant de découper dans l'application. Stockez la liste dans l'ordre du plus récent au plus ancien et demandez uniquement ce dont la page a besoin :

LRANGE jobs:recent 0 49

Ce petit changement peut supprimer une quantité surprenante de latence et de pression mémoire.

MONITOR est un scalpel, pas un tableau de bord

MONITOR est utile lorsque vous avez besoin de voir exactement quelles commandes un client envoie, surtout lorsque vous soupçonnez que l'application fait quelque chose de différent de ce que suggère la revue de code. Mais sur un serveur Redis occupé, MONITOR crée une surcharge et produit un flot de sortie.

Utilisez-le pour une fenêtre courte et contrôlée :

redis-cli MONITOR | head -n 200

Arrêtez-le ensuite. En production, préférez l'échantillonnage à partir des journaux d'application, des statistiques de commandes Redis ou d'une courte fenêtre de maintenance lorsque cela est possible.

INFO commandstats est souvent plus sûr pour une vue d'ensemble :

redis-cli INFO commandstats

Il montre les compteurs d'appels par commande et les microsecondes cumulées. Il ne vous dira pas quelle clé était lente, mais il peut révéler qu'une application émet beaucoup plus d'appels HGETALL, KEYS ou EVAL que prévu.

Les scripts Lua ont besoin de limites

Les scripts Lua sont puissants car ils s'exécutent de manière atomique dans Redis. Ce même comportement atomique signifie qu'un script long bloque les autres commandes pendant son exécution. Les scripts lents proviennent souvent de boucles sur de grandes collections, de découverte de clés illimitée ou d'une logique qui est passée d'un petit assistant à une mini-application.

Examinez les scripts avec les mêmes questions :

  • Combien de clés cela peut-il toucher ?
  • Combien d'éléments cela peut-il parcourir ?
  • Que se passe-t-il lorsque la clé d'entrée a un million de membres ?
  • Le travail peut-il être divisé en morceaux plus petits ?
  • Le script renvoie-t-il une charge utile volumineuse ?

Si un script apparaît dans SLOWLOG, résistez à la tentation d'augmenter uniquement slowlog-log-slower-than. Le journal vous dit qu'un bloc atomique prend suffisamment de temps pour affecter les autres clients.

Persistance, forks et "commandes lentes" qui sont des symptômes

Parfois, les commandes sont lentes parce que Redis est occupé par un travail en arrière-plan. Les instantanés RDB et les opérations de réécriture AOF peuvent augmenter le processeur, la pression mémoire et les E/S disque. Sous Linux, forker un processus Redis volumineux peut également créer des pics de latence, en particulier lorsque la surallocation mémoire, les pages énormes ou le stockage lent sont impliqués.

Vérifiez :

redis-cli INFO persistence
redis-cli INFO stats
redis-cli INFO memory
redis-cli LATENCY LATEST

Si les pics de latence coïncident avec les sauvegardes en arrière-plan ou les réécritures AOF, réglez la persistance avec soin. Vous pouvez avoir besoin d'un stockage plus rapide, de politiques de sauvegarde ajustées, de seuils de réécriture AOF ou de paramètres mémoire. Ne désactivez pas la persistance simplement pour améliorer un benchmark, sauf si Redis est purement un cache jetable et que l'entreprise accepte de perdre les données.

Le comportement du client peut surcharger Redis sans une seule mauvaise commande

Un serveur Redis peut être affecté par des millions de petits appels inefficaces tout autant que par une commande évidemment lente. Une page qui effectue 200 appels GET séquentiels semblera lente même si chaque GET individuel est rapide.

Utilisez le pipelining lorsque l'application a besoin de nombreuses commandes indépendantes et peut tolérer de recevoir les réponses ensemble :

GET user:1
GET user:2
GET user:3

envoyé comme un pipeline évite un aller-retour par commande. Le pipelining ne remplace pas une bonne modélisation des données et peut augmenter l'utilisation de la mémoire si les lots sont trop volumineux. Commencez par des tailles de lot modestes et mesurez.

Inspectez également les pools de connexions. Si les journaux d'application montrent des appels Redis prenant 500 ms mais que Redis ne voit aucune commande lente, l'application attend peut-être une connexion libre. Augmentez le pool uniquement après avoir vérifié pourquoi les connexions existantes sont occupées. Un pool plus grand peut masquer le symptôme tout en augmentant la pression sur Redis.

Une liste de contrôle pratique pour les incidents

Lorsque la latence de Redis nuit aux utilisateurs, collectez les faits dans cet ordre :

date -u
redis-cli PING
redis-cli --latency -i 1
redis-cli SLOWLOG GET 20
redis-cli INFO commandstats
redis-cli INFO clients
redis-cli INFO memory
redis-cli INFO persistence
redis-cli LATENCY LATEST

Demandez ensuite ce qui a changé : un déploiement, un nouveau point de terminaison, un événement de croissance des données, un travail par lots, une migration, une requête de tableau de bord, un nouveau modèle de clé de cache ou une réécriture de persistance. Les ralentissements Redis sont souvent liés à un modèle d'accès unique qui est devenu populaire ou à une clé qui est devenue beaucoup plus grande que prévu.

Pour chaque commande lente, notez le modèle de clé et le propriétaire. "SMEMBERS lent" ne suffit pas. "Le service de recommandations appelle SMEMBERS product:123:viewers sur un ensemble qui peut croître sans limite" est exploitable.

Résumé de la liste de contrôle d'optimisation des performances

  1. Activez et surveillez SLOWLOG : Examinez périodiquement SLOWLOG GET pour identifier les commandes lentes récurrentes. Ajustez slowlog-log-slower-than si nécessaire.
  2. Utilisez MONITOR avec prudence : Pour le débogage en temps réel lors de ralentissements présumés, mais désactivez-le immédiatement après.
  3. Évitez KEYS sur les grands espaces de clés de production : Utilisez SCAN pour une itération incrémentielle lorsque la découverte de clés est vraiment nécessaire.
  4. Optimisez les scripts Lua : Assurez-vous que les scripts EVAL et EVALSHA sont efficaces et ne s'exécutent pas excessivement longtemps.
  5. Traitez les grandes structures de données de manière itérative : Utilisez SSCAN, ZSCAN, LRANGE avec limites, ou SCAN au lieu de récupérer des collections entières.
  6. Analysez les arguments des commandes : Assurez-vous que les arguments passés aux commandes ne provoquent pas de comportement inattendu (par exemple, des comptes très volumineux, des modèles complexes).
  7. Surveillez les ressources du serveur : Gardez un œil sur l'utilisation du processeur, de la mémoire et du réseau du serveur Redis. Les commandes lentes peuvent parfois être un symptôme d'un serveur surchargé.
  8. Optimisations côté client : Vérifiez que votre application n'envoie pas de commandes trop rapidement ou par lots inefficaces. Envisagez le pipelining pour plusieurs commandes le cas échéant.

Vérification finale

Utilisez SLOWLOG pour trouver les commandes lentes à l'intérieur de Redis, les outils de latence pour détecter les pics côté serveur et la temporisation de l'application pour détecter l'attente du client. Corrigez ensuite le modèle d'accès, pas seulement le seuil. Les commandes limitées, les réponses plus petites, le traitement par lots raisonnable et la propriété claire des grandes clés font plus pour les performances de Redis que la recherche de modifications ponctuelles.