Redis è un datastore in-memory estremamente veloce, spesso usato come cache, broker per code (queue), store per sessioni e per operazioni atomiche (lock, contatori, rate limiting). Laravel lo integra in modo nativo: puoi usarlo tramite i driver di Cache, Queue, Session e tramite il client Redis per operazioni dirette.
Prerequisiti e scenari d’uso
- Cache: ridurre query e calcoli ripetuti, memorizzare risposte e risultati.
- Queue: eseguire job asincroni (email, export, elaborazioni) con un broker veloce.
- Sessioni: archiviare sessioni su un backend condiviso in ambienti multi-istanza.
- Lock distribuiti: coordinare processi concorrenti ed evitare race condition.
- Contatori e rate limiting: incrementi atomici, limiti di frequenza e metriche.
- Pub/Sub: messaggistica in tempo reale o notifiche interne (con i giusti caveat).
Installazione di Redis
In sviluppo, il modo più semplice è usare Docker. In produzione, spesso Redis viene gestito come servizio dedicato (VM o managed service). Ecco un esempio rapido con Docker:
docker run --name redis -p 6379:6379 -d redis:7-alpine
# (opzionale) vedere i log
docker logs -f redis
Se usi Docker Compose insieme a Laravel:
services:
app:
build: .
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- "6379:6379"
Client Redis in Laravel: Predis o PhpRedis
Laravel può usare:
- PhpRedis (estensione PHP): in genere più performante e consigliata in produzione.
- Predis (libreria PHP): semplice da installare via Composer quando non puoi usare estensioni.
Opzione A: PhpRedis (consigliata)
Installa l’estensione (il comando dipende dal sistema). Esempio tipico su Linux:
# esempio generico; i pacchetti cambiano tra distro
sudo apt-get update
sudo apt-get install -y php-redis
In .env imposta il client:
REDIS_CLIENT=phpredis
Opzione B: Predis
Installa via Composer e imposta il client:
composer require predis/predis
REDIS_CLIENT=predis
Configurazione in Laravel
Laravel legge la configurazione da config/database.php nella sezione redis. In genere è sufficiente impostare le variabili in .env:
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0
REDIS_CACHE_DB=1
Se Redis richiede autenticazione:
REDIS_PASSWORD=unaPasswordMoltoForte
Se usi Docker Compose e il servizio si chiama redis, imposta:
REDIS_HOST=redis
Prefissi e separazione dei database
Laravel usa spesso database Redis diversi per isolare i dati: uno per le operazioni generiche e uno per la cache. Puoi anche usare un prefix per evitare collisioni tra applicazioni che condividono lo stesso Redis.
REDIS_PREFIX=miaapp_database_
CACHE_PREFIX=miaapp_cache_
Nota: i prefissi reali dipendono dalla configurazione (cache e redis). In molte installazioni Laravel applica un prefisso automatico basato su APP_NAME se non lo imposti.
Usare Redis come cache
Imposta Redis come driver della cache:
CACHE_DRIVER=redis
Esempi tipici con la facade Cache:
<?php
use Illuminate\Support\Facades\Cache;
// memorizza per 10 minuti
Cache::put('users.count', 123, now()->addMinutes(10));
// recupera con default
$count = Cache::get('users.count', 0);
// remember: calcola e salva solo se mancante
$top = Cache::remember('users.top', now()->addMinutes(5), function () {
return \App\Models\User::query()
->orderByDesc('score')
->take(10)
->get();
});
// invalidazione
Cache::forget('users.count');
Tag (attenzione alla compatibilità)
I tag della cache sono supportati da Redis, ma la semantica cambia a seconda del driver; inoltre possono aumentare la complessità e la memoria usata. Se li usi, mantieni i tag piccoli e coerenti.
<?php
use Illuminate\Support\Facades\Cache;
Cache::tags(['users', 'leaderboard'])->put('top10', $top, now()->addMinutes(5));
$top10 = Cache::tags(['users', 'leaderboard'])->get('top10');
Cache::tags(['users'])->flush();
Cache di query e N+1
Redis non sostituisce la corretta progettazione del database. Prima riduci N+1 con eager loading e indici; poi usa cache per i risultati più richiesti e con invalidazione chiara (ad esempio su eventi di modello).
Usare Redis per le sessioni
In ambienti con più istanze (load balancer) salvare le sessioni su Redis evita “logout” casuali e problemi di sticky sessions. Imposta:
SESSION_DRIVER=redis
SESSION_CONNECTION=default
Per alcune applicazioni è utile definire un DB Redis dedicato alle sessioni (o un prefisso dedicato), specialmente se anche queue e cache condividono Redis.
Usare Redis per le code (Queue)
Redis è uno dei backend più comuni per la queue di Laravel. Imposta:
QUEUE_CONNECTION=redis
Configura code multiple e priorità in config/queue.php (di solito non serve modificare se usi il default). Esegui un worker:
php artisan queue:work
# oppure per ambienti di produzione con supervisore
php artisan queue:work --sleep=1 --tries=3 --timeout=120
Esempio di Job
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendReportEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public int $userId) {}
public function handle(): void
{
// invia email, genera report, ecc.
}
}
Dispatch:
<?php
use App\Jobs\SendReportEmail;
SendReportEmail::dispatch($user->id)->onQueue('emails');
Laravel Horizon
Se la tua queue usa Redis e vuoi un pannello di controllo con metriche e gestione di processi/queue, Horizon è spesso la scelta migliore. È particolarmente utile quando hai molte code e worker da coordinare.
Operazioni Redis dirette con la facade Redis
Oltre ai driver (Cache/Queue/Session), puoi usare Redis come datastore key-value e strutture dati (liste, set, hash). Laravel espone la facade Redis.
<?php
use Illuminate\Support\Facades\Redis;
// stringhe
Redis::set('status', 'ok');
$status = Redis::get('status');
// TTL (secondi)
Redis::setex('otp:123', 60, '987654');
// incrementi atomici
$views = Redis::incr('page:home:views');
// hash
Redis::hset('user:1', 'name', 'Ada');
Redis::hset('user:1', 'email', 'ada@example.com');
$user = Redis::hgetall('user:1');
Connessioni e database
Puoi specificare una connessione configurata in config/database.php:
<?php
use Illuminate\Support\Facades\Redis;
$redis = Redis::connection('cache'); // ad es. un DB/prefix dedicato
$redis->set('key', 'value');
Pipelining per prestazioni
Quando devi fare molte operazioni, riduci i round-trip usando pipeline:
<?php
use Illuminate\Support\Facades\Redis;
$results = Redis::pipeline(function ($pipe) {
for ($i = 1; $i <= 100; $i++) {
$pipe->incr("counter:{$i}");
}
});
// $results contiene gli output dei comandi
Transazioni e atomicità
Per operazioni che devono essere atomiche su più comandi, puoi usare transazioni (MULTI/EXEC). In alternativa, per logiche più complesse e sicure, valuta Lua script.
<?php
use Illuminate\Support\Facades\Redis;
$result = Redis::transaction(function ($tx) {
$tx->incr('cart:1:items');
$tx->expire('cart:1:items', 3600);
});
Lua script
Lua permette di eseguire logiche atomiche direttamente in Redis. Esempio: incrementa un contatore solo se non supera una soglia.
<?php
use Illuminate\Support\Facades\Redis;
$lua = <<<'LUA'
local current = redis.call('GET', KEYS[1])
if not current then current = 0 else current = tonumber(current) end
local next = current + tonumber(ARGV[1])
if next > tonumber(ARGV[2]) then
return -1
end
redis.call('SET', KEYS[1], next)
redis.call('EXPIRE', KEYS[1], tonumber(ARGV[3]))
return next
LUA;
$value = Redis::eval($lua, 1, 'quota:user:1', 1, 100, 3600);
if ($value === -1) {
// quota superata
}
Lock distribuiti con Cache::lock
Quando più processi possono eseguire la stessa operazione (es. generare un report, ricalcolare una cache), un lock evita concorrenza indesiderata. Laravel fornisce un’API semplice basata su Redis.
<?php
use Illuminate\Support\Facades\Cache;
$lock = Cache::lock('reports:daily', 60);
if ($lock->get()) {
try {
// sezione critica
} finally {
$lock->release();
}
} else {
// qualcun altro sta già lavorando
}
Se vuoi attendere il lock fino a un certo tempo:
<?php
use Illuminate\Support\Facades\Cache;
Cache::lock('reports:daily', 60)->block(10, function () {
// acquisito entro 10 secondi
});
Rate limiting con Redis
Laravel usa spesso cache store per il rate limiting. Con Redis ottieni incrementi atomici e TTL affidabili. Esempio: definire un limiter personalizzato in App\Providers\RouteServiceProvider (o dove configuri i limitatori).
<?php
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('api', function (Request $request) {
$key = $request->user()?->id ?: $request->ip();
return Limit::perMinute(60)->by($key);
});
Applicazione alle rotte:
<?php
use Illuminate\Support\Facades\Route;
Route::middleware(['throttle:api'])->group(function () {
// rotte API
});
Pub/Sub e notifiche interne
Redis Pub/Sub è utile per comunicazioni effimere (messaggi non persistiti). Per eventi critici, preferisci una queue o un sistema di messaggistica persistente. Un esempio minimale:
<?php
use Illuminate\Support\Facades\Redis;
// Publisher
Redis::publish('events', json_encode(['type' => 'order.created', 'id' => 123]));
// Subscriber (script dedicato, non dentro una request web)
Redis::subscribe(['events'], function ($message) {
// gestisci messaggio
});
Pattern utili: cache-aside, write-through e invalidazione
- Cache-aside (consigliato): l’app legge dalla cache, se manca legge dal DB e scrive in cache (es.
Cache::remember). - Write-through: l’app scrive su cache e DB insieme; richiede maggiore disciplina e gestione errori.
- Invalidazione: definisci regole chiare (su eventi di dominio o observer dei modelli) per cancellare chiavi correlate.
Esempio semplice di invalidazione su evento modello:
<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
class UserObserver
{
public function saved(User $user): void
{
Cache::forget("user:{$user->id}");
}
}
Testing: come gestire Redis nei test
Nei test conviene:
- Usare un database Redis separato (es.
REDIS_DB=15) e svuotarlo tra i test. - Mockare Cache/Redis quando stai testando logica pura e non integrazione.
- Per test d’integrazione, eseguire Redis in container nel CI.
Esempio: svuotare Redis a inizio suite (approccio semplice):
<?php
use Illuminate\Support\Facades\Redis;
beforeEach(function () {
Redis::flushdb();
});
Osservabilità e manutenzione
- Monitoraggio: controlla memoria, evictions, hit ratio, latenza, connessioni.
- Politiche di eviction: se Redis ha limite di memoria, scegli una policy adatta (es. LRU/LFU) in base al tipo di carico.
- Dimensionamento: tieni separati i carichi (cache vs queue) se crescono molto, anche con istanze Redis diverse.
- Persistenza: per cache pura spesso non serve persistere; per altri usi valuta RDB/AOF in base a requisiti e costi.
Sicurezza
- Non esporre Redis su internet: usa rete privata/VPC e firewall.
- Imposta password e, se disponibile, TLS (soprattutto su servizi managed o reti non fidate).
- Usa prefissi e DB separati per ridurre rischi di collisione e impatti tra componenti.
- Evita di salvare dati sensibili in chiaro; per sessioni valuta anche cifratura applicativa se necessario.
Checklist rapida
- Installa Redis (Docker/servizio) e rendilo raggiungibile dall’app.
- Scegli il client: phpredis se possibile, altrimenti predis.
- Configura
.env: host, porta, password, DB dedicati. - Imposta i driver:
CACHE_DRIVER=redis,QUEUE_CONNECTION=redis,SESSION_DRIVER=redisse serve. - Usa
Cache::remembere invalidazione chiara; usaCache::lockper sezioni critiche. - Ottimizza round-trip con pipeline e usa Lua per logiche atomiche complesse.
- Monitora metriche e imposta limiti/eviction policy coerenti con il tuo carico.
Con queste basi puoi integrare Redis in Laravel in modo affidabile e scalabile: parti dalla cache, poi sposta su Redis queue e sessioni quando l’architettura lo richiede, e usa operazioni dirette (lock, contatori, hash) quando servono atomicità e performance.