Fondamentaux du cache Redis : modèles, pièges et concepts pour les entretiens
Le cache Redis dans Node.js : approche cache-aside, TTL, protection contre les surcharges et politiques d’éviction, ainsi que les questions d’entretien sur la conception de systèmes liées à ces concepts.
Sources: Redis Isn’t Just a Cache: The Concepts Every Backend Developer Should Know
Pourquoi avons-nous besoin d’un cache alors que nous disposons déjà d’une base de données ?
Imaginez un point d’entrée dans votre application qui ressemble à ceci :
GET /products/123
Un client accède à cette route, et votre serveur interroge alors MongoDB :
API → MongoDB → Product
La base de données renvoie l’enregistrement du produit, et tout fonctionne correctement.
Imaginons maintenant que 10 000 personnes demandent ce même produit en même temps approximativement.
Vous seriez alors contraint de répéter exactement le même aller-retour des milliers de fois :
Request
↓
Database
↓
Same Product
Votre base de données continue de réaliser des tâches identiques pour des requêtes identiques.
N’ serait-il pas préférable que la réponse puisse être mémorisée quelque part ?
C’est essentiellement à cela que sert le caching.
Qu’est-ce que le caching ?
Un cache est une couche de stockage plus rapide qui conserve temporairement les données fréquemment demandées, afin que votre application ne soit pas contrainte d’accéder au stockage principal, plus lent, à chaque fois.
Au lieu du flux habituel :
Request → Database → Response
nous introduisons un raccourci :
Request
↓
Cache
↓
Hit? ── Yes → Response
│
No
↓
Database
↓
Cache
↓
Response
L’objectif n’est pas d’éliminer complètement la base de données.
C’est plutôt de réduire les opérations sur la base de données qui n’ont pas besoin d’être effectuées deux fois.
Caché trouvé vs Caché manquant
Ces deux concepts reviennent constamment, il est donc utile de les connaître par cœur.
Caché trouvé
Cela se produit lorsque les données que vous cherchez sont déjà présentes dans le cache.
Request
↓
Redis
↓
Found ✅
↓
Response
Caché manquant
Cela se produit lorsque le cache ne contient rien pour vous.
Request
↓
Redis
↓
Not Found ❌
↓
Database
↓
Store in Redis
↓
Response
Une métrique utile ici est le taux de caché trouvé:
Cache Hit Ratio =
Cache Hits / Total Cache Requests
Un ratio plus élevé indique généralement que davantage de requêtes sont traitées directement depuis le cache plutôt que depuis la base de données sous-jacente.
Cela dit, un chiffre plus grand ne signifie pas automatiquement succès. Si le cache est rempli d’entrées obsolètes ou de faible valeur, un taux élevé de réponses depuis le cache n’en fait pas pour autant une solution avantageuse.
Alors, quel rôle joue Redis ?
Redis est un stockage de données en mémoire fréquemment utilisé pour le caching, bien que ses capacités dépassent de loin ce seul usage.
Il propose une série de structures de données, notamment :
- Chaînes de caractères
- Hashs
- Listes
- Ensembles
- Ensembles triés
- Flux
- JSON
- Et d’autres types spécialisés
Pour un cache simple, on peut l’imaginer de la manière suivante :
Key Value
────────────────────────────
product:123 Product data
user:42 User data
Puisque Redis stocke les données fréquemment utilisées en mémoire, les lectures provenant du cache peuvent être extrêmement rapides.
Redis dans une application Node.js
Avec le client node-redis, vous l’installez comme suit :
npm install redis
Établir une connexion de base ne nécessite que quelques lignes de code :
import { createClient } from "redis";
const redis = createClient();
await redis.connect();
Par la suite, stocker et récupérer des valeurs est simple :
await redis.set("user:42", "Hamza");
const user = await redis.get("user:42");
La documentation officielle de Redis indique actuellement node-redis comme le client recommandé pour les projets Node.js.
Cependant, connaître les commandes SET et GET ne suffit que partiellement.
La vraie question à laquelle il faut répondre est :
Comment concevoir réellement une application autour de Redis ?
Le pattern le plus important : Cache-Aside
Si un schéma existe que tout développeur backend devrait apprendre en premier, c’est le Cache-Aside.
Dans ce schéma, c’est l’application elle-même qui est chargée de gérer le cache.
Request
↓
Check Redis
↓
┌───────────────┐
│ Cache Hit? │
└───────────────┘
↓ Yes ↓ No
Response Database
↓
Redis
↓
Response
Une version simplifiée en code ressemble à ceci :
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);const product = await Product.findById(id);await redis.set(key, JSON.stringify(product), {
EX: 300
});
L’option EX: 300 définit un TTL de 300 secondes pour l’entrée mémorisée.
Cache-aside est sans doute le schéma de mise en cache le plus utilisé avec Redis.
TTL : Le cache ne doit pas durer éternellement
Supposons que vous ayez mémorisé ce qui suit :
product:123
Que se passe-t-il si le prix du produit est mis à jour par la suite ?
Sans date d’expiration, les utilisateurs pourraient continuer à voir le prix ancien indéfiniment.
C’est la raison d’existence du TTL, ou Time To Live.
Product stored
↓
TTL = 300 seconds
↓
Expires
↓
Next request → Database → Fresh cache
Le TTL impose une limite sur la durée pendant laquelle des données obsolètes peuvent être servies.
Néanmoins, résistez à l’envie de choisir un TTL excessivement long.
La valeur appropriée dépend entièrement de le niveau d’obsolètement que votre application peut supporter.
Invalidation du cache : le célèbre problème difficile
Considérez ce scénario :
Database:
price = ₹799
Redis:
price = ₹799
Ensuite, la base de données est mise à jour :
Database:
price = ₹899
Mais Redis continue de conserver :
price = ₹799
À ce stade, votre application sert des données incohérentes en fonction de la source d’lecture.
Une solution courante consiste à vider le cache chaque fois que les données de base changent :
await Product.findByIdAndUpdate(id, update);
await redis.del(`product:${id}`);
Après cela, la prochaine requête suit ce chemin :
Redis → Miss
↓
Database → ₹899
↓
Redis → Store fresh value
Pour les systèmes basés sur le principe cache-aside, supprimer simplement l’entrée de cache concernée à chaque écriture s’avère généralement plus simple et plus sûr que de tenter de maintenir le cache en permanence synchronisé avec la base de données.
Trois problèmes de cache que vous devez connaître
Ces sujets constituent d’excellents thèmes de discussion lors d’entretiens.
1. Cache Stampede
Imaginez qu’une clé fortement utilisée arrive à expiration :
product:123 → EXPIRED
Immédiatement après, 1 000 requêtes arrivent en même temps.
Chacune d’elles échoue à trouver la donnée dans le cache.
Chacune d’elles doit donc se tourner vers la base de données.
1000 requests
↓
1000 DB queries 💥
Le cache était censé protéger la base de données contre les charges excessives, mais l’événement d’expiration a lui-même provoqué une augmentation brutale de la charge.
Les méthodes typiques pour y remédier incluent :
- Le verrouillage ou les requêtes séquentielles, afin qu’une seule requête réapprovisionne le cache tandis que les autres attendent
La documentation officielle de Redis traite explicitement des stratégies de protection contre les surcharges liées aux clés très utilisées.
2. Pénétration du cache
Que se passe-t-il lorsque les clients continuent de demander des données qui n’existent tout simplement pas ?
GET /users/999999
Redis renvoie une réponse d’erreur de non-réponse.
La base de données renvoie « non trouvé ».
La demande identique suivante reproduit exactement le même cycle.
Un acteur malveillant ou un client défectueux pourrait exploiter cela pour submerger la base de données de requêtes inutiles.
cachage négatif — c’est-à-dire enregistrer le fait qu’une donnée est absente :
user:999999 → NOT_FOUND
TTL → short
Avec cela en place, les recherches répétées pour le même enregistrement inexistant peuvent être traitées directement depuis Redis, sans avoir à consulter la base de données à chaque fois.
3. Avalanche de cache
Imaginons maintenant un grand nombre de clés mémorisées qui expirent toutes à peu près au même moment.
Key A → expired
Key B → expired
Key C → expired
Key D → expired
↓
Massive DB traffic
Cette situation est connue sous le nom d’avalanche de cache.
Une façon typique de réduire ce risque consiste à éviter d’attribuer des dates d’expiration identiques à de grands groupes de clés — par exemple, en ajoutant une légère variation aléatoire à chaque TTL.
Éviction de cache : que se passe-t-il lorsque Redis manque de mémoire ?
La mémoire de Redis n’est pas infinie.
Lorsque l’utilisation de la mémoire atteint le plafond configuré, Redis commence à supprimer des clés conformément à la règle d’éviction que vous avez définie. Redis dispose de plusieurs modes d’éviction, vous permettant d’éliminer les éléments les moins récemment utilisés dans l’ensemble de l’espace des clés ou uniquement parmi celles pour lesquelles une date d’expiration a été fixée, d’éliminer les éléments les moins fréquemment utilisés dans l’un ou l’autre de ces deux domaines, d’éliminer en fonction des clés les plus proches de leur expiration, ou de refuser complètement de nouvelles écritures une fois la mémoire pleine.
LRU signifie essentiellement :
Éliminer tout ce qui n’a pas été utilisé depuis un certain temps.
LFU signifie essentiellement :
Éliminer tout ce qui n’est pas consulté très souvent.
Le choix le plus adapté dépend de vos schémas d’accès spécifiques.
Redis n’est pas seulement un cache
Ce point est souvent abordé lors des entretiens.
Comme Redis propose plusieurs structures de données intégrées, il peut servir à bien plus que simplement au cacheage :
Strings → Caching
Hashes → Objects / sessions
Lists → Queues
Sets → Unique collections
Sorted Sets → Leaderboards
Streams → Event processing
Ainsi, l’affirmation selon laquelle :
"Redis est une base de données de cacheage"
ne décrit qu’une partie de la réalité.
Redis est un stockage de structures de données en mémoire, généralement utilisé pour le cacheage, mais également capable de gérer des sessions, des files d’attente, des classements, la limitation des débits, les messages pub/sub et bien d’autres fonctionnalités.
La documentation officielle de Redis sur les types de données présente ces structures ainsi que d’autres types spécialisés qu’il prend en charge.
Qu’en est-il de la persistance de Redis ?
Puisque Redis fonctionne en mémoire, un redémarrage efface-t-il tout ?
Pas nécessairement.
Redis propose plusieurs stratégies de persistance :
- RDB — prises de vue périodiques à un instant donné du dataset
- AOF — un journal qui enregistre chaque opération d’écriture
- RDB + AOF combinés
- Aucune persistance du tout
Si Redis ne sert que de cache, il est souvent acceptable que son contenu soit perdu à la redémarrage, car l’application peut simplement le recharger depuis la base de données principale.
Cependant, si Redis contient des données qui ont une importance propre, les paramètres de persistance deviennent un problème bien plus sérieux.
Un piège courant lors des entretiens
« Pourquoi ne pas stocker tout simplement tout dans Redis ? »
Parce que la mémoire de Redis est une ressource limitée et relativement coûteuse par rapport aux systèmes de stockage persistant classiques.
Cela signifie qu’il ne faut pas mettre en cache de manière indiscriminée.
Les bons candidats pour le caching présentent ces caractéristiques :
- Ils sont fréquemment demandés
- Leur récupération ou leur calcul est coûteux
- Une certaine obsolétude est tolérable
- Leur stockage en mémoire en vaut la peine
Considérez Redis comme une couche d’amélioration des performances fonctionnant à côté de votre système, et non comme un substitut complet à votre base de données principale.
Cache local vs Cache distribué
Imaginez une application Node.js exécutée sur trois serveurs distincts :
Server A → Cache A
Server B → Cache B
Server C → Cache C
Chacun de ces serveurs finit par gérer son propre cache indépendant.
L’introduction d’une instance Redis partagée change la situation :
┌→ Server A ─┐
Client ──────┼→ Server B ─┼→ Redis
└→ Server C ─┘
Désormais, chaque instance d’application accède au même cache partagé.
Cette avantage lié à l’état partagé est une raison majeure pour laquelle Redis se distingue particulièrement bien lorsque une application est étendue horizontalement.
Le contexte d’un entretien avancé
Supposons qu’un intervieweur pose ce défi :
"Concevez une stratégie de mise en cache pour une API de produit très fréquentée."
"J’utiliserai Redis."
Request
↓
Redis
↙ ↘
Hit Miss
↓ ↓
Response Database
↓
Redis
↓
Response
Puis elle aborde chacune de ces décisions de conception :
1. Schéma de mise en cache
Privilégiez le schéma cache-aside.
2. TTL
Définissez-le en fonction de la tolérance aux données obsolètes du produit.
3. Invalidation
Supprimez la clé correspondante chaque fois que les données de base sont mises à jour.
4. Protection contre les surcharges
Appliquez des techniques de verrouillage, de regroupement des requêtes ou de mise à jour précoce.
5. Éviction
Choisissez une politique LRU ou LFU adaptée au travail à effectuer.
6. Conception des clés
product:{id}
user:{id}
7. Surveillance
Faites attention à :
- Taux de réussite
- Taux d’échec
- Latence
- Utilisation de la mémoire
- Nombre d’évictions
- Clés fréquemment utilisées
- Erreurs Redis
Ce type de raisonnement structuré permet d’obtenir une réponse bien plus convaincante en matière de conception de système que de se contenter d’affirmer que "Redis est rapide."
Révision rapide pour l’entretien
Pourquoi se donner la peine de mettre en cache ?
Cela réduit la latence et épargne votre stockage de données principal d’avoir à traiter sans cesse les mêmes lectures coûteuses.
Pourquoi choisir spécifiquement Redis ?
Celui-ci offre un accès rapide en mémoire, une prise en charge de l’expiration, des structures de données polyvalentes, ainsi que des fonctionnalités conçues pour les systèmes distribués.
Que signifie « cache-aside » ?
L’application cherche d’abord dans le cache ; en cas d’échec, elle interroge la base de données puis écrit ce résultat à nouveau dans le cache.
Que signifie TTL ?
C’est la durée de vie d’une entrée stockée dans le cache avant qu’elle n’expire automatiquement.
Que signifie l’invalidation du cache ?
Cela consiste à effacer ou à mettre à jour les entrées du cache chaque fois que les données sources changent.
Qu’est-ce qu’une « cache stampede » ?
C’est une situation où un grand nombre de requêtes échouent simultanément à trouver des données dans le cache et sollicitent toutes la base de données en même temps.
LRU contre LFU ?
LRU élimine ce qui a été utilisé le moins récemment. LFU élimine ce qui a été utilisé le moins souvent dans l’ensemble.
Toute requête mérite-t-elle d’être mise en cache ?
Pas du tout. Prenez en compte la fréquence d’accès aux données, le coût de leur récupération, le niveau d’obsolescence acceptable et la quantité de mémoire disponible.
Le modèle mental à retenir
Évitez de vous concentrer sur la mémorisation des commandes Redis en tant que point de départ. Préférez comprendre l’architecture sous-jacente :
┌─────────────┐
Request ─────→│ Redis │
└──────┬──────┘
│
Cache Hit?
↙ ↘
YES NO
↓ ↓
Response Database
↓
Redis
↓
Response
Ensuite, posez-vous quatre questions :
Qu’est-ce qui doit être mis en cache ?
Pendant combien de temps doit-il y rester ?
Quand faut-il le rendre invalide ?
Que se passe-t-il si la cache échoue ou expire sous une charge importante ?
C’est en abordant ces questions que l’on passe d’une connaissance superficielle du caching à une réflexion de niveau professionnel.
En conclusion
Le caching n’est pas simplement :
"Placer Redis devant MongoDB."
Une stratégie de mise en cache solide repose fondamentalement sur des compromis.
Vous échangez :
la mémoire contre la vitesse
et, parfois :
la fraîcheur contre les performances
Redis fournit les mécanismes, mais c’est la logique de votre application qui détermine leur utilisation.
Si vous vous préparez à des entretiens, ne vous contentez pas de dire :
"Redis est une base de données en mémoire."
Soyez prêt à expliquer en détail :
la mise en cache latérale, le TTL, l’invalidation, le taux de hits, l’éviction, les phénomènes de stampede et d’avalanche, la mise en cache distribuée, ainsi que la cohérence.
Lorsque vous comprendrez comment ces concepts s’articulent, vous irez au-delà de la simple connaissance de Redis.
Vous comprenez désormais comment les systèmes à fort trafic protègent leurs bases de données tout en restant rapides.