Ottimizzazione database WordPress per agenzie: guida pratica a performance e pulizia
Ottimizzare il database WordPress è l’operazione di manutenzione che più spesso viene trascurata dalle agenzie che gestiscono multipli siti client. Il database cresce silenziosamente: revisioni, transient scaduti, meta orfani, opzioni autoload gonfie. Nella nostra esperienza su 50+ siti gestiti, un database non ottimizzato può aggiungere 200-400ms al TTFB su ogni richiesta, rallentare i backup e causare timeout durante gli aggiornamenti. In questa guida mostrimpo come pulire e ottimizzare il database WordPress in modo sicuro, con script WP-CLI replicabili su tutti i clienti.
Se gestisci più siti, questo articolo si integra con la nostra guida completa alla gestione multi-sito WordPress per agenzie e con la guida alle performance WordPress e Core Web Vitals.
TL;DR — Cosa impari in questa guida
- Identificare e rimuovere i dati morti che gonfiano il database (revisioni, transient, meta orfani)
- Ottimizzare le tabelle InnoDB con OPTIMIZE TABLE e capire la differenza con MyISAM
- Ridurre le opzioni autoload che rallentano ogni page load
- Aggiungere indici custom alle tabelle più query per velocizzare le ricerche
- Script WP-CLI per automatizzare la pulizia su 50+ siti in batch
- Plugin per ottimizzazione database: quali usare e quali evitare
Perché il database WordPress si gonfia nel tempo
Il database WordPress immagazzina tutto: contenuti, metadati, opzioni, commenti, transient. Ogni volta che un utente salva un articolo, WordPress crea una revisione. Ogni volta che un plugin usa la Transients API senza impostare una scadenza, crea una riga permanente in wp_options. Quando elimini un post senza i ganci di cleanup corretti, i suoi meta restano in wp_postmeta come righe orfane.
Il risultato? Un sito con 3 anni di attività può avere un wp_posts con 50.000 revisioni inutilizzate e un wp_postmeta dove il 40-60% dello spazio è frammentato. Su un sito di e-commerce con WooCommerce, il problema è ancora più grave: ogni ordine genera righe in 5 tabelle diverse.
Tabella: dove si accumulano i dati inutili
| Tabella | Cosa si accumula | Impatto tipico |
|---|---|---|
wp_posts |
Revisioni, draft abbandonati, post programmati scaduti | 50.000+ righe su siti attivi da 3+ anni |
wp_postmeta |
Meta orfani (post_id non esistente), campi serializzati gonfi | Frammentazione 40-60% su siti con page builder |
wp_options |
Transient scaduti non rimossi, autoload eccessivo | 2-5MB deserializzati ad ogni page load |
wp_comments |
Spam, pingback, trackback | 10.000+ righe su blog senza anti-spam |
wp_termmeta |
Meta di termini eliminati | Raramente problematico, ma cresce con plugin SEO |
Pulizia database: operazioni sicure passo per passo
1. Rimuovere le revisioni dei post
Le revisioni sono la causa numero uno del gonfiore del database. Un post modificato 200 volte genera 200 righe in wp_posts e altrettante in wp_postmeta. Per rimuoverle con WP-CLI:
# Rimuovere tutte le revisioni
wp post delete $(wp post list --post_type=revision --format=ids) --force
# Oppure con query SQL diretta
wp db query "DELETE FROM wp_posts WHERE post_type = 'revision';"
# Limitare il numero di revisioni future in wp-config.php
define('WP_POST_REVISIONS', 5);
Il parametro WP_POST_REVISIONS accetta un intero (numero massimo di revisioni da conservare) o false per disabilitarle completamente. Nella nostra esperienza, 5 revisioni sono sufficienti per la maggior parte dei clienti: abbastanza per un rollback di sicurezza, ma non abbastanza da gonfiare il database.
2. Pulire i transient scaduti
I transient sono dati temporanei memorizzati in wp_options con un timeout. In teoria, WordPress li elimina quando scadono. In pratica, il garbage collection dipende da WP-Cron, che spesso non viene eseguito regolarmente su siti con poco traffico o con WP-Cron disabilitato. Il risultato: migliaia di transient scaduti che restano nel database.
# Rimuovere tutti i transient scaduti
wp transient delete --expired
# Con query SQL diretta (più aggressivo)
wp db query "DELETE FROM wp_options WHERE option_name LIKE '_transient_timeout_%' AND option_value < UNIX_TIMESTAMP();"
wp db query "DELETE FROM wp_options WHERE option_name LIKE '_transient_%' AND option_name NOT LIKE '_transient_timeout_%' AND option_name NOT IN (SELECT REPLACE(option_name, '_transient_timeout_', '_transient_') FROM wp_options WHERE option_name LIKE '_transient_timeout_%');"
Per approfondire il funzionamento dei transient, leggi la nostra guida alla REST API WordPress dove spieghiamo come usarli per il caching delle risposte API.
3. Eliminare i meta orfani
I meta orfani sono righe in wp_postmeta il cui post_id non esiste più in wp_posts. Si creano quando un post viene eliminato senza che i plugin eseguano il cleanup dei metadati. Per identificarli e rimuoverli:
# Contare i meta orfani
wp db query "SELECT COUNT(*) FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;"
# Eliminarli
wp db query "DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;"
4. Rimuovere commenti spam e pingback
# Eliminare tutti i commenti spam
wp comment delete $(wp comment list --status=spam --format=ids) --force
# Eliminare pingback e trackback
wp db query "DELETE FROM wp_comments WHERE comment_type IN ('pingback', 'trackback');"
wp db query "DELETE FROM wp_commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM wp_comments);"
Ottimizzazione tabelle InnoDB: come e quando
Dopo aver rimosso i dati inutili, il passo successivo è ottimizzare le tabelle fisiche. WordPress usa InnoDB come storage engine di default dal 2018, ma è importante capire come funziona OPTIMIZE TABLE su InnoDB rispetto al vecchio MyISAM.
OPTIMIZE TABLE su InnoDB vs MyISAM
| Aspetto | MyISAM | InnoDB |
|---|---|---|
| Operazione | Deframmentazione nativa | ALTER TABLE … FORCE (ricrea la tabella) |
| Locking | Lock completo in lettura/scrittura | Online DDL (lock breve solo sui metadati) |
| Spazio disco recuperato | Sì, immediatamente | Sì, se innodb_file_per_table = ON |
| Momento ideale | Finestra a basso traffico | Può girare in orario di lavoro |
Per verificare la frammentazione delle tabelle prima dell’ottimizzazione:
# Controllare la frammentazione via WP-CLI
wp db query "SELECT table_name, ROUND(data_length/1024/1024, 2) AS data_mb, ROUND(data_free/1024/1024, 2) AS free_mb, ROUND(data_free/(data_length+1)*100, 1) AS frag_pct FROM information_schema.tables WHERE table_schema = DATABASE() AND data_free > 0 ORDER BY data_free DESC;"
Una frammentazione superiore al 20% giustifica un OPTIMIZE TABLE. Tabelle sotto i 10MB con frammentazione bassa possono essere ignorate: l’overhead dell’operazione supera il beneficio.
# Ottimizzare tutte le tabelle
wp db optimize
# Oppure via SQL su singola tabella
wp db query "OPTIMIZE TABLE wp_posts;"
Secondo la documentazione ufficiale di MySQL 8.0, OPTIMIZE TABLE su InnoDB mostra un messaggio “Table does not support optimize, doing recreate + analyze instead”. È normale e sicuro da ignorare.
Autoload options: il killer silenzioso delle performance
Ogni richiesta WordPress carica tutte le righe di wp_options dove autoload = 'yes'. Plugin mal configurati possono memorizzare array serializzati enormi con autoload attivo, costringendo PHP a deserializzare megabyte di dati ad ogni page load.
# Controllare la dimensione totale delle opzioni autoload
wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) AS autoload_mb FROM wp_options WHERE autoload = 'yes';"
# Trovare le opzioni autoload più pesanti
wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024, 2) AS size_kb FROM wp_options WHERE autoload = 'yes' ORDER BY LENGTH(option_value) DESC LIMIT 20;"
Una dimensione autoload superiore a 1MB è un segnale di allarme. Noi abbiamo trovato siti clienti con 5MB di autoload causati da plugin di analytics che memorizzavano log serializzati nelle opzioni. La soluzione è disabilitare l’autoload per quelle righe:
# Disabilitare autoload per una riga specifica
wp db query "UPDATE wp_options SET autoload = 'no' WHERE option_name = 'nome_opzione_gonfia';"
Aggiungere indici custom per velocizzare le query
Le tabelle WordPress hanno indici di base, ma alcune query personalizzate o plugin possono beneficiare di indici aggiuntivi. Per esempio, wp_postmeta ha un indice su meta_key ma non su meta_value. Se hai plugin che cercano per meta_value, aggiungere un indice può fare la differenza:
# Aggiungere indice su meta_value (solo se necessario)
wp db query "ALTER TABLE wp_postmeta ADD INDEX meta_value_idx (meta_value(50));"
Attenzione: gli indici aggiungono overhead in scrittura. Aggiungili solo su campi che vengono effettivamente usati nelle clausole WHERE. Monitora le query lente con:
# Abilitare il log delle query lente in wp-config.php
define('SAVEQUERIES', true);
# Poi nel footer del tema:
global $wpdb;
$queries = $wpdb->queries;
foreach ($queries as $q) {
if ($q[1] > 0.05) { // Query che impiegano più di 50ms
error_log('Slow query: ' . $q[0] . ' - ' . $q[1] . 's');
}
}
Script WP-CLI per pulizia batch su multipli siti
Per le agenzie che gestiscono decine di siti, la pulizia manuale non è scalabile. Ecco uno script bash che puoi eseguire su tutti i siti via SSH. Questo script è quello che usiamo internamente ad AgencyPilot per la manutenzione mensile.
#!/bin/bash
# db-cleanup.sh — Pulizia database WordPress batch
# Uso: ./db-cleanup.sh /var/www/sito-cliente
SITE_PATH=$1
WP="wp --path=${SITE_PATH}"
echo "=== Pulizia database: ${SITE_PATH} ==="
# 1. Rimuovi revisioni
echo "Rimozione revisioni..."
$WP post delete $($WP post list --post_type=revision --format=ids) --force 2>/dev/null
# 2. Rimuovi transient scaduti
echo "Pulizia transient..."
$WP transient delete --expired
# 3. Rimuovi spam
echo "Rimozione commenti spam..."
$WP comment delete $($WP comment list --status=spam --format=ids) --force 2>/dev/null
# 4. Rimuovi meta orfani
echo "Rimozione meta orfani..."
$WP db query "DELETE pm FROM wp_postmeta pm LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;"
# 5. Ottimizza tabelle
echo "Ottimizzazione tabelle..."
$WP db optimize
# 6. Report frammentazione residua
echo "=== Frammentazione residua ==="
$WP db query "SELECT table_name, ROUND(data_free/1024/1024, 2) AS free_mb FROM information_schema.tables WHERE table_schema = DATABASE() AND data_free > 1024*1024 ORDER BY data_free DESC;"
echo "=== Completato ==="
Per eseguirlo su tutti i siti in una volta sola, combinalo con un loop sui percorsi dei siti. Per automazione più avanzata, consulta la nostra guida all’automazione della gestione WordPress per agenzie.
Plugin per ottimizzazione database: confronto
Se preferisci un’interfaccia grafica invece di WP-CLI, ci sono plugin che automatizzano la pulizia. Ecco il nostro confronto basato su test reali.
| Plugin | Pulizia | Ottimizzazione | Automazione | Verdetto |
|---|---|---|---|---|
| WP-Optimize | Revisioni, transient, spam, pingback | Ottimizza tabelle InnoDB | Schedule settimanale/mensile | Consigliato — completo e affidabile |
| Advanced Database Cleaner | Meta orfani, transient, revisioni | Ottimizzazione base | Schedule con cron | Consigliato — rileva meta orfani meglio di altri |
| WP-Sweep | Meta orfani, termine orfani, opzioni orfane | Limitata | No schedule | Utile per pulizia one-off |
| WP Clean Up Optimizer | Base | Base | Limitata | Sconsigliato — codice datato |
Importante: i plugin di pulizia possono eliminare più dati del necessario. Sempre eseguire un backup completo prima di qualsiasi operazione di pulizia sul database.
Automazione della manutenzione: cron mensile
La pulizia del database non è un’operazione una-tantum. Va automatizzata su cadenza mensile. Per i server Linux, crea un cron job di sistema (non WP-Cron) che esegue lo script di pulizia:
# Crontab di sistema (eseguire il 1° di ogni mese alle 3:00)
0 3 1 * * /usr/local/bin/db-cleanup.sh /var/www/sito-cliente-1 >> /var/log/db-cleanup.log 2>&1
0 3 1 * * /usr/local/bin/db-cleanup.sh /var/www/sito-cliente-2 >> /var/log/db-cleanup.log 2>&1
Per gestire decine di siti, crea un file di configurazione con i percorsi e un wrapper che itera su tutti i siti. Noi usiamo questo approccio su 50+ siti clienti: il primo del mese, alle 3:00 di notte, ogni database viene pulito e ottimizzato automaticamente. Il log viene inviato su Slack per monitorare eventuali problemi.
FAQ — Ottimizzazione database WordPress
Con quale frequenza dovrei ottimizzare il database WordPress?
La cadenza ideale è mensile per siti con traffico medio, bimestrale per siti a basso traffico. Siti di e-commerce con WooCommerce o siti con editing intensivo beneficiano di pulizia quindicinale. Ottimizzare più spesso del necessario non produce benefici misurabili e aggiunge overhead inutile.
OPTIMIZE TABLE blocca il sito?
Su InnoDB (lo standard dal 2018), OPTIMIZE TABLE usa online DDL: la tabella rimane leggibile e scrivibile durante la maggior parte dell’operazione, con un lock breve sui metadati all’inizio e alla fine. Su MyISAM, invece, la tabella viene bloccata completamente. Verifica lo storage engine prima di procedere.
Posso eliminare tutte le revisioni senza conseguenze?
Sì, le revisioni sono copie dei contenuti e la loro rimozione non ha impatto sul funzionamento del sito. Tuttavia, se i tuoi clienti usano le revisioni per recuperare contenuti precedenti, conviene limitare il numero con WP_POST_REVISIONS invece di eliminarle tutte.
Qual è una dimensione accettabile per le opzioni autoload?
Secondo le best practice di WP Engine e la nostra esperienza, le opzioni autoload dovrebbero stare sotto 1MB. Sopra 1MB si inizia a notare degrado del TTFB. Sopra 2MB è un problema serio che va affrontato identificando e disattivando l’autoload delle opzioni gonfie.
I plugin di ottimizzazione database sono sicuri?
I plugin affidabili come WP-Optimize e Advanced Database Cleaner sono sicuri se usati correttamente. Il rischio principale è che eliminano dati in modo aggressivo: sempre eseguire un backup prima del primo utilizzo e rivedere cosa viene proposto per l’eliminazione. Evita plugin sconosciuti o con recensioni basse: alcuni eseguono query DELETE non sicure.
Checklist mensile per agenzie
- Esegui un backup completo prima di qualsiasi operazione
- Rimuovi le revisioni dei post (
wp post delete --post_type=revision) - Pulisci i transient scaduti (
wp transient delete --expired) - Elimina i commenti spam e i pingback
- Rimuovi i meta orfani con la query SQL
- Controlla la dimensione autoload di
wp_options - Esegui
wp db optimizesu tutte le tabelle - Verifica la frammentazione residua con il report
- Registra i risultati in un log per confrontare nel tempo
Seguendo questa checklist mensile, i database dei tuoi clienti rimarranno snelli e veloci, i backup saranno più rapidi e le query più efficienti. Per un’automazione completa del processo, consulta anche la nostra guida alla sicurezza WordPress per agenzie dove parliamo di hardening e monitoraggio continuo.