NoSQL et Redis
Toutes les données ne se rangent pas bien dans des tables. Les bases NoSQL proposent d'autres rangements : documents, colonnes larges, graphes, ou simples paires clé-valeur. Redis, qui garde ses données en mémoire vive, se place souvent à côté d'une base relationnelle : il sert de cache, compte des visites, tient des classements… à condition d'en connaître les règles.
Après cette fiche, tu sais
- situer les grandes familles NoSQL face au modèle relationnel, et choisir selon le besoin ;
- manipuler des clés Redis (SET, GET, DEL, INCR) et expliquer pourquoi INCR ne perd aucune mise à jour ;
- donner une durée de vie à une clé et appliquer le modèle cache-aside ;
- choisir entre chaîne, hash, liste, ensemble et ensemble trié ;
- dire ce que Redis retrouve après une panne, et ce qu'il fait quand la mémoire est pleine ;
- parcourir les clés avec SCAN plutôt qu'avec KEYS.
Quatre autres façons de ranger les données
Une base relationnelle range les données dans des tables normalisées, avec un schéma fixé à l'écriture ; elle vérifie l'intégrité, offre des transactions ACID et des requêtes SQL riches. Les bases non relationnelles, aussi appelées bases NoSQL, privilégient les schémas souples, la mise à l'échelle horizontale et des façons précises d'accéder aux données ; elles relâchent souvent une partie du modèle relationnel, comme la rigidité du schéma ou l'étendue des transactions.1
Prenons une seule élève, Mia, en classe 3M2, inscrite en maths et en info. Fais défiler : la même fiche, cinq rangements.
Trois tables reliées par des clés étrangères. Le schéma est fixé à l'écriture et l'intégrité est vérifiée ; pour relire toute la fiche de Mia, il faut une jointure.
Toute la fiche dans un seul document JSON, avec sa liste de cours. Chaque document a ses propres champs et peut évoluer. Usages typiques : catalogues de produits, gestion de contenu, profils.
Une clé unique, une valeur que la base ne regarde pas : on lit, on écrit et on supprime par la clé. Simple et rapide : cache, sessions, profils d'utilisateurs.
Des lignes éparses dont les colonnes sont regroupées en familles : une ligne ne stocke que les colonnes qu'elle possède. Fort débit d'écriture : télémétrie des objets connectés, personnalisation.
Des nœuds et des arêtes, chacun avec ses propriétés : les relations deviennent des données qu'on parcourt. Réseaux sociaux, réseaux de fraude, graphes de connaissances.
Choisir un modèle
| Besoin | Modèle conseillé |
|---|---|
| Transactions strictes sur plusieurs entités | Relationnel |
| Agrégats qui évoluent, API centrées sur JSON | Document |
| Lectures par clé ou cache à très faible latence | Clé-valeur |
| Télémétrie volumineuse, éparse, beaucoup d'écritures | Colonnes larges (ou séries chronologiques) |
| Parcours de relations en profondeur | Graphe |
Ce n'est pas l'un ou l'autre : la plupart des systèmes en production combinent plusieurs modèles, chacun pour les données qui lui conviennent. On parle de persistance polyglotte.1 Dans la plupart des magasins clé-valeur, lire ou écrire une valeur est une opération atomique.1
Quel modèle pour quel besoin ?
Touche une situation, puis le modèle qui lui convient le mieux.
Un dictionnaire géant, en mémoire
Redis est un serveur de structures de données : il associe des clés à des valeurs typées, chaînes, hashes, listes, ensembles, ensembles triés…3 Son nom vient de REmote DIctionary Server.2
Il garde tout son jeu de données en mémoire vive, et peut l'écrire sur disque pour le retrouver après un redémarrage (notion 5). Lectures et écritures sont donc très rapides, mais les données ne peuvent pas dépasser la mémoire disponible. Ordre de grandeur donné par la documentation : un million de petites paires clé-chaîne occupent environ 85 Mo sur une machine 64 bits.2
> SET eleve:42:prenom Mia OK > GET eleve:42:prenom "Mia" > SET eleve:42:prenom Lina OK > GET eleve:42:prenom "Lina" > EXISTS eleve:42:prenom eleve:99:prenom (integer) 1 > DEL eleve:42:prenom (integer) 1 > GET eleve:42:prenom (nil)
Écrit une chaîne. Si la clé existe, sa valeur est remplacée, quel que soit son type, et sa durée de vie est effacée.
Lit la chaîne ; (nil) si la clé n'existe pas.
Supprime les clés données et renvoie le nombre de clés supprimées ; une clé absente est ignorée.
Renvoie le nombre de clés existantes parmi celles données.
Une chaîne peut contenir n'importe quels octets (texte, JSON, image), jusqu'à 512 Mo par valeur.3 Pour nommer les clés, la documentation compose les siennes avec des deux-points, comme bike:1 ou bike:1:stats : un type d'objet, un identifiant, éventuellement un champ.
SET k a b répond (error) ERR syntax error : Redis lit b comme une option de SET. Avec des guillemets, SET k "a b" range bien la valeur a b.
Compter sans rien perdre
INCR ajoute 1 au nombre stocké dans une clé ; si la clé n'existe pas, elle part de 0. Redis n'a pas de type entier : la chaîne est lue comme un entier signé de 64 bits, et une valeur qui n'est pas un entier provoque une erreur.3 Surtout, INCR est une opération atomique : lire, ajouter et écrire se fait pendant qu'aucun autre client n'exécute de commande.3 Fais défiler pour voir la différence.
Lire, puis écrire
dans Redis 10visites perdues 0
Prédis la réponse
Pour chaque commande, choisis ce que redis-cli affiche.
Garder une copie, mais pas trop longtemps
EXPIRE clé secondes donne une durée de vie à une clé : passé ce délai, elle est supprimée automatiquement. Une telle clé est dite volatile. TTL clé donne le temps qui reste, en secondes : -1 si la clé n'a pas d'expiration, -2 si elle n'existe pas.3 L'option EX de SET écrit la valeur et fixe sa durée de vie en une seule commande.
> SET meteo:lausanne "18 degres" EX 60 OK > TTL meteo:lausanne (integer) 60 > SET meteo:lausanne "19 degres" OK > TTL meteo:lausanne (integer) -1 > TTL cle:inexistante (integer) -2
Un SET sans option remplace la valeur et sa durée de vie : TTL repasse à -1. Les commandes qui modifient la valeur sans la remplacer, comme INCR, LPUSH ou HSET, la conservent.3
Redis supprime les clés expirées de deux façons : passivement, quand un client essaie de lire une clé dont le délai est dépassé ; activement, en testant régulièrement quelques clés au hasard parmi celles qui ont une expiration.3
Le modèle cache-aside
Le modèle cache-aside charge les données dans le cache à la demande. L'application lit d'abord le cache ; si l'élément n'y est pas, elle le lit dans le magasin de données, l'ajoute au cache, puis le renvoie. Quand une donnée change, elle écrit la modification dans le magasin, puis invalide l'élément du cache.6 Fais défiler.
L'application demande d'abord la clé à Redis. Elle n'y est pas : GET renvoie (nil). C'est un défaut de cache (en anglais, cache miss).
En cas d'absence, l'application lit la donnée dans le magasin de données, ici la base relationnelle.
Elle range une copie dans Redis avec une durée de vie, EX 300 : cinq minutes. Puis elle renvoie le menu.
La demande suivante trouve la clé : la réponse vient de la mémoire, sans toucher à la base. Tant que la copie vit, la base se repose.
Pour modifier, on écrit d'abord dans la base, puis on supprime la clé du cache. Dans l'ordre inverse, une lecture glissée entre les deux remettrait l'ancien menu en cache.
En Java, avec le client Jedis, le même modèle tient en deux méthodes. lireMenuEnBase et ecrireMenuEnBase sont les accès à la base relationnelle, par exemple en JDBC.
RedisClient redis = RedisClient.create(
"redis://localhost:6379");
String menuDuJour(String jour) {
String cle = "menu:" + jour;
String menu = redis.get(cle);
if (menu == null) { // absent
menu = lireMenuEnBase(jour);
redis.set(cle, menu,
SetParams.setParams().ex(300));
}
return menu;
}
void changerMenu(String jour, String m) {
ecrireMenuEnBase(jour, m); // 1. base
redis.del("menu:" + jour); // 2. cache
}
menuDuJour, une seule lecture en base, TTL de 300 s ; après changerMenu, la clé n'existe plus, et l'appel suivant relit la base et renvoie le nouveau menu.La durée de vie est un réglage délicat : trop courte, l'application relit sans cesse la base ; trop longue, la copie devient périmée. Le cache convient surtout aux données qui changent peu et qu'on lit souvent.6 Revenons à la cantine : 100 demandes par minute, régulières, de 11 h 50 à 12 h 10, et une copie gardée 5 minutes. Fais défiler.
Cache vide
demandes 0lectures en base 0
Que vaut le TTL ?
Trois situations, trois réponses de la commande TTL.
Remets la lecture dans l'ordre
Le corps de menuDuJour a été mélangé : touche une ligne, puis celle avec laquelle l'échanger.
Cinq formes de valeurs
Une clé Redis ne contient pas forcément une chaîne. Les types de base sont les chaînes, les hashes, les listes, les ensembles et les ensembles triés ; d'autres existent, comme les streams ou JSON.3 Chaque type a ses commandes : appliquer à une clé une commande d'un autre type renvoie une erreur WRONGTYPE (« Operation against a key holding the wrong kind of value »). Fais défiler.
Le type de base : une suite d'octets, texte, nombre, objet sérialisé ou image. On l'utilise beaucoup pour le cache ; INCR en fait un compteur.
Un enregistrement fait de paires champ-valeur, parfait pour un objet simple ou un groupe de compteurs. HSET écrit plusieurs champs, HGET en lit un, HINCRBY compte dans un champ.
Une liste chaînée de chaînes, dans l'ordre d'insertion. LPUSH ajoute en tête et RPUSH en queue, en temps constant ; LTRIM ne garde qu'une plage, par exemple les trois plus récentes.
Des chaînes uniques, sans ordre. Ajouter, retirer ou tester l'appartenance prend un temps constant, quelle que soit la taille ; SINTER donne les membres communs à plusieurs ensembles.
Des chaînes uniques, chacune avec un score, toujours rangées par score ; à score égal, l'ordre lexicographique départage. C'est l'outil des classements.
> HSET eleve:42 prenom Mia classe 3M2 (integer) 2 > HINCRBY eleve:42 absences 1 (integer) 1 > HGET eleve:42 classe "3M2" > LPUSH actus a1 a2 a3 (integer) 3 > LPUSH actus a4 (integer) 4 > LTRIM actus 0 2 OK > LRANGE actus 0 -1 1) "a4" 2) "a3" 3) "a2" > SADD cours:maths luca mia noah (integer) 3 > SADD cours:maths mia (integer) 0 > SADD cours:info mia lina noah (integer) 3 > SINTER cours:maths cours:info 1) "mia" 2) "noah"
Les réponses comptent les éléments ajoutés : le second SADD … mia renvoie 0, car mia est déjà membre. L'ordre renvoyé par SINTER peut varier, puisqu'un ensemble n'a pas d'ordre. Un ensemble trié, lui, est toujours rangé : ajouter un membre ou changer son score coûte O(log N), et lire les premiers ne demande aucun tri.3 Fais défiler pour suivre un classement.
Quatre membres
commande 1 sur 71re place noah
Quelle structure ?
Pour chaque besoin, choisis le type de valeur le plus adapté.
En mémoire, mais pas sans filet
La persistance, c'est l'écriture des données sur un stockage durable. Redis propose quatre options : RDB, des instantanés de tout le jeu de données à intervalles ; AOF, un journal de chaque écriture, rejoué au démarrage ; aucune persistance, parfois choisie pour un cache ; ou RDB et AOF ensemble.4
Un fichier compact, dump.rdb, idéal pour les sauvegardes et pour redémarrer vite. Mais après un arrêt brutal, on perd tout ce qui a été écrit depuis le dernier instantané, souvent plusieurs minutes.4
Chaque commande qui modifie les données est ajoutée au journal. Avec le réglage par défaut, un fsync par seconde, on peut perdre au plus environ une seconde d'écritures.4
Une règle d'instantané save 300 100 signifie : écrire un instantané si au moins 100 clés ont changé et que 300 secondes ont passé. Sur Redis 8.2, CONFIG GET save donne par défaut 3600 1 300 100 60 10000, et l'AOF est désactivé (appendonly no). Fais défiler : une élection de délégués, des votes qui arrivent, puis une coupure de courant.
Instantané
étape 1 sur 8en mémoire 1000
La documentation de Redis conseille les deux méthodes ensemble pour une sécurité des données comparable à celle de PostgreSQL ; RDB seul si quelques minutes de perte sont acceptables ; elle déconseille l'AOF seul, car un instantané de temps en temps reste précieux pour les sauvegardes et les redémarrages rapides.4 Le journal contient les commandes au format du protocole de Redis.
save "", appendonly no) : une clé écrite, le serveur arrêté par SHUTDOWN NOSAVE puis relancé, GET renvoie (nil). Avec appendonly yes, la clé est revenue ; le journal contenait la commande sous la forme *3 $3 SET $8 panier:7 $10 3 articles.Après la panne
Trois configurations, trois pannes : que retrouve-t-on ?
Quand la mémoire est pleine
La directive maxmemory fixe la mémoire maximale des données ; 0 signifie aucune limite, ce qui est le réglage par défaut sur un système 64 bits.5 Si la limite est atteinte, Redis répond par défaut avec une erreur aux commandes d'écriture, et continue d'accepter les lectures.2 Il peut aussi évincer des clés, selon la politique choisie avec maxmemory-policy.
| Politique | Quand la mémoire est pleine |
|---|---|
noeviction | Aucune éviction : les commandes qui ajoutent des données renvoient une erreur ; les lectures marchent. |
allkeys-lru | Évince les clés les moins récemment utilisées (LRU). |
allkeys-lfu | Évince les clés les moins fréquemment utilisées. |
volatile-lru | LRU, mais seulement parmi les clés qui ont une expiration. |
volatile-ttl | Évince les clés à expiration dont le TTL restant est le plus court. |
D'autres variantes existent (au hasard, ou selon la dernière modification). Les politiques volatile-… se comportent comme noeviction si aucune clé n'a d'expiration. Pour un cache où une partie des clés est bien plus demandée que le reste, la documentation présente allkeys-lru comme un bon choix par défaut ; Redis n'y calcule pas le LRU exact, il échantillonne quelques clés au hasard et évince la plus ancienne.5 Le nombre d'échantillons se règle avec maxmemory-samples : 5 par défaut sur Redis 8.2.
noeviction : 269 acceptées, puis 131 refusées avec OOM command not allowed when used memory > 'maxmemory'. En allkeys-lru : les 400 acceptées, 169 clés évincées.Lister les clés sans bloquer le serveur
KEYS motif renvoie toutes les clés qui correspondent au motif, en O(N). La documentation le réserve au débogage : il peut ruiner les performances sur une grande base, et ne doit pas apparaître dans le code d'une application. SCAN parcourt les clés par petits lots, avec un curseur : on commence à 0, on renvoie à chaque appel le curseur reçu, et le parcours est terminé quand le curseur revient à 0.3 Fais défiler.
KEYS parcourt toutes les clés en une seule commande. Sur un million de clés, les autres clients attendent derrière, le temps que la réponse entière soit calculée.
SCAN part du curseur 0 et renvoie un petit lot de clés, plus le curseur à utiliser ensuite : ici 917504.
On rappelle SCAN avec le curseur reçu. COUNT n'est qu'une indication : 6 clés cette fois. Entre deux appels, les autres clients sont servis. Le curseur saute d'un endroit à l'autre de la table.
Quand Redis renvoie le curseur 0, le parcours complet est terminé. Un lot vide avec un autre curseur ne signifie pas la fin : il faut continuer.
Une clé présente du début à la fin du parcours est renvoyée au moins une fois ; une clé peut revenir plusieurs fois ; une clé ajoutée ou supprimée pendant le parcours peut apparaître ou non.
KEYS *. Un parcours complet avec SCAN … MATCH eleve:4242* COUNT 1000 a demandé 1000 appels et trouvé les mêmes 111 clés que KEYS ; la plupart des lots étaient vides, car MATCH filtre les clés après les avoir lues.Mémoire et parcours
Quatre questions sur maxmemory, les politiques d'éviction et SCAN.
Les pièges classiques
GET puis SET perd des mises à jour dès que deux clients écrivent en même temps. Utilise les commandes atomiques : INCR, HINCRBY, ZINCRBY.
Sans EXPIRE ni EX, la copie reste jusqu'à ce qu'on la supprime : elle finit par être périmée.
Réécrire une clé avec SET sans option supprime sa durée de vie. Repose-la, ou utilise l'option KEEPTTL pour la garder.
Une seule commande parcourt toutes les clés et bloque les autres clients. Parcours avec SCAN.
Entre les deux, une lecture peut remettre l'ancienne valeur en cache. D'abord la base, ensuite DEL.
Sans RDB ni AOF, un redémarrage efface tout. Pour un cache, c'est acceptable ; pour des votes ou des paniers, non.
Exercices corrigés
Cherche d'abord, puis ouvre le corrigé.
1Compte les visites de la page du menu, jour par jour, et fais disparaître chaque compteur deux jours après sa création.Voir le corrigé
> INCR visites:menu:2026-10-01 (integer) 1 > EXPIRE visites:menu:2026-10-01 172800 NX (integer) 1 > INCR visites:menu:2026-10-01 (integer) 2 > EXPIRE visites:menu:2026-10-01 172800 NX (integer) 0
Une clé par jour, que INCR crée à 1 la première fois. L'option NX (depuis Redis 7.0) ne pose la durée de vie que si la clé n'en a pas encore : le deuxième EXPIRE renvoie 0 et ne repousse pas l'échéance. INCR, lui, ne touche pas à la durée de vie. 172 800 s font 2 jours.
2Dans le classement de la scène 3, ajoute 10 points à luca, puis affiche les trois premiers avec leur score.Voir le corrigé
> ZADD classement 120 luca 95 mia 140 noah (integer) 3 > ZINCRBY classement 10 luca "130" > ZRANGE classement 0 2 REV WITHSCORES 1) "noah" 2) "140" 3) "luca" 4) "130" 5) "mia" 6) "95"
ZINCRBY renvoie le nouveau score. Avec REV, ZRANGE lit du plus grand score au plus petit ; WITHSCORES ajoute chaque score après son membre.
3Pourquoi faut-il écrire en base avant de supprimer la clé du cache, et pas l'inverse ?Voir le corrigé
Si l'on supprime d'abord la clé, une lecture peut arriver avant la mise à jour de la base : elle ne trouve rien en cache, relit l'ancienne valeur dans la base et la remet en cache. La valeur périmée y reste alors jusqu'à l'expiration. En écrivant d'abord dans la base, la relecture qui suit la suppression trouve la nouvelle valeur.6 Même ainsi, le cache-aside ne garantit pas une cohérence parfaite : un autre programme peut modifier la base sans toucher au cache.
4La liste actus doit garder les 100 actualités les plus récentes. Quelles commandes à chaque nouvelle actualité ?Voir le corrigé
LPUSH actus a101 LTRIM actus 0 99
LPUSH place la nouvelle actualité en tête et renvoie la longueur de la liste ; LTRIM ne garde que les indices 0 à 99, donc les 100 plus récentes, et renvoie OK. C'est une liste « plafonnée ».
La fiche en huit lignes
Teste-toi
Sources
- Microsoft, Azure Architecture Center, Comprendre les modèles de données (bases relationnelles et non relationnelles, dites NoSQL ; modèles document, colonnes larges, clé-valeur et graphe avec leurs usages ; persistance polyglotte ; choix d'un modèle selon le besoin). learn.microsoft.com
- Redis, Redis FAQ (base en mémoire mais persistante sur disque, jeu de données limité par la mémoire, environ 85 Mo pour un million de petites paires clé-chaîne, erreurs en écriture quand maxmemory est atteint, opérations atomiques sur les types, origine du nom). redis.io
- Redis, documentation de développement : Redis data types et les pages des chaînes, hashes, listes, ensembles et ensembles triés ; référence des commandes SET, DEL, EXISTS, INCR, EXPIRE, TTL, KEYS, SCAN, ZRANGE et ZREVRANGE. redis.io
- Redis, Redis persistence (RDB, AOF, aucune persistance, combinaison ; avantages et inconvénients ; fsync chaque seconde par défaut ; recommandations). redis.io
- Redis, Key eviction (directive maxmemory, 0 par défaut sur 64 bits, politiques d'éviction, allkeys-lru comme bon choix par défaut, LRU approché par échantillonnage). redis.io
- Microsoft, Azure Architecture Center, Le modèle Cache-Aside (lecture à la demande, invalidation après écriture dans le magasin de données, durée de vie, cohérence, ordre des opérations). learn.microsoft.com
Fiche écrite et sourcée en octobre 2026. Les commandes ont été exécutées sur Redis 8.2.3 et le code Java avec Jedis 8.0.1 ; les réponses affichées et les mesures (compteur partagé, mémoire pleine, KEYS sur un million de clés) viennent de ces essais.