Accueil / Articles / Redis au-delà du caching : sessions, limites de vitesse, files d’attente et Pub/Sub en Node.js

Redis au-delà du caching : sessions, limites de vitesse, files d’attente et Pub/Sub en Node.js

Sept modèles Redis pour les backends Node.js — mise en cache avec des TTL, clés OTP, limites de fréquence, tâches BullMQ, sessions, limites Pub/Sub, et quand ne pas utiliser Redis.

1424 mots

Les premiers modèles mentaux considèrent Redis comme « simplement un cache » : stocker une valeur, définir une date d’expiration, la lire plus rapidement que la base de données, et c’est tout.

Ce tableau n’est pas faux. Il est incomplet.

En pratique, Redis est utilisé pour les réponses API fréquentes, les sessions partagées, les compteurs d’abus, les codes uniques, les files d’attente de tâches différées, la diffusion en temps réel légère, ainsi que pour d’autres valeurs à courte durée de vie.

Une meilleure approche : considérer Redis comme un stockage de données en mémoire rapide ayant de nombreuses fonctions en arrière-plan — le cacheage n’en est que la première.

1. Redis peut rendre une API beaucoup plus rapide

Commencez par le cacheage.

Pensez à GET /products.

Sans cache, chaque requête peut se dérouler comme suit :

Client → API Node.js → PostgreSQL → API Node.js → Client

Une requête coûteuse effectuée des milliers de fois signifie que la base de données doit répéter le même travail.

Redis peut se placer avant cette requête.

Client → API Node.js → Redis

Lorsqu’on trouve la donnée, on la renvoie immédiatement. En cas d’échec, on interroge PostgreSQL, on stocke le résultat dans Redis, puis on le renvoie.

Un exemple simple avec ioredis :

import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
async function getProducts() {
  const cached = await redis.get("products");
  if (cached) {
    return JSON.parse(cached);
  }
  const products = await database.product.findMany();
  await redis.set(
    "products",
    JSON.stringify(products),
    "EX",
    300
  );
  return products;
}h

Ici, EX signifie que le contenu mis en cache expire après 300 secondes.

Il reste un problème majeur : la invalidation du cache.

Supposons que Redis contienne product:123 → price:500 tandis que PostgreSQL enregistre désormais 600. La base de données est correcte ; Redis peut toutefois encore afficher 500.

Mettre en cache ne signifie pas « mettre tout dans Redis ». Les équipes doivent encore prendre en compte :

  • TTL
  • Invalidation
  • Données obsolètes
  • Échecs de recherche
  • Faillites du cache

Mettre une valeur dans Redis est simple. La maintenir à jour est plus difficile.

2. Redis convient bien aux données temporaires

Les valeurs à durée de vie courte conviennent parfaitement : codes d’accès uniques, liens de réinitialisation, tokens de vérification, blocs de session éphémères, compteurs d’abus et verrous de sécurité.

Exemple : émission d’un code unique :

const otp = "482913";
await redis.set(
  `otp:${userId}`,
  otp,
  "EX",
  300
);

L’OTP expire automatiquement après cinq minutes.

Aucune table dédiée à l’OTP n’est nécessaire, pas plus de tâche de nettoyage distincte.

Lisez-le à nouveau avec :

const otp = await redis.get(`otp:${userId}`);

Une fois la durée de vie écoulée, Redis supprime la clé conformément à son mécanisme d’expiration.

Ce schéma fait de Redis un hébergement idéal pour les données d’application à durée de vie courte.

3. Redis peut aider pour la limitation des débits

Prenons l’exemple de POST /login.

Sans limites, un client peut envoyer des tentatives d’accès en masse — des milliers de requêtes consécutives.

Redis peut gérer un compteur pour ce client :

const key = `login-attempts:${ip}`;
const attempts = await redis.incr(key);
if (attempts === 1) {
  await redis.expire(key, 60);
}
if (attempts > 10) {
  throw new Error("Too many requests");
}

La structure est : IP → compteur Redis → nombre de tentatives → limite.

Cela revêt une importance accrue lorsqu’il y a plusieurs serveurs API derrière un équilibrateur de charge. Les compteurs en mémoire par processus ne sont pas globaux. Redis fournit à ces serveurs un stockage partagé.

4. Redis peut gérer des tâches en arrière-plan

La création d’un compte peut nécessiter la création de l’utilisateur, l’envoi d’un e-mail de bienvenue, la génération de données, la notification d’un autre service, ainsi que l’exécution d’autres tâches.

La requête HTTP ne doit pas attendre que toutes ces opérations soient terminées.

Mieux vaut envoyer les tâches dans une file d’attente :

Client → API → File d’attente → Réponse

Puis :

File d’attente → Travailleur → Traitement de la tâche

BullMQ est une option courante pour Node.js qui utilise Redis :

await emailQueue.add("welcome-email", {
  userId: user.id,
  email: user.email,
});

Un travailleur traite chaque tâche séparément :

const worker = new Worker(
  "email",
  async (job) => {
    if (job.name === "welcome-email") {
      await sendWelcomeEmail(job.data.email);
    }
  },
  {
    connection: redisConnection,
  }
);

L’API n’a pas besoin d’attendre le fournisseur d’e-mails avant de répondre à l’utilisateur.

Cela convient aux tâches lentes, réexécutables, dépendantes de services externes, gourmandes en CPU, ou qui ne sont pas nécessaires avant que l’API ne réponde.

Division des responsabilités : l’API gère la demande ; le processus secondaire s’occupe du travail lourd.

5. Redis peut stocker des sessions

La gestion des sessions est un autre domaine d’application adapté. Une clé telle que session:abc123 peut contenir :

{
  "userId": "123",
  "role": "ADMIN"
}

Cela devient précieux lorsque plusieurs instances backend partagent un même stockage de sessions via Redis.

Distinction importante : Redis ne rend pas automatiquement l’authentification sécurisée.

Les équipes doivent encore gérer les identifiants de session, les cookies sécurisés, leur expiration, le CSRF le cas échéant, ainsi que l’authentification et l’autorisation.

Redis est une infrastructure, pas une stratégie de sécurité.

6. Redis peut aider pour les fonctionnalités en temps réel

Pub/Sub permet de diffuser des événements entre les instances. Un scénario typique consiste en un client qui contacte le serveur A, une publication dans Redis, un gestionnaire d’abonnement sur le serveur B, puis une livraison à un autre client.

Illustration :

await redis.publish(
  "notifications",
  JSON.stringify({
    userId: "123",
    message: "Your order has shipped",
  })
);
await subscriber.subscribe("notifications")
subscriber.on("message", (channel, message) => {
  console.log(channel, message);
});

Utile pour les notifications, les mises à jour en temps réel, les flux liés aux chats et la propagation des événements.

Limitation : Redis Pub/Sub n’est pas une file d’attente de messages durables.

Si le traitement durable, les tentatives répétées ou une livraison garantie sont importants, privilégiez une file d’attente ou Redis Streams en fonction du cas.

Il est essentiel de connaître cette différence.

7. Redis devient un problème s’il est utilisé partout

La leçon la plus importante : une fois que Redis est disponible, il est tentant de y placer tout ce qui existe.

Ne le faites pas.

La vitesse seule ne signifie pas que chaque donnée doit être stockée en mémoire.

PostgreSQL peut rester la source de vérité permanente pour les données métier, tandis que Redis gère le cache, les sessions, les OTP, les limites de vitesse et les files d’attente.

Une séparation pratique consiste à conserver les archives commerciales durables dans PostgreSQL et à réserver Redis pour les chemins fréquemment utilisés, avec des durées de vie courtes, ainsi que pour des charges de travail telles que les files d’attente ou les compteurs.

Définir cette frontière dès le début évite de nombreux redessins ultérieurs.

Les structures de données Redis sont importantes

Redis n’est pas seulement basé sur le format clé → chaîne de caractères. Il propose plusieurs structures.

Chaînes de caractères

Valeurs simples, comme user:123:name → “Mit”.

Hashes

Plusieurs champs sous une seule clé :

user:123
name → Mit
role → ADMIN
email → example@email.com

Listes

Collections ordonnées et certains schémas similaires à des files d’attente.

Ensembles

Valeurs uniques.

Ensembles triés

Éléments ordonnés selon un score — par exemple, un classement :

1000 → Player A
900  → Player B
800  → Player C

Choisir la bonne structure simplifie souvent le problème.

Erreurs courantes à éviter avec Redis

Erreur 1 : mettre en cache tout ce qui est possible

Toute requête n’a pas besoin d’un cache. Le caching ajoute de la complexité. Si une requête est déjà suffisamment rapide, Redis risque de résoudre un problème qui n’existe pas.

Erreur 2 : absence de date d’expiration

Les données temporaires sans TTL s’accumulent. Si les données n’ont pas besoin de durer éternellement, définissez une politique de date d’expiration pour elles.

Erreur 3 : considérer Redis comme la base de données permanente

Si Redis contient la seule copie des données commerciales critiques, le système présente une dépendance sérieuse. Il est essentiel de connaître la source fiable des données.

Erreur 4 : ignorer les pannes de Redis

Décidez de ce qui se passe lorsque Redis est indisponible. Pour de nombreuses utilisations de cache, il est acceptable de recourir à la base de données en cas de panne. La stratégie appropriée dépend du rôle joué par Redis.

Erreur 5 : utiliser Redis sans comprendre la charge de travail

La vitesse n’est pas infinie. Il faut encore réfléchir à la mémoire, à l’éviction des données, aux connexions, à la conception des clés, aux TTL, à la sérialisation, au retard réseau et aux besoins de persistance.

Comment Redis change la façon de penser les backends

De nombreux designs commencent par un gestionnaire de requêtes qui communique directement avec SQL.

Lorsqu’apparaissent un stockage en mémoire et des travailleurs différés, le flux de traitement s’allonge souvent : le gestionnaire vérifie d’abord Redis, puis la base de données ; ou bien il enfile les tâches dans une file d’attente destinée à un travailleur qui communique avec un fournisseur externe.

Les backends deviennent des ensembles de composants spécialisés. Redis est l’un de ces composants qui peut jouer plus d’un rôle.

Remarque finale

Ne adoptez pas Redis simplement parce que tout le monde l’a fait.

Optez pour ces solutions lorsque le besoin est clair : le cacheage pour les lectures fréquentes, les clés TTL pour les secrets à courte durée de vie, les compteurs pour limiter les abus, les files d’attente pour les tâches différées, un stockage de sessions partagé entre instances, ou Pub/Sub lorsque des distributions légères suffisent.

Évitez de faire de Redis la solution par défaut à chaque problème lié au backend.

Les bons systèmes ne sont pas ceux qui disposent de la liste la plus longue d’outils. Ce sont ceux où chaque outil a sa raison d’être.