⚡️ Redis & Memcached – Cache, Sessions & Performance
Guide opérationnel complet : Installation, comparaison et intégration Django (Cache & Sessions).
Concept : Cache In-Memory
Pourquoi utiliser un cache ? RAM vs Disque.
Cache PerformanceRedis : Vue d'ensemble
Serveur de structures de données, persistance.
Redis Data StructuresInstallation Redis (Linux)
apt/dnf, redis-server, redis-cli, systemd.
Configuration Redis
redis.conf, bind, requirepass (sécurité).
Memcached : Vue d'ensemble
Cache K/V simple, multi-thread, volatil.
Memcached K/V StoreInstallation Memcached
apt/dnf, /etc/memcached.conf, -m (RAM).
Comparaison : Redis vs Memcached
Tableau comparatif (Types, Persistance, Scalabilité).
Comparaison Use CaseCLI : redis-cli
PING, SET, GET, KEYS, FLUSHALL.
Types de Données Redis
Strings, Lists (LPUSH), Hashes (HSET), Sets (SADD).
Hashes ListsCLI : Memcached (telnet)
stats, get, set, flush_all.
Libs Python/Django
pip install django-redis, pymemcache.
Django : Configurer CACHES
settings.py, django_redis.cache.RedisCache.
Django : Utilisation (API)
cache.set(), cache.get(), @cache_page.
Django : Sessions en Cache
SESSION_ENGINE (cached_db, cache).
Monitoring Redis
MONITOR, INFO (memory, clients, stats).
Monitoring Memcached
stats (hits, misses), stats slabs.
Cheat-sheet Redis/Memcached
Commandes CLI fréquentes.
cheat CLIPourquoi utiliser un cache ?
Problème : Les bases de données (PostgreSQL, MariaDB) sont lentes. Elles lisent sur le **Disque Dur (SSD/HDD)**. Une requête Django complexe (avec JOIN) peut prendre 200ms.
Solution : Un cache In-Memory (Redis, Memcached) stocke les résultats de ces requêtes dans la **RAM** (mémoire vive).
RAM vs Disque
Accéder à la RAM est des milliers de fois plus rapide qu'accéder au disque.
- Accès BDD (Disque) : 50ms - 500ms
- Accès Cache (RAM) : < 1ms
Flux d'une Requête Django (avec Cache)
[Utilisateur] -> [Nginx] -> [Gunicorn/Django]
|
| 1. Demande "cache.get('cle_rapport_ventes')"
|
▼
[Cache (Redis)]
|
| 2a. (Trouvé) -> Renvoie le JSON (1ms)
|
| 2b. (Non trouvé / Expiré)
|
▼
[BDD (Postgres)]
|
| 3. (Requête SQL lente, 300ms)
|
▼
[Django]
|
| 4. (Sauvegarde "cache.set('cle_rapport_ventes', ...)")
|
▼
[Cache (Redis)]
|
▼
[Utilisateur] (Réponse)
Le "Couteau Suisse" In-Memory
Redis (REmote DIctionary Server) est un **serveur de structures de données In-Memory**.
Ce n'est pas juste un cache K/V. C'est une "boîte à outils" pour les données :
- Structures de données : Supporte Strings, Lists (files d'attente), Hashes (objets), Sets, Sorted Sets.
- Persistance : Peut (optionnellement) sauvegarder ses données sur le disque (Snapshots ou AOF) pour survivre à un redémarrage.
- Mono-thread (Single-threaded) : Gère les opérations de manière atomique et très rapide (similaire à Node.js).
Cas d'usage (au-delà du cache)
Grâce à ses structures de données, Redis est utilisé pour :
- Cache : (Cache Django/Laravel).
- Sessions : (Stockage des sessions Django).
- Broker (File d'attente) : (Broker pour Celery).
- Pub/Sub : (Système de messagerie temps-réel).
- Rate Limiting : (Compteurs).
Ubuntu / Debian
sudo apt update sudo apt install -y redis-server
RHEL / Rocky / AlmaLinux
# (Le dépôt EPEL est souvent requis) sudo dnf install -y epel-release sudo dnf install -y redis
Post-Installation (systemd)
# Activer au démarrage sudo systemctl enable --now redis-server # (ou 'redis' sur RHEL) # Vérifier le statut sudo systemctl status redis-server
redis-cli (Client)
L'outil CLI pour interagir avec Redis.
# Lancer le client redis-cli # Tester la connexion 127.0.0.1:6379> PING PONG 127.0.0.1:6379> exit
Par défaut, Redis écoute sur localhost:6379.
redis.conf) & SécuritéFichier de Configuration
Emplacement : /etc/redis/redis.conf
Attention : L'installation par défaut (apt) est sécurisée (bind 127.0.0.1) et ne permet que les connexions locales.
bind (Interface d'écoute)
# (Défaut) N'écoute que sur localhost bind 127.0.0.1 ::1 # (Pour autoriser le réseau local - ex: Django sur autre VM) # bind 192.168.1.100 127.0.0.1 # (DANGEREUX - N'écoute partout) # bind 0.0.0.0
protected-mode
# (Défaut) Si vous mettez 'bind 0.0.0.0' SANS mot de passe, # Redis refusera quand même les connexions externes. protected-mode yes
requirepass (Mot de passe)
(Recommandé en production) Définir un mot de passe.
# Décommenter et changer requirepass "votre_mot_de_passe_tres_solide"
maxmemory (Limite RAM)
(Crucial en prod) Limiter la RAM que Redis peut utiliser (ex: 2Go).
maxmemory 2gb
maxmemory-policy (Éviction)
Que faire si maxmemory est atteint ?
# (Recommandé pour le cache) # Supprime les clés les moins récemment utilisées (LRU) maxmemory-policy allkeys-lru
# Appliquer les changements sudo systemctl restart redis-server
Le Cache K/V "Pur"
Memcached est l'ancêtre des caches In-Memory. C'est un cache simple, rapide et volatil.
Il ne fait QU'UNE seule chose : stocker des paires Clé/Valeur (K/V) en RAM.
- K/V Simple : Les clés sont des strings, les valeurs sont des strings (ou objets sérialisés).
- Multi-thread : Très rapide, utilise tous les cœurs CPU (contrairement à Redis).
- Volatil (Non-persistant) : Si vous redémarrez Memcached, tout le cache est perdu. Il n'y a pas de persistance sur disque.
- Pas de structures de données : Pas de Lists, Hashes, etc.
Cas d'usage
Memcached est parfait pour le Caching Pur (et seulement ça) :
- Cache de résultats de BDD (Cache Django).
- Cache de sessions (Sessions Django).
- Cache de rendus HTML.
Si vous avez juste besoin d'un cache K/V simple et que la perte de cache au redémarrage n'est pas grave (le cache se "réchauffe"), Memcached est excellent.
Installation
Ubuntu / Debian
sudo apt update sudo apt install -y memcached sudo systemctl enable --now memcached
RHEL / Rocky / AlmaLinux
sudo dnf install -y memcached sudo systemctl enable --now memcached
Configuration (/etc/memcached.conf)
Le fichier de config (Debian) ou /etc/sysconfig/memcached (RHEL) permet de régler les options de démarrage.
# (Exemple: /etc/memcached.conf) # -m : Mémoire (RAM) à allouer (ex: 64MB) -m 64 # -p : Port (défaut 11211) -p 11211 # -l : Adresse d'écoute (127.0.0.1 = localhost) -l 127.0.0.1 # -c : Connexions max -c 1024
# Appliquer les changements sudo systemctl restart memcached
| Critère | Redis | Memcached |
|---|---|---|
| Modèle | Serveur de Structures de Données | Cache K/V Simple |
| Types de données | Strings, Lists, Hashes, Sets... | Strings (binaires) |
| Persistance | Oui (Snapshot RDB, Log AOF) | Non (100% volatil, RAM uniquement) |
| Architecture | Mono-thread (Single-threaded) | Multi-thread (très rapide sur multi-cœur) |
| Usage (Django Cache) | Excellent. (Requis pour certaines fonctionnalités). | Excellent (très rapide). |
| Usage (Django Sessions) | Recommandé (persistant). | Non recommandé (sessions perdues au reboot). |
| Usage (Broker Celery) | Oui (Broker & Result Backend). | Non. |
Conclusion (Pour Django)
Utilisez Redis.
Redis peut tout faire : Cache Django, Sessions Django, et Broker/Backend Celery. Il est plus flexible et la persistance (optionnelle) est un atout majeur.
redis-cli# Se connecter redis-cli # (Si mot de passe configuré) redis-cli -a "votre_mot_de_passe" # Test de connexion 127.0.0.1:6379> PING PONG # --- Strings (K/V) --- # (SET [clé] [valeur] EX [secondes]) 127.0.0.1:6379> SET user:1:name "Alice" EX 60 OK # Lire la clé 127.0.0.1:6379> GET user:1:name "Alice" # Voir le temps restant (Time To Live) 127.0.0.1:6379> TTL user:1:name (integer) 55 # Supprimer 127.0.0.1:6379> DEL user:1:name (integer) 1 # Lister les clés (DANGER en PROD) 127.0.0.1:6379> KEYS * 1) "user:1:name" # Vider TOUTE la BDD (DANGER) 127.0.0.1:6379> FLUSHALL OK
Hashes (Objets)
Idéal pour stocker un objet (ex: un utilisateur) sans le sérialiser (JSON).
# HSET [clé] [champ] [valeur] ... HSET user:100 nom "Bob" email "bob@mail.com" (integer) 2 # Lire un champ HGET user:100 nom "Bob" # Lire tout l'objet HGETALL user:100 1) "nom" 2) "Bob" 3) "email" 4) "bob@mail.com"
Lists (Files d'attente / Queues)
(Utilisé par Celery)
# Ajouter à droite (Push) RPUSH ma_queue "tache1" (integer) 1 RPUSH ma_queue "tache2" (integer) 2 # Lire/Retirer à gauche (Pop) LPOP ma_queue "tache1" # Voir la longueur LLEN ma_queue (integer) 1
telnet)Memcached n'a pas de CLI dédiée, on utilise telnet (ou nc).
$ sudo apt install telnet $ telnet localhost 11211 # --- Commandes (Syntaxe brute) --- # SET [clé] [flags] [expiration_sec] [nb_octets] set user:1:name 0 60 5 Alice STORED # GET [clé] get user:1:name VALUE user:1:name 0 5 Alice END # DELETE [clé] delete user:1:name DELETED # Vider TOUT le cache (DANGER) flush_all OK # Quitter quit
Activez votre venv Django et installez les connecteurs.
Pour Redis (Recommandé)
django-redis est le package de cache standard.
(venv) $ pip install django-redis
Pour Memcached
Vous avez le choix entre deux librairies (pymemcache est plus moderne).
(venv) $ pip install pymemcache # (ou) (venv) $ pip install pylibmc
CACHES (settings.py)settings.py (avec django-redis)
# (Assurez-vous que 'pip install django-redis' est fait)
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# URL de connexion (avec BDD /0)
"LOCATION": "redis://127.0.0.1:6379/0",
# (Si vous avez un mot de passe)
# "LOCATION": "redis://:mot_de_passe_solide@127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}settings.py (avec pymemcache)
# (Assurez-vous que 'pip install pymemcache' est fait)
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
# (IP:Port)
"LOCATION": "127.0.0.1:11211",
}
}(Alternative : pylibmc)
# (Backend C, plus rapide mais plus lourd à installer)
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
"LOCATION": "127.0.0.1:11211",
}
}API bas-niveau (cache.get / cache.set)
La méthode la plus flexible, utilisée dans vos views.py ou services pour mettre en cache des requêtes BDD lourdes.
from django.core.cache import cache
from django.shortcuts import render
from .models import Article
def articles_populaires(request):
# 1. Définir une clé de cache
CACHE_KEY = 'articles_populaires_list'
# 2. Essayer de lire le cache
articles_list = cache.get(CACHE_KEY)
# 3. Si le cache est vide (Miss)
if not articles_list:
print("Cache MISS - Génération de la requête...")
# (Requête BDD lente)
articles_list = Article.objects.order_by('-vues')[:10]
# 4. Mettre en cache (pour 15 minutes)
# cache.set(clé, valeur, timeout_secondes)
cache.set(CACHE_KEY, articles_list, 60 * 15)
else:
print("Cache HIT - Servi depuis Redis/Memcached")
return render(request, 'liste.html', {'articles': articles_list})Décorateur @cache_page
Met en cache la **réponse HTTP complète** (le HTML) d'une vue. Très simple.
from django.views.decorators.cache import cache_page
# Cache cette vue pendant 15 minutes (60 * 15)
@cache_page(60 * 15)
def ma_vue_tres_lourde(request):
# (Requêtes BDD, rendu de template...)
# ...
return render(request, 'ma_page.html', {...})Vider le cache
cache.delete('articles_populaires_list')
# (ou)
cache.clear() # (Vide tout le cache !)Problème : Sessions en BDD
Par défaut (SESSION_ENGINE = 'django.contrib.sessions.backends.db'), Django stocke les sessions dans la table django_session.
Cela signifie qu'à chaque requête (même statique) d'un utilisateur connecté, Django doit faire un SELECT et un UPDATE sur la BDD, ce qui est lent.
Solution : Sessions en Cache
On dit à Django de stocker les sessions dans le cache (Redis/Memcached) au lieu de la BDD.
SESSION_ENGINE
# settings.py # Option 1: "Cache-only" (Si vous perdez le cache, # les utilisateurs sont déconnectés. OK pour Memcached/Redis) SESSION_ENGINE = "django.contrib.sessions.backends.cache" # Option 2: "Cache + DB" (La plus robuste) # (Écrit dans le cache ET la BDD. Lit depuis le cache.) # (Recommandé si vous utilisez Redis) SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" # (Définit quel cache utiliser, si vous en avez plusieurs) SESSION_CACHE_ALIAS = "default"
redis-cli)MONITOR
Affiche **toutes** les commandes reçues par le serveur en temps réel. (Ne *jamais* lancer en production chargée).
$ redis-cli MONITOR OK 1667300000.123 [0 127.0.0.1:54321] "GET" "django:session:abc" 1667300000.124 [0 127.0.0.1:54321] "SET" "django:cache:key" ...
INFO (L'outil principal)
Affiche des centaines de statistiques.
$ redis-cli INFO # Memory used_memory_human:1.50M maxmemory_human:2.00G mem_fragmentation_ratio:1.1 # Stats total_connections_received:150 total_commands_processed:10500 keyspace_hits:8000 keyspace_misses:2500 # (Calcul du Cache Hit Ratio) # (hits / (hits + misses)) -> 8000 / 10500 = ~76%
stats)$ telnet localhost 11211 # Commande 'stats' stats STAT pid 1234 STAT uptime 3600 STAT cmd_get 50000 STAT cmd_set 10000 STAT get_hits 45000 STAT get_misses 5000 STAT bytes 15000000 STAT limit_maxbytes 67108864 # (64M) ... END
Statistiques Clés
cmd_get: Total des demandesGET.get_hits: Cache HIT (trouvé).get_misses: Cache MISS (non trouvé).- Cache Hit Ratio :
get_hits / cmd_get(Ici: 45000 / 50000 = 90%). limit_maxbytes: RAM allouée (-m).bytes: RAM actuellement utilisée.
Redis (redis-cli)
PING AUTH [password] # K/V SET mykey "value" SET mykey "value" EX 3600 # (Expire 1h) GET mykey DEL mykey INCR counter # (Atomique) DECR counter # Hashes HSET user:1 nom "Alice" age 30 HGET user:1 nom HGETALL user:1 # Keys KEYS * # (DANGER) SCAN 0 # (Mieux) TTL mykey # (Temps restant) EXPIRE mykey 60 # (Mettre 60s) FLUSHALL # (Vider) # Monitoring INFO MONITOR
Memcached (telnet localhost 11211)
# K/V # set [clé] [flags] [expire_sec] [octets] set mykey 0 3600 5 value STORED get mykey # VALUE mykey 0 5 # value # END delete mykey DELETED # Monitoring stats stats slabs stats items # Vider (DANGER) flush_all OK # Quitter quit
