Object cache con Redis: quando serve davvero a WordPress (e quando no)

La differenza tra cache di pagina e object cache, le pagine in cui Redis può dimezzare il tempo di risposta, i casi in cui non serve e come configurarlo e misurarne l’effetto.

Una serie di chiavi memorizzate in cache, come alloptions e post, collegate da un fulmine a un database

Tra i consigli di ottimizzazione per WordPress, «installa Redis» è diventato quasi un riflesso condizionato. Lo suggeriscono i plugin di prestazioni, lo propongono gli hosting come funzione aggiuntiva, lo raccomanda persino lo strumento Salute del sito di WordPress. Eppure, su molti siti, attivare Redis non produce alcun miglioramento visibile, mentre su altri cambia radicalmente la velocità delle pagine più importanti. La differenza non dipende da Redis, ma da quale problema si sta cercando di risolvere.

In questo articolo vediamo che cos’è l’object cache di WordPress, in cosa si distingue dalla cache di pagina, quando una cache persistente come Redis fa davvero la differenza, quando è inutile e come configurarla e misurarla correttamente.

Cache di pagina e object cache: due cose diverse

La cache di pagina, di cui abbiamo parlato nell’articolo su TTFB e cache di pagina, memorizza l’HTML completo di una pagina e lo restituisce alle richieste successive senza eseguire WordPress. È lo strumento più efficace per le prestazioni: una pagina servita dalla cache arriva in poche decine di millisecondi, qualunque sia la complessità del sito.

L’object cache lavora a un livello diverso. Non memorizza pagine, ma i singoli dati che WordPress usa per costruirle: le opzioni del sito, gli articoli, i metadati, le tassonomie, i risultati di alcune query, i transient dei plugin. Quando WordPress deve generare una pagina, invece di chiedere ogni volta questi dati al database, li cerca prima nella cache. Non evita l’esecuzione di WordPress, ma la rende più rapida.

Schema dei livelli di cache: una richiesta servita dalla cache di pagina riceve subito l’HTML pronto; se la cache di pagina non ha la pagina, WordPress la genera cercando i dati prima nell’object cache Redis in memoria e solo se mancano nel database MySQL o MariaDB. A lato, le pagine senza cache di pagina: utenti collegati, carrello e checkout, area riservata, ricerca interna, REST API e AJAX, bacheca di amministrazione, cron
La cache di pagina evita di eseguire WordPress; l’object cache lo accelera quando deve essere eseguito.

L’object cache di WordPress, con e senza Redis

WordPress ha un’object cache integrata da sempre: tutte le funzioni del core usano wp_cache_get() e wp_cache_set() per memorizzare i dati già letti. Per impostazione predefinita, però, questa cache vive solo nella memoria della singola richiesta: evita di leggere due volte la stessa opzione durante la generazione di una pagina, ma alla richiesta successiva riparte da zero. Ogni pagina generata deve rileggere tutto dal database.

Una cache persistente cambia questo comportamento. Con un file chiamato object-cache.php nella cartella wp-content, un cosiddetto drop-in, WordPress sostituisce la propria implementazione con una che salva i dati in un sistema esterno, come Redis o Memcached, dove restano disponibili tra una richiesta e l’altra. Il drop-in viene installato in genere da un plugin, come Redis Object Cache o soluzioni commerciali più avanzate, oppure direttamente dall’hosting. Redis è un archivio di dati in memoria, estremamente veloce: leggere un valore richiede frazioni di millisecondo, contro i millisecondi di una query al database.

Con una cache persistente attiva cambiano anche i transient, i dati temporanei che molti plugin usano per memorizzare risultati costosi: invece di essere salvati nella tabella delle opzioni del database, vengono salvati nella cache. E dalla versione 6.1 WordPress memorizza nella cache anche i risultati di molte query sui contenuti, un ulteriore vantaggio quando la cache è persistente.

Quando serve davvero

La domanda chiave è: quante pagine del sito vengono generate da WordPress invece di essere servite dalla cache di pagina? Se la risposta è «quasi nessuna», Redis ha poco da offrire. Se la risposta è «molte», può fare una grande differenza. I casi tipici in cui la cache di pagina non può intervenire sono:

  • Negozi online. Carrello, checkout, area cliente e, per molte configurazioni, tutte le pagine visitate da chi ha un prodotto nel carrello, come spiegato nell’articolo sulle Core Web Vitals per WooCommerce.
  • Siti con utenti collegati. Aree riservate, siti di membership, piattaforme di formazione, forum e community, dove le pagine sono personalizzate.
  • Richieste dinamiche. Ricerca interna, filtri, chiamate alla REST API e AJAX, che raramente vengono memorizzate.
  • Amministrazione. La bacheca e l’editor, dove redattori e gestori lavorano ogni giorno.
  • Cache fredda. La prima richiesta dopo uno svuotamento della cache di pagina, o le pagine poco visitate che non sono mai in cache.

In tutti questi casi, ogni richiesta esegue WordPress per intero, e il numero di query al database può essere di decine o centinaia. Ridurle con una cache in memoria abbassa il tempo di generazione, e quindi il TTFB, spesso della metà o più, e allo stesso tempo riduce il carico sul database, che è frequentemente il collo di bottiglia dei siti con molto traffico dinamico.

Confronto illustrativo del TTFB con e senza Redis: una pagina servita dalla cache di pagina resta a 40 millisecondi in entrambi i casi; il carrello WooCommerce passa da 820 a 390 millisecondi, meno 52 per cento; la bacheca di amministrazione da 760 a 350 millisecondi, meno 54 per cento
Sulle pagine già in cache Redis non cambia nulla; su quelle dinamiche può dimezzare il tempo di risposta.

Quando non serve

Per un sito vetrina o un blog con poche pagine, visitato quasi solo da utenti anonimi e servito interamente dalla cache di pagina, Redis non porta benefici misurabili ai visitatori: le loro pagine non passano per WordPress. Può rendere un po’ più rapida la bacheca per chi scrive, ma non è un intervento sulle Core Web Vitals. In questi casi la priorità è assicurarsi che la cache di pagina funzioni bene e copra tutte le pagine.

Ci sono anche casi in cui una cache persistente può peggiorare le cose. Se Redis si trova su un altro server con una latenza di rete significativa, centinaia di letture per pagina possono costare più delle query che sostituiscono. Se la memoria assegnata è insufficiente, i dati vengono espulsi continuamente e la percentuale di letture riuscite crolla. E alcuni plugin memorizzano in cache valori enormi, come intere liste di prodotti o risposte di API esterne, che rallentano ogni lettura e saturano la memoria.

Configurazione: i dettagli che contano

Una configurazione corretta fa la differenza tra un Redis che accelera il sito e uno che occupa solo memoria. Alcuni punti meritano attenzione.

  • Memoria e politica di espulsione. Va impostato un limite di memoria adeguato con maxmemory e una politica di espulsione come allkeys-lru, che rimuove i dati usati meno di recente quando la memoria è piena. Senza limite, Redis può crescere fino a mettere in difficoltà il server.
  • Connessione locale. Se Redis è sullo stesso server di PHP, un socket Unix riduce ulteriormente la latenza rispetto a una connessione TCP.
  • Persistenza su disco. Per una cache non è necessario salvare i dati su disco: se Redis si riavvia, la cache si ripopola da sola. Disattivare la persistenza evita scritture inutili.
  • Separazione tra siti. Se più siti condividono la stessa istanza, ciascuno deve avere un prefisso delle chiavi diverso, per evitare che i dati si mescolino.
  • Serializzazione e compressione. Le soluzioni più avanzate permettono serializzatori più efficienti e la compressione dei valori, utili quando i dati memorizzati sono molti o grandi.
# redis.conf: impostazioni tipiche per una cache di WordPress
maxmemory 256mb
maxmemory-policy allkeys-lru
save ""
appendonly no
unixsocket /var/run/redis/redis.sock
unixsocketperm 770
// wp-config.php: connessione tramite socket e prefisso dedicato
define( 'WP_REDIS_SCHEME', 'unix' );
define( 'WP_REDIS_PATH', '/var/run/redis/redis.sock' );
define( 'WP_REDIS_PREFIX', 'negozio_' );
define( 'WP_REDIS_MAXTTL', 86400 );

I nomi esatti delle costanti dipendono dal plugin usato; quelli dell’esempio sono tipici del plugin Redis Object Cache. Su molti hosting gestiti Redis è già configurato, e basta attivarlo dal pannello.

Il problema delle opzioni caricate automaticamente

Un caso particolare merita una menzione. WordPress carica a ogni richiesta, in un colpo solo, tutte le opzioni contrassegnate come caricate automaticamente, e con una cache persistente le memorizza come un unico valore. Molti plugin, anche dopo essere stati disinstallati, lasciano in questa lista opzioni di grandi dimensioni. Il risultato è un valore di diversi megabyte letto e deserializzato a ogni pagina generata, con o senza Redis. Controllare la dimensione delle opzioni caricate automaticamente e ripulire quelle inutili è un intervento complementare che spesso vale quanto l’installazione della cache stessa; lo strumento Salute del sito segnala quando questa dimensione è eccessiva.

Redis o Memcached?

Memcached è l’altra soluzione storica per l’object cache di WordPress, e per l’uso tipico come cache le prestazioni dei due sistemi sono paragonabili. Redis si è imposto come scelta predefinita per alcune caratteristiche pratiche: supporta strutture di dati più ricche, permette di svuotare selettivamente gruppi di chiavi, offre strumenti di diagnostica più completi ed è supportato da un ecosistema di plugin più ampio. Se l’hosting offre già Memcached ben configurato, non c’è motivo di cambiare; se si parte da zero, Redis è in genere la scelta più semplice da gestire.

Invalidazione e svuotamento

Una cache è utile solo se i dati che contiene sono corretti. Il core di WordPress invalida automaticamente le voci relative a un articolo, a un termine o a un’opzione quando vengono modificati, e i plugin scritti bene fanno lo stesso con i propri dati. I problemi nascono con codice che scrive direttamente nel database, aggirando le funzioni di WordPress, per esempio con importazioni massive o script personalizzati: la cache continua a restituire i valori vecchi finché non scadono o non viene svuotata.

Per questo conviene includere lo svuotamento dell’object cache nelle procedure di rilascio e dopo ogni operazione diretta sul database, e diffidare delle soluzioni che la svuotano troppo spesso. Un plugin che esegue uno svuotamento completo a ogni salvataggio di un contenuto annulla gran parte dei benefici: il rapporto di letture riuscite crolla proprio nei momenti di maggiore attività.

Un esempio tipico

Consideriamo un negozio con circa tremila prodotti, una cache di pagina efficiente per i visitatori anonimi e un traffico concentrato nelle ore serali. Le pagine del catalogo rispondono in 50 millisecondi; il carrello e il checkout, invece, impiegano oltre 800 millisecondi, con più di 200 query per pagina, e durante le promozioni il database arriva al limite. Con Redis locale, 256 megabyte di memoria e una pulizia delle opzioni caricate automaticamente, le query per pagina scendono sotto le 40, il TTFB di carrello e checkout si dimezza e il carico del database nei picchi si riduce drasticamente. Il catalogo resta a 50 millisecondi, come prima: lì lavorava già la cache di pagina.

Come misurare l’effetto

Prima di attivare Redis conviene misurare il punto di partenza sulle pagine che non passano per la cache di pagina: TTFB del carrello, dell’area riservata, della ricerca, della bacheca. Il plugin Query Monitor mostra per ogni pagina il numero di query al database, il loro tempo totale e, con una cache persistente attiva, la percentuale di letture riuscite dalla cache. Sul lato di Redis, il comando INFO stats riporta il totale di letture riuscite e fallite, da cui si ricava il rapporto complessivo.

Dopo l’attivazione, i segnali di una configurazione efficace sono chiari: le query per pagina scendono sensibilmente, il rapporto di letture riuscite supera stabilmente il 90%, il TTFB delle pagine dinamiche si riduce. Se le query non diminuiscono, il drop-in potrebbe non essere attivo; se il rapporto di letture riuscite è basso, la memoria è probabilmente insufficiente o qualche plugin svuota la cache troppo spesso.

In sintesi

L’object cache persistente con Redis non sostituisce la cache di pagina e non accelera le pagine che la cache di pagina già serve. Il suo campo è tutto ciò che WordPress deve generare a ogni richiesta: carrelli e checkout, aree riservate, utenti collegati, ricerche, API e amministrazione. Lì può dimezzare il tempo di risposta e alleggerire il database, con effetti diretti sul TTFB e quindi sull’LCP delle pagine più importanti per il business.

Per un sito vetrina interamente in cache è un intervento di scarsa utilità; per un negozio online o un sito con molti utenti collegati è spesso uno dei più efficaci. La differenza la fanno la configurazione, con memoria adeguata, connessione locale e prefissi separati, e la misura, prima e dopo, sulle pagine giuste.

IL PROSSIMO PASSO

Partiamo
dal tuo sito.

Raccontaci il progetto, i problemi che riscontri e i tuoi obiettivi. Definiremo insieme un intervento su misura.

Richiedi un’analisi

Ti porta al modulo della pagina Contatti.

MANAGED SERVER S.R.L.info@corewebvitals.it +39 02 5656 9681

Lunedì – venerdì · 09:30 – 19:30

Preferisci un modulo online?
Contattaci su managedserver.it ↗