È una frustrazione che sentiamo di continuo: quando hai lanciato il tuo negozio PrestaShop era veloce, e un anno dopo le stesse pagine arrancano. Stesso hosting, stesso tema, stessi moduli, nulla che tu possa indicare come cambiato. Allora che cosa è successo? In molti di questi casi il colpevole è proprio l’unica cosa che è cambiata, in silenzio, ogni singolo giorno in cui il negozio è rimasto aperto: il database. PrestaShop registra per impostazione predefinita una quantità sorprendente di attività, e le tabelle che al lancio contenevano poche centinaia di righe ora ne contengono milioni. Le query che su una tabella piccola erano istantanee diventano un freno misurabile su una tabella enorme e, dato che le pagine prodotto, le pagine categoria e il back office toccano tutte quelle tabelle, il rallentamento si diffonde ovunque nello stesso momento.
Questa guida riguarda nello specifico il gonfiarsi del database che si accumula nel tempo: quali tabelle crescono, perché crescono, che cosa è davvero sicuro pulire e come farlo senza rompere il negozio. Non è un articolo generico sul "perché PrestaShop è lento" (moduli, hosting e peso del front-end sono cause separate, ne parliamo in che cosa rende davvero lento PrestaShop) e non riguarda il tentativo di aggirare query lente con la cache (Redis e la cache full-page hanno articoli dedicati). Qui parliamo della ruggine che si accumula dentro le tabelle stesse.
Le tabelle soggette a gonfiarsi e la politica di pulizia sicura qui sotto sono valide per PrestaShop 1.7, 8.x e 9.x. Le tabelle statistiche effettivamente presenti variano in base alla versione e ai moduli installati, quindi la query di verifica viene per prima: misura sempre il tuo schema prima di prendere di mira qualsiasi tabella.
Perché i database PrestaShop crescono in silenzio
PrestaShop include un sistema di statistiche e log che registra molto più di quanto la maggior parte dei commercianti immagini. Connessioni, sessioni ospite, ricerche interne, errori 404, log delle email inviate, carrelli abbandonati: quando le relative funzioni statistiche e di logging sono attive, tutto finisce nel database, e la conservazione dei dati resta quasi interamente a carico tuo. PrestaShop non ha una politica integrata del tipo "mantieni 90 giorni", e la rimozione dipende dai moduli e dalla configurazione in uso; perciò, nella maggior parte dei negozi, i dati semplicemente si accumulano.
Per un negozio modesto con 500 visitatori al giorno, i numeri non perdonano:
- ps_connections, una riga per ogni connessione di un visitatore. 500/giorno significa circa 15.000/mese, circa 182.000/anno, e non si ferma mai.
- ps_connections_source. Tracciamento del referrer/HTTP-referer per ogni connessione; spesso è più grande della stessa ps_connections.
- ps_guest, una riga per ogni ospite univoco (impronta browser/OS/schermo), che si accumula per ogni visitatore non autenticato.
- ps_log, il log dell’applicazione. Un solo modulo troppo verboso che genera avvisi può scrivere diverse righe per ogni caricamento di pagina e spingere questa tabella a milioni di righe.
- ps_pagenotfound, ogni 404, incluso il traffico incessante dei bot che sondano /wp-admin, /phpmyadmin e percorsi simili.
- ps_statssearch, ogni query di ricerca interna digitata da un visitatore.
- ps_cart e ps_cart_product, ogni carrello mai creato, la stragrande maggioranza dei quali non diventerà mai un ordine.
- ps_mail, un log di ogni email inviata da PrestaShop.
- le tabelle statistiche native (ps_statssearch, ps_searchengine e, in alcune installazioni, ps_referrer/ps_referrer_cache), il livello di analisi che quasi nessuno consulta davvero dentro PrestaShop perché usa invece Google Analytics. Quali di queste tabelle esistano dipende dalla versione di PrestaShop, dai moduli statistiche installati e dalla storia degli aggiornamenti del database: quindi controlla lo schema reale prima di intervenire su una qualsiasi di esse, invece di presupporre un elenco fisso.
Il punto è semplice: dopo due o tre anni queste tabelle contengono spesso milioni di righe, l’insieme di lavoro di MySQL non entra più comodamente in memoria, il buffer pool InnoDB va in sofferenza, e anche le query ben indicizzate rallentano perché il motore deve leggere più pagine dal disco. Il tuo negozio non è diventato più pesante in un modo visibile al visitatore: è diventato pesante dove solo il database lo sente.
Scopri prima che cosa è davvero gonfio nel tuo negozio
SELECT table_name, table_rows, ROUND((data_length + index_length) / 1024 / 1024, 1) AS mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name LIKE 'ps\_%'
ORDER BY (data_length + index_length) DESC
LIMIT 20;
Non pulire alla cieca. Ogni negozio si gonfia in modo diverso in base al tipo di traffico e ai moduli installati, quindi la prima cosa da fare è misurare le dimensioni delle tabelle. Esegui questa query in phpMyAdmin (o Adminer, o dalla CLI MySQL), sostituendo il nome reale del tuo database: elenca le tabelle più grandi per numero di righe e dimensione su disco:
- SELECT table_name, table_rows, ROUND((data_length + index_length)/1024/1024, 1) AS size_mb FROM information_schema.tables WHERE table_schema = 'your_db_name' ORDER BY (data_length + index_length) DESC LIMIT 25;
Leggi il risultato prima di toccare qualsiasi cosa. Se in cima trovi ps_orders, ps_order_detail e ps_product_lang, è un gonfiarsi sano: dati aziendali reali che devi conservare. Se invece in cima alla lista ci sono ps_connections, ps_guest, ps_log, ps_statssearch o ps_pagenotfound, hai trovato spazzatura che può valere gigabyte. Questa singola query ti dice se il resto dell’articolo ti riguarda davvero e quali tabelle prendere di mira.
Quali tabelle pulire, e quali non toccare mai
È qui che le guide diventano pericolose, quindi serve precisione. Le tabelle si dividono in tre gruppi.
| Tabella | Che cosa contiene | Politica di pulizia |
|---|---|---|
| ps_connections | Log delle connessioni dei visitatori | DELETE filtrato per data per conservare 30–90 giorni, oppure TRUNCATE se non vuoi conservare nulla. GA lo fa meglio. |
| ps_connections_source | Referrer per connessione | Pulisci insieme a ps_connections. |
| ps_guest | Impronte dei visitatori ospiti | Pulisci insieme a ps_connections (attenzione alla FK verso ps_customer). |
| ps_log | Log applicativo/errori | Prima leggilo per gli errori recenti, poi svuotalo. |
| ps_pagenotfound | Log 404 | Dai un’occhiata ai link realmente rotti, poi svuotalo. |
| ps_statssearch | Query di ricerca interne | Conserva circa 90 giorni per ricavare indicazioni di merchandising, DELETE filtrato per data per le righe più vecchie. |
| ps_mail | Log delle email inviate | Conserva le più recenti per l’assistenza, svuota quelle vecchie. |
| ps_cart / ps_cart_product | Carrelli, inclusi quelli abbandonati | Elimina solo i carrelli orfani (senza ordine) più vecchi di 30–60 giorni. |
| ps_customer_session / ps_employee_session | Righe dei token di sessione (1.7.6.6+) | Le righe scadute possono essere eliminate; preserva quelle attive. Sono minuscole: non sono una vera fonte di gonfiamento. |
| ps_orders + ps_order_* | Dati degli ordini | Mai eliminare, conservazione legale (spesso 7–10 anni). |
| ps_customer | Account cliente | Elimina solo su richiesta GDPR, mai in blocco. |
| ps_product + related | Catalogo | Mai, i prodotti disattivati restano comunque collegati agli ordini. |
| ps_configuration | Impostazioni del negozio | Mai. Una riga sbagliata può rompere il negozio. |
La tabella dei carrelli richiede il bisturi, non il martello
L’errore che vediamo più spesso è svuotare completamente ps_cart con TRUNCATE per recuperare spazio. Non farlo. Una riga di carrello senza un id_cart corrispondente in ps_orders è un carrello abbandonato ed è sicura da rimuovere quando è vecchia; un carrello che è collegato a un ordine fa parte della storia di quell’ordine. Lo schema sicuro identifica solo i carrelli orfani più vecchi di una certa finestra temporale, poi elimina prima le righe figlie in ps_cart_product e solo dopo i carrelli, così non lasci riferimenti orfani. Il modo affidabile per farlo è costruire prima un elenco di valori id_cart candidati e poi eliminare in base a quell’elenco: questo permette anche di lavorare a blocchi in modo pulito. Nota che MySQL non consente LIMIT su un DELETE multi-tabella (JOIN), quindi evitiamo del tutto la forma JOIN nell’operazione di eliminazione:
- CREATE TEMPORARY TABLE tmp_orphan_carts AS SELECT c.id_cart FROM ps_cart c LEFT JOIN ps_orders o ON o.id_cart = c.id_cart WHERE o.id_order IS NULL AND c.date_add < DATE_SUB(NOW(), INTERVAL 60 DAY);
- DELETE FROM ps_cart_product WHERE id_cart IN (SELECT id_cart FROM tmp_orphan_carts) LIMIT 20000;, ripeti finché riporta 0 righe interessate.
- DELETE FROM ps_cart WHERE id_cart IN (SELECT id_cart FROM tmp_orphan_carts) LIMIT 20000;, anche qui, ripeti fino a 0 righe.
- DROP TEMPORARY TABLE tmp_orphan_carts;
Ogni DELETE ora prende di mira una sola tabella senza join, quindi LIMIT 20000 è valido e puoi rieseguire ogni istruzione finché non viene completata. Termina sempre le eliminazioni delle righe figlie in ps_cart_product prima di eliminare le righe padre in ps_cart, e quel lavoro a blocchi è intenzionale: la prossima sezione spiega perché conta più di quanto sembri.
Come pulire in sicurezza senza mandare giù il negozio
Passaggio 1, fai il backup del database, senza eccezioni
Prima di qualsiasi DELETE o TRUNCATE, crea un backup completo dei dati (mysqldump o snapshot del tuo hosting). Lo strumento integrato di PrestaShop Parametri avanzati → Database → Backup DB esiste, ma per tabelle grandi un mysqldump lato server è più affidabile. Se una pulizia va storta, il ripristino è la tua unica rete di sicurezza: trattalo come obbligatorio, non come facoltativo.
Passaggio 2, elimina a blocchi, non con una sola query gigante
DELETE FROM ps_connections
WHERE date_add < DATE_SUB(NOW(), INTERVAL 180 DAY)
LIMIT 5000;
Un singolo DELETE che rimuove due milioni di righe mantiene un lock lungo e può rendere il front office non responsivo mentre viene eseguito. Lavora a blocchi da 10.000–50.000 righe (il LIMIT nelle query sui carrelli qui sopra), con una breve pausa tra un’esecuzione e l’altra, e ripeti finché la tabella è pulita. Quando vuoi una finestra di conservazione invece di una tabella vuota, mantieni gli ultimi 90 giorni, elimina il resto, usa un DELETE filtrato per data come DELETE FROM ps_connections WHERE date_add < DATE_SUB(NOW(), INTERVAL 90 DAY) LIMIT 50000;, ripetuto finché segnala 0 righe. Solo quando vuoi davvero eliminare tutto da una tabella di log sacrificabile dovresti usare TRUNCATE TABLE ps_connections;: è molto più veloce perché elimina e ricrea la tabella invece di rimuovere le righe una alla volta, ma non può essere filtrato per data e azzera l’auto-incremento, quindi riservalo allo svuotamento di intere tabelle e fallo in un ordine sicuro rispetto alle dipendenze se tabelle correlate si referenziano tra loro.
Passaggio 3, recupera lo spazio su disco con OPTIMIZE TABLE
Questa è la parte che i commercianti spesso trascurano. PrestaShop usa InnoDB e, dopo un grande DELETE, il numero di righe diminuisce ma il file su disco non si riduce: lo spazio liberato resta riservato come frammentazione interna e le query possono ancora leggere quelle pagine vuote. Devi ricostruire esplicitamente la tabella:
- OPTIMIZE TABLE ps_connections, ps_connections_source, ps_guest, ps_log, ps_pagenotfound, ps_statssearch;
Su InnoDB, OPTIMIZE TABLE ricostruisce la tabella e rianalizza i suoi indici, deframmentando i dati. Il fatto che restituisca davvero spazio al sistema operativo dipende dalla configurazione: con innodb_file_per_table abilitato, impostazione predefinita sui MySQL/MariaDB moderni, ogni tabella ha il proprio tablespace e lo spazio liberato viene restituito al filesystem; con un tablespace di sistema condiviso, no. La ricostruzione può anche bloccare o copiare la tabella a seconda della versione e della configurazione di MySQL/MariaDB, quindi eseguila dopo le eliminazioni, in una finestra di manutenzione o di basso traffico, e verifica il comportamento sulla versione specifica del tuo server prima di contare sul recupero dello spazio.
Passaggio 4. Svuota la cache PrestaShop solo se serve
Eliminare righe dalle tabelle di log e statistiche è una pulizia dei dati, non una modifica al codice o allo schema, quindi di norma non richiede lo svuotamento della cache: PrestaShop non mette in cache quelle righe grezze di log. Svuota la cache solo se la pulizia ha toccato dati che qualcosa sta attualmente memorizzando in cache (per esempio statistiche del cruscotto che alimentano un widget in cache), oppure dopo modifiche a codice, moduli o schema che invalidano davvero l’output compilato. Quando serve, usa Parametri avanzati → Prestazioni → Svuota cache nel back office (oppure elimina il contenuto di var/cache/); nelle versioni che mantengono un indice delle classi compilato, la ricostruzione avviene come parte dello stesso svuotamento.
Farlo dal back office invece che con SQL grezzo

L'elenco di stato verifica la versione di PHP, i moduli, le tabelle del database, la frammentazione, le righe ripulibili e la dimensione della cache, ciascuno contrassegnato come non ancora verificato.

La configurazione deve stare nel modulo, non in SQL improvvisato conservato sul portatile di qualcuno.
L’SQL grezzo è preciso ma spietato: un JOIN mancante e lasci orfano un ordine. Se preferisci non scrivere istruzioni DELETE contro un negozio in produzione, hai due strade più sicure dentro PrestaShop:
- SQL Manager (integrato): Parametri avanzati → Database → SQL Manager ti permette di salvare e rieseguire la query di verifica delle dimensioni vista sopra e qualsiasi controllo in sola lettura. È principalmente uno strumento per SELECT, ma è il posto giusto per misurare prima e dopo, e per tenere a portata di mano la query di verifica mese dopo mese.
- Un modulo di manutenzione: un modulo dedicato alla pulizia gestisce la logica dei carrelli orfani, le eliminazioni a blocchi e il passaggio OPTIMIZE da una schermata del back office con checkbox: niente SQL, con le operazioni distruttive bloccate da una conferma. È la strada che consiglieremmo a un proprietario di negozio non sviluppatore, proprio perché elimina il rischio di digitare per errore un DELETE su ps_orders.
Qualunque opzione tu scelga, la politica è la stessa dell’SQL qui sopra: cambia solo il modo in cui la esegui in sicurezza.
Rendila ricorrente, non un panico annuale
Il gonfiarsi del database è continuo, quindi anche la pulizia dovrebbe esserlo. Un negozio pulito una volta e poi ignorato torna agli stessi conteggi di righe nel giro di pochi mesi. Imposta una routine mensile fissa:
- Dai un’occhiata a ps_log e ps_pagenotfound per individuare elementi reali (errori ricorrenti, link davvero rotti), poi svuota le voci più vecchie di 30 giorni.
- Elimina i carrelli orfani più vecchi di 60 giorni (le due query sui carrelli qui sopra), prima le righe figlie in ps_cart_product, poi i carrelli.
- Elimina le righe di ps_connections, ps_connections_source e ps_guest più vecchie di 90 giorni con un DELETE filtrato per data (attenzione alla FK ps_guest → ps_customer), oppure svuotale del tutto con TRUNCATE se non vuoi alcuna finestra di conservazione.
- Riduci ps_statssearch e ps_mail a circa 90 giorni con lo stesso schema DELETE filtrato per data.
- Esegui OPTIMIZE TABLE su tutto ciò che hai pulito.
L’intera sequenza può vivere in uno script SQL lanciato da un cron job mensile sul server, oppure essere eseguita su pianificazione da un modulo di manutenzione. Automatizzarla è la differenza tra un negozio che resta veloce e uno che scivola silenziosamente verso caricamenti pagina da 4 secondi mentre tu sei impegnato a gestire l’attività.
Un livello più in profondità: indici e query
Potare le righe è la mossa con il maggior impatto perché riduce ogni scansione di tabella in una volta sola, ma vale la pena citare due aspetti collegati, così sai dove si trova il limite:
- Indici mancanti nei moduli di terze parti. Molti moduli creano le proprie tabelle senza indicizzare le colonne su cui filtrano. Abilita lo slow query log di MySQL, cerca query che colpiscono una tabella di modulo con una scansione completa e aggiungi un indice sulla colonna WHERE/JOIN. Una tabella pulita con l’indice giusto può trasformare una query da 2 secondi in pochi millisecondi.
- Ottimizzazione MySQL a livello server (innodb_buffer_pool_size e simili) e ottimizzazione più ampia delle query sono un’altra disciplina: qui si entra nel territorio di hosting e configurazione, trattato in ottimizzazione delle prestazioni dalle query del database alla cache full-page e in scegliere l’hosting per PrestaShop. Pulire prima i dati significa ottimizzare un database snello invece di uno sovraccarico, quindi fai questo passaggio prima di comprare un server più grande.
Misura il prima e il dopo, correttamente
Non affidarti alla sensazione che "sembra più veloce". Prima di pulire, registra il tuo TTFB (Time to First Byte) su un insieme coerente di pagine: homepage, una pagina prodotto, una pagina categoria e il cruscotto del back office (spesso l’admin è dove il gonfiarsi del database pesa di più, perché le tabelle statistiche alimentano quei widget del cruscotto). Annota anche le dimensioni delle tabelle principali dalla query di verifica. Dopo la pulizia e OPTIMIZE, rimisura le stesse pagine e le stesse tabelle.
Non ti daremo una percentuale garantita: il guadagno dipende interamente da quanto era gonfio il database all’inizio. Un negozio la cui ps_connections conteneva tre anni di log vedrà un salto molto più grande di uno già ordinato. Se la diagnostica non punta chiaramente al database, parti da un controllo strutturato della lentezza invece di tirare a indovinare: il tuo negozio è lento, come verificarlo e cosa fare. E se vuoi vedere com’è un PrestaShop ottimizzato da cima a fondo, ne abbiamo documentato uno in PageSpeed 99/100 su un negozio reale con 130 moduli.
Il principio resta valido qualunque strada tu scelga: PrestaShop continuerà tranquillamente a registrare dati fino a seppellirsi se glielo permetti, perché nulla pota quelle tabelle al posto tuo. Trova il gonfiarsi reale, elimina solo ciò che è davvero sacrificabile, recupera lo spazio con OPTIMIZE e metti tutto in calendario. Fallo, e il tuo negozio conserva la velocità che aveva il giorno del lancio, anche dopo anni di attività, senza comprare nient’altro.
Domande frequenti
È sicuro svuotare ps_connections e ps_guest con TRUNCATE?
Svuotare con TRUNCATE ps_connections e le tabelle associate (ps_connections_source, ps_connections_page) è sicuro se non usi le statistiche native di PrestaShop: quelle tabelle alimentano analisi che la maggior parte dei commercianti sostituisce comunque con Google Analytics. ps_guest richiede un po’ più di attenzione per via del collegamento con chiave esterna a ps_customer; preferisci un DELETE filtrato per data che conservi le righe recenti a un TRUNCATE alla cieca. Quello che non devi mai svuotare con TRUNCATE è ps_cart: i carrelli abbandonati sono mescolati a carrelli appartenenti a ordini reali, quindi qui serve il bisturi sui soli orfani mostrato sopra, non il martello.
Ho eliminato milioni di righe ma il disco non si è ridotto. Perché?
InnoDB non restituisce lo spazio liberato al sistema operativo dopo un DELETE: il conteggio delle righe diminuisce ma il file su disco resta della stessa dimensione, con le pagine liberate trattenute come frammentazione interna. Devi ricostruire la tabella con OPTIMIZE TABLE per recuperarlo, e anche allora lo spazio viene rilasciato al filesystem solo se innodb_file_per_table è attivo (impostazione predefinita sui MySQL/MariaDB moderni). Esegui OPTIMIZE dopo le eliminazioni, in una finestra di basso traffico, perché può bloccare o copiare la tabella a seconda della versione.
Devo svuotare la cache PrestaShop dopo aver pulito il database?
Di solito no. Eliminare righe dalle tabelle di log e statistiche è una pulizia dei dati, non una modifica al codice o allo schema, e PrestaShop non mette in cache quelle righe grezze. Svuota la cache solo se la pulizia ha toccato dati che qualcosa sta attualmente memorizzando in cache, per esempio statistiche del cruscotto usate da un widget in cache, oppure dopo vere modifiche a codice/moduli/schema. La normale pulizia di log e carrelli non lo richiede.
Perché eliminare a blocchi invece che con una sola query?
Un singolo DELETE che rimuove due milioni di righe mantiene un lock lungo e può bloccare il front office mentre viene eseguito. Lavorare a blocchi da 10.000–50.000 righe con una breve pausa tra un blocco e l’altro mantiene ogni lock breve e il negozio responsivo. Usa un DELETE filtrato per data con un LIMIT, ripetuto finché segnala zero righe. TRUNCATE è l’eccezione: è quasi istantaneo perché elimina e ricrea la tabella, ma non può essere filtrato per data, quindi riservalo allo svuotamento completo di una tabella di log sacrificabile.
Non si gonfierà di nuovo?
Sì: è proprio per questo che deve diventare ricorrente. PrestaShop non ha una politica di conservazione integrata, quindi un negozio pulito una sola volta torna agli stessi conteggi di righe nel giro di pochi mesi. Metti la sequenza (pulizia delle tabelle log/statistiche/carrelli, poi OPTIMIZE) in un cron mensile, oppure lascia che un modulo di manutenzione la esegua su pianificazione, così le tabelle non hanno mai il tempo di ricrescere. Automatizzarla è la differenza tra restare veloci e scivolare silenziosamente verso caricamenti pagina da 4 secondi.
Letture correlate
- Database Cleanup, esegue la logica dei carrelli orfani, le eliminazioni a blocchi e OPTIMIZE dal back office su pianificazione, mostrando i conteggi delle righe prima che venga eliminato qualcosa
- Performance Revolution, quando il database pulito è ancora limitato dal TTFB, aggiunge sopra il livello di cache full-page e CSS critico
- Ottimizzazione delle prestazioni: dalle query del database alla cache full-page, indici, EXPLAIN e tuning InnoDB per il database snello che hai appena creato
- Che cosa rende davvero lento PrestaShop, conferma che il gonfiarsi del database sia la causa prima di iniziare a eliminare