Redis: il database che vive in memoria
Categoria: Database | Livello: Principiante–Intermedio
Cos'è Redis?
Redis (acronimo di REmote DIctionary Server) è un database in-memory open source, nato nel 2009 per mano di Salvatore Sanfilippo. A differenza dei database relazionali che scrivono i dati su disco, Redis li mantiene direttamente in RAM, raggiungendo velocità di accesso dell'ordine dei microsecondi.
Non è un semplice database chiave-valore: Redis supporta strutture dati evolute come liste, insiemi, hash e code con priorità, rendendolo uno strumento estremamente versatile.
> 💡 Curiosità: Redis è usato da Twitter, GitHub, Stack Overflow, Snapchat e da milioni di applicazioni in tutto il mondo come strato di caching e gestione delle sessioni.
Architettura: in-memory vs on-disk
Il punto distintivo di Redis è dove risiedono i dati durante l'esecuzione.
| Caratteristica | Database tradizionale (es. MySQL) | Redis |
|---|---|---|
| Dove vivono i dati | Disco (HDD/SSD) | RAM (memoria principale) |
| Velocità di accesso | Millisecondi | Microsecondi |
| Modello dei dati | Tabelle relazionali | Strutture dati (key-value++) |
| Persistenza | Sempre su disco | Opzionale (RDB o AOF) |
| Capacità tipica | Terabyte | Limitata dalla RAM disponibile |
| Query language | SQL | Comandi specifici per tipo |
| Uso principale | Dati applicativi permanenti | Cache, sessioni, code, pub/sub |
Il fatto di lavorare in RAM è sia il punto di forza che il limite principale: i dati sono fulminei da leggere e scrivere, ma la capacità è vincolata dalla memoria fisica del server.
Quando usare Redis
Redis non sostituisce un database relazionale, ma lo affianca per risolvere problemi specifici con efficienza.
✅ Redis è la scelta giusta quando:
- Hai bisogno di una cache per ridurre i tempi di risposta (es. query costose salvate temporaneamente)
- Vuoi gestire le sessioni utente in modo veloce e scalabile
- Devi implementare una coda di messaggi o un sistema di task in background
- Vuoi un sistema di rate limiting (es. massimo 100 richieste al minuto per utente)
- Hai bisogno di leaderboard in tempo reale (es. classifiche di un gioco)
- Vuoi implementare un sistema pub/sub per messaggi in tempo reale
- Hai dati con scadenza automatica (es. token temporanei, OTP, link di verifica)
❌ Redis non è adatto quando:
- I tuoi dati devono persistere in modo affidabile senza configurazioni aggiuntive
- Hai dataset di dimensioni superiori alla RAM disponibile
- Hai bisogno di query complesse con JOIN, aggregazioni e transazioni ACID complete
- Il tuo budget non consente server con tanta memoria RAM
Installazione e primi passi
Su Linux / Ubuntu
sudo apt update
sudo apt install redis-server
# Avvio del servizio
sudo systemctl start redis-server
sudo systemctl enable redis-server
# Verifica
redis-cli ping
# Output: PONG
Su Windows
Il modo più semplice è usare WSL2 (Windows Subsystem for Linux) oppure Docker:
docker run -d -p 6379:6379 --name redis redis:latest
Connessione alla shell interattiva
redis-cli
Il prompt cambia in 127.0.0.1:6379> — sei connesso al server Redis locale sulla porta predefinita 6379.
Il modello chiave-valore
La base di Redis è semplice: ogni dato è identificato da una chiave (stringa) e ha un valore associato. La chiave è univoca all'interno del database.
# Imposta un valore
SET nome "Mario Rossi"
# Leggi il valore
GET nome
# Output: "Mario Rossi"
# Cancella una chiave
DEL nome
# Verifica se una chiave esiste
EXISTS nome
# Output: 0 (non esiste) oppure 1 (esiste)
# Conta tutte le chiavi presenti
DBSIZE
Scadenza automatica (TTL)
Una delle funzionalità più potenti di Redis è la possibilità di impostare una scadenza su qualsiasi chiave:
# Imposta un valore con scadenza di 60 secondi
SET token:abc123 "utente_42" EX 60
# Controlla il tempo rimanente
TTL token:abc123
# Output: 58 (secondi rimanenti)
# Dopo la scadenza
TTL token:abc123
# Output: -2 (la chiave non esiste più)
Questo meccanismo è perfetto per token di sessione, OTP e dati temporanei.
Le strutture dati principali
Redis non è un semplice dizionario: supporta diversi tipi di valore, ognuno con i propri comandi ottimizzati.
1. String — il tipo base
Il tipo più semplice: una chiave associata a una stringa (o numero).
SET contatore 0
INCR contatore # → 1
INCR contatore # → 2
INCRBY contatore 5 # → 7
DECR contatore # → 6
INCR è atomico: anche con più client che operano contemporaneamente, non ci sono race condition. Perfetto per contatori di visite o like.
2. List — lista ordinata
Una lista di stringhe, accessibile da entrambi gli estremi come una deque.
RPUSH coda:lavori "job_1" # Aggiunge in coda
RPUSH coda:lavori "job_2"
RPUSH coda:lavori "job_3"
LRANGE coda:lavori 0 -1 # Legge tutta la lista
# Output: job_1, job_2, job_3
LPOP coda:lavori # Estrae dal fronte (FIFO)
# Output: "job_1"
Caso d'uso tipico: code di task per sistemi di elaborazione asincrona.
3. Hash — dizionario annidato
Un hash associa a una chiave un insieme di coppie campo-valore. Ideale per rappresentare oggetti.
HSET studente:42 nome "Giulia" cognome "Ferrari" classe "4A" media 8.2
HGET studente:42 nome # → "Giulia"
HGETALL studente:42 # → tutti i campi e valori
HINCRBY studente:42 media 0.3 # Incrementa il campo numerico
4. Set — insieme senza duplicati
Un insieme non ordinato di stringhe uniche.
SADD tag:articolo "database" "nosql" "redis" "cache"
SADD tag:articolo "redis" # Non aggiunge: già presente
SMEMBERS tag:articolo # Elenca tutti i tag
SISMEMBER tag:articolo "sql" # → 0 (non presente)
SCARD tag:articolo # → 4 (numero di elementi)
5. Sorted Set — classifica con punteggio
Come un Set, ma ogni elemento ha un punteggio numerico che determina l'ordinamento. Perfetto per le classifiche.
ZADD classifica 1500 "mario"
ZADD classifica 2300 "giulia"
ZADD classifica 1800 "luca"
# Top 3 in ordine decrescente
ZREVRANGE classifica 0 2 WITHSCORES
# Output: giulia 2300, luca 1800, mario 1500
# Aggiorna il punteggio
ZINCRBY classifica 200 "mario"
Redis in Python: esempio pratico
Installa la libreria client:
pip install redis
Esempio 1 — Cache di query costosa
import redis
import time
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
def get_dati_studente(id_studente):
cache_key = f"studente:{id_studente}"
# Controlla la cache
cached = r.get(cache_key)
if cached:
print("✅ Dati dalla cache")
return cached
# Simula una query lenta al database
print("⏳ Query al database...")
time.sleep(2)
dati = f"Studente {id_studente}: Giulia Ferrari, 4A, media 8.2"
# Salva in cache per 30 secondi
r.set(cache_key, dati, ex=30)
return dati
# Prima chiamata: query al database
print(get_dati_studente(42))
# Seconda chiamata: risposta immediata dalla cache
print(get_dati_studente(42))
Esempio 2 — Contatore di visite con rate limiting
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
def registra_visita(pagina: str, ip_utente: str) -> dict:
# Contatore globale di visite
visite_totali = r.incr(f"visite:{pagina}")
# Rate limiting: max 5 richieste al minuto per IP
rate_key = f"rate:{ip_utente}:{pagina}"
richieste = r.incr(rate_key)
if richieste == 1:
r.expire(rate_key, 60) # Scadenza di 60 secondi
bloccato = richieste > 5
return {
"visite_totali": visite_totali,
"richieste_ultimo_minuto": richieste,
"accesso_consentito": not bloccato
}
# Simulazione
for i in range(7):
risultato = registra_visita("home", "192.168.1.1")
stato = "✅" if risultato["accesso_consentito"] else "🚫 BLOCCATO"
print(f"Richiesta {i+1}: {stato} | "
f"Richieste nell'ultimo minuto: {risultato['richieste_ultimo_minuto']}")
Output:
Richiesta 1: ✅ | Richieste nell'ultimo minuto: 1
Richiesta 2: ✅ | Richieste nell'ultimo minuto: 2
Richiesta 3: ✅ | Richieste nell'ultimo minuto: 3
Richiesta 4: ✅ | Richieste nell'ultimo minuto: 4
Richiesta 5: ✅ | Richieste nell'ultimo minuto: 5
Richiesta 6: 🚫 BLOCCATO | Richieste nell'ultimo minuto: 6
Richiesta 7: 🚫 BLOCCATO | Richieste nell'ultimo minuto: 7
Persistenza: Redis non è solo volatile
Per default Redis salva i dati in RAM, ma offre due meccanismi di persistenza opzionali:
RDB (Redis Database Backup)
Crea snapshot periodici del database su disco. Veloce al riavvio, ma si possono perdere gli ultimi dati scritti prima dello snapshot.
# In redis.conf
save 900 1 # Salva se almeno 1 chiave è cambiata negli ultimi 900 secondi
save 300 10 # Salva se almeno 10 chiavi sono cambiate negli ultimi 300 secondi
AOF (Append Only File)
Registra ogni operazione di scrittura su un file di log. Più sicuro, ma più lento di RDB.
# In redis.conf
appendonly yes
appendfsync everysec # Sincronizza su disco ogni secondo
> 💡 Configurazione consigliata: Per la maggior parte dei casi d'uso, è sufficiente RDB. Per applicazioni critiche, si usano entrambi.
Redis vs SQLite: quando scegliere quale
| Scenario | SQLite | Redis |
|---|---|---|
| Dati strutturati e persistenti | ✅ | ❌ |
| Query complesse con relazioni | ✅ | ❌ |
| Cache temporanea ad alta velocità | ❌ | ✅ |
| Sessioni utente con scadenza | ❌ | ✅ |
| Contatori e statistiche in tempo reale | ❌ | ✅ |
| Classifiche e leaderboard | ❌ | ✅ |
| Progetto didattico semplice | ✅ | ✅ |
Non sono in competizione: spesso si usano insieme. SQLite (o MySQL) per i dati permanenti, Redis per accelerare gli accessi frequenti.
Esercizi proposti
Esercizio 1 — Base Apri redis-cli e crea le seguenti strutture: una stringa con il tuo nome e TTL di 10 secondi; una lista con i giorni della settimana; un hash che rappresenta un prodotto (nome, prezzo, disponibilità). Verifica ogni struttura con i comandi di lettura appropriati.
Esercizio 2 — Intermedio Scrivi un programma Python che simuli un sistema di voti per un sondaggio. Gli utenti (identificati da un ID) possono votare una delle tre opzioni (A, B, C). Usa un Sorted Set per tenere il punteggio in tempo reale e mostra la classifica aggiornata dopo ogni voto. Impedisci che lo stesso utente voti due volte usando un Set.
Esercizio 3 — Avanzato Implementa un sistema di cache con invalidazione: un programma che simula chiamate a un'API esterna (con un time.sleep per simulare la latenza). Le risposte vengono salvate in Redis con TTL di 30 secondi. Quando i dati vengono aggiornati, la cache deve essere invalidata esplicitamente. Misura e mostra il tempo di risposta con e senza cache.
Riepilogo
| Concetto | Punti chiave |
|---|---|
| Architettura | In-memory, accesso in microsecondi |
| Strutture dati | String, List, Hash, Set, Sorted Set |
| TTL | Scadenza automatica delle chiavi, ideale per dati temporanei |
| Persistenza | Opzionale: RDB (snapshot) o AOF (log continuo) |
| Quando usarlo | Cache, sessioni, code, rate limiting, leaderboard, pub/sub |
| Quando non usarlo | Dati strutturati complessi, dataset superiori alla RAM disponibile |
| Integrazione Python | Libreria redis (pip install redis) |
Risorse per approfondire
- 📖 Documentazione ufficiale Redis
- 🎮 Try Redis — tutorial interattivo nel browser
- 🐍 redis-py — client Python ufficiale
- 🛠️ RedisInsight — interfaccia grafica gratuita
Articolo pubblicato su filippobilardo.it — Tutti i diritti riservati