Retour au cours

data / redis

Hashes : objets structurés

Leçon 31 exercice

Explication

Ce que vous allez apprendre

  • Modéliser un objet (comme un utilisateur) avec un hash plutôt qu'avec plusieurs clés séparées
  • Lire un ou plusieurs champs d'un hash sans jamais rapatrier l'objet entier inutilement
  • Modifier atomiquement un champ numérique interne à un objet avec HINCRBY
  • Comprendre le gain d'overhead mémoire d'un hash face à des clés individuelles multiples
  • Parcourir un très gros hash sans bloquer le serveur, avec HSCAN

Dans quel contexte ?

Un profil utilisateur comporte plusieurs champs — nom, email, âge, solde de points de fidélité — qui doivent être lus et modifiés indépendamment les uns des autres, sans jamais avoir besoin de sérialiser/désérialiser un objet JSON complet pour changer un seul champ. C'est exactement la situation où un hash Redis surpasse une simple string contenant du JSON.

D'abord, une question naturelle : pourquoi pas plusieurs clés séparées ?

Rien n'empêche techniquement de créer user:1000:name, user:1000:email, user:1000:age comme trois clés indépendantes. Mais chaque clé Redis porte un coût mémoire fixe de métadonnées internes, indépendant de la taille de sa valeur : multiplier les clés pour un seul objet logique gaspille de la mémoire, en plus de rendre les opérations sur l'objet entier plus complexes à coordonner.

Une fois ce problème identifié, le hash apparaît comme la structure naturelle

Un hash regroupe tous les champs d'un objet sous une seule clé Redis (user:1000), un peu comme une mini-table sans jointures. HGETALL user:1000 récupère l'objet complet en un seul aller-retour réseau, et HGET user:1000 name ne lit qu'un champ précis sans jamais toucher aux autres.

ApprocheNombre de clés Redis pour un utilisateurOverhead mémoireRécupérer l'objet complet
Clés séparées (user:1000:name, ...)Une par champÉlevé (métadonnées par clé)Plusieurs GET ou un MGET
Hash (user:1000)Une seuleFaibleUn seul HGETALL
String JSON (user:1000 = '{"name":...}')Une seuleFaibleUn seul GET, mais sérialisation complète à chaque modif

Ensuite, un avantage du hash face à une string JSON mérite d'être souligné

Modifier un seul champ d'un objet stocké en JSON dans une string classique oblige à lire toute la valeur, la désérialiser côté application, modifier le champ, resérialiser, puis réécrire toute la valeur. Un hash évite complètement cette étape : HSET user:1000 age 31 modifie directement le champ voulu, côté serveur, sans jamais transporter le reste de l'objet sur le réseau.

Prérequis

Cette leçon suppose que tu es à l'aise avec SET/GET et les opérations atomiques comme INCR (leçon précédente) : HINCRBY en est l'équivalent appliqué à un champ interne d'un hash.

Il reste un cas particulier utile pour l'initialisation conditionnelle

HSETNX user:1000 role "user" ne crée le champ QUE s'il n'existe pas déjà — utile pour attribuer une valeur par défaut sans jamais écraser une valeur déjà présente, par exemple lors d'une migration de données ou d'un rattrapage de champs manquants.

Piège fréquent

Utiliser HGETALL sur un hash contenant des dizaines de milliers de champs peut bloquer le serveur le temps de renvoyer l'intégralité du contenu en une seule réponse. Exactement comme KEYS pour l'espace de clés global, HSCAN permet de parcourir un hash volumineux par petits morceaux, sans jamais bloquer les autres clients.

Bonne pratique

Réserve les hashes aux objets réellement "plats" (sans listes ni objets imbriqués à l'intérieur des champs) : dès qu'un champ doit lui-même contenir une structure complexe, une string JSON ou une combinaison de plusieurs structures Redis devient souvent plus appropriée.

Maintenant que tu sais modéliser un objet structuré, la prochaine leçon aborde les listes, adaptées cette fois à des collections ordonnées comme des files d'attente ou des piles de tâches.

Commandes & code

Hashes : objets structurés

Un hash Redis stocke un objet sous forme de champs/valeurs, comme une mini table sans les jointures.

bash
# Créer et lire un hash représentant un utilisateur
HSET user:1000 name "Bob" email "bob@example.com" age 30
HGET user:1000 name                    # -> "Bob"
HMGET user:1000 name email             # -> ["Bob", "bob@example.com"]
HGETALL user:1000                      # -> tous les champs/valeurs

# Modification atomique d'un champ numérique
HINCRBY user:1000 age 1                # age passe à 31
HINCRBYFLOAT user:1000 solde 19.99

# Existence et suppression d'un champ
HEXISTS user:1000 email                # -> 1
HDEL user:1000 age                     # supprime uniquement le champ "age"

# Lister champs / valeurs / nombre de champs
HKEYS user:1000
HVALS user:1000
HLEN user:1000

# Créer seulement si le champ n'existe pas encore
HSETNX user:1000 role "user"

# Itération non bloquante sur un très gros hash
HSCAN user:1000 0 COUNT 50
python
# Pourquoi un hash plutôt que plusieurs clés "user:1000:name", "user:1000:email" ?
# - Une seule clé Redis -> moins d'overhead mémoire (métadonnées par clé)
# - HGETALL récupère tout l'objet en un aller-retour réseau
# - Convient parfaitement à un objet "plat" (pas de listes/objets imbriqués)

Résumé

  • Un hash modélise un objet avec des champs nommés, sans le coût d'une clé Redis par champ.
  • HINCRBY/HINCRBYFLOAT permettent des mises à jour atomiques de compteurs internes à l'objet.
  • Pour des hashes volumineux, HSCAN évite de bloquer le serveur (contrairement à HGETALL sur un hash énorme).

Exercices pratiques

1 disponible
1

Mission : migrer 50000 profils utilisateurs sans casser les clés existantes

Objectif : Modéliser des profils utilisateurs avec des hashes, ajouter un champ par défaut sans écraser les profils déjà migrés, et parcourir un hash volumineux sans bloquer le serveur.

Contexte

L'ancien système stockait chaque champ utilisateur (user:1000:name, user:1000:email, ...) comme une clé séparée, pour 50000 utilisateurs. Une migration vers des hashes est en cours, mais un script parallèle a déjà positionné le champ role sur certains comptes. Tu dois compléter la migration sans jamais écraser ce que ce script a déjà fait, et savoir explorer un très gros hash sans bloquer le serveur.

Résoudre l’exercice →