Optimisation PrestaShop : nettoyer la base et configurer le cache natif

Nettoyez votre base PrestaShop : sauvegarde, purge incrémentale des tables volumineuses, configuration du cache natif (Smarty/CCC/Symfony) et validation des gains (Redis, TTFB).

Trois écrans avec du code et des diagrammes pour l'optimisation.

Table des matières :

  1. Pré-requis : sauvegarde, staging et fenêtre de maintenance
  2. Audit de volumétrie : repérer les tables qui plombent InnoDB
  3. Nettoyage de la base PrestaShop : SQL ciblé et purge incrémentale
  4. Cache natif PrestaShop : Smarty, CCC et cache Symfony (BO + services)
  5. Backend de cache (Redis/Memcached) et validation des gains (TTFB, DB, hit ratio)

Pré-requis : sauvegarde, staging et fenêtre de maintenance

Nettoyer une base PrestaShop et durcir le cache natif, ça touche à trois couches à risque : données (MySQL/MariaDB), FS (permissions de var/cache/ et themes/), et comportement applicatif (invalidation de cache, overrides, modules). Contexte recommandé pour les exemples ci-dessous : PrestaShop 8.1.x à 9.1.x, PHP 8.2/8.3, MariaDB 10.6+ ou MySQL 8.0+. Sur PrestaShop 9.x (Symfony 6.4), le Back-Office et une partie des services tournent clairement sur le kernel Symfony : casser var/cache sans warmup, c’est du TTFB gratuit (et des erreurs 500 faciles si les droits ne sont pas bons).

Avant de toucher quoi que ce soit : snapshot + dump logique.

  • Snapshot (si VM / LVM / stockage managé) : utile pour rollback rapide en cas de purge trop agressive.
  • Dump logique : utile pour extraire sélectivement des tables, et portable (utile si vous devez restaurer sur une machine différente).
  • Sauvegarde fichiers : pensez au couple base + fichiers (images, thème, overrides, modules). Un dump SQL sans /img ne vous sauve pas d’une suppression accidentelle d’assets.

En production, un mysqldump cohérent sur InnoDB se fait avec --single-transaction (et sans verrou global), et on garde un dump compressé hors serveur. Exemple (à adapter au nom de base et au compte avec privilèges minimaux) :

mysqldump --single-transaction --routines --triggers \
  --default-character-set=utf8mb4 \
  -u prestashop -p prestashop_db | gzip > prestashop_db_$(date +%F).sql.gz

Pour la partie fichiers (exemple volontairement simple), vous pouvez au minimum archiver le thème actif + modules + overrides + config. Évitez d’archiver var/cache (inutile) :

tar --exclude='var/cache/*' -czf files_$(date +%F).tar.gz \
  app/config parameters.php themes modules override img

Travaillez idéalement en staging fidèle (mêmes versions PHP, mêmes options MySQL, même volume) pour valider les requêtes de purge, les temps de lock et l’impact cache. Si vous n’avez pas encore de staging propre, partez d’un environnement Docker maîtrisé (cf. Staging PrestaShop : créer un environnement Docker fidèle à la production).

Enfin, prévoyez une fenêtre de maintenance si vous faites des DELETE massifs/OPTIMIZE : sur gros volumes, vous pouvez saturer l’I/O, gonfler les undo logs et bloquer des transactions. Dans un contexte e-commerce français (pics lors des soldes, Black Friday, ou opérations média), la bonne pratique est de planifier ces opérations hors horaires de conversion, et de désactiver temporairement ce qui écrit beaucoup (par exemple des modules de tracking interne très verbeux), le temps de la purge.

Checklist rapide avant intervention :

  • [ ] Maintenance activée (BO) ou restriction IP admin (Nginx/Apache) si vous devez garder un accès privé.
  • [ ] Cron/queues arrêtés (relances de mails, exports, feeds, synchronisations ERP, etc.).
  • [ ] Dump SQL + archive fichiers validés (taille non nulle, test d’ouverture gzip -t).
  • [ ] Staging prêt si vous testez des requêtes non triviales.
  • [ ] Plan de rollback (restauration base + fichiers, temps estimé, personne responsable).

Audit de volumétrie : repérer les tables qui plombent InnoDB

Le nettoyage utile commence par un audit chiffré. Dans PrestaShop, les tables qui grossissent “toutes seules” sont typiquement celles liées à la navigation et à la “télémétrie” historique : *_connections, *_connections_page, *_guest, *_log, *_statssearch, *_pagenotfound, parfois *_mail. À l’inverse, des tables métier (produits, commandes) ne doivent pas être « nettoyées » sans règle fonctionnelle explicite. Objectif : isoler ce qui est purement éphémère (caches, logs, paniers abandonnés) et ce qui est utile au business (conversions, SAV, compta).

Commencez par la volumétrie via information_schema pour trier par poids réel (données + indexes). Cette requête marche sur MySQL/MariaDB ; adaptez table_schema :

SELECT
  table_name,
  table_rows,
  ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'prestashop_db'
ORDER BY (data_length + index_length) DESC
LIMIT 30;

Ensuite, mettez des dates sur ces volumes (sinon vous “nettoyez à l’aveugle”). Sur les tables typiques, commencez par regarder la période couverte :

SELECT MIN(date_add) AS oldest, MAX(date_add) AS newest, COUNT(*) AS rows
FROM ps_connections;

Si vous hébergez en UE (cas le plus courant pour des boutiques françaises), gardez aussi en tête les obligations RGPD côté limitation de conservation : conserver des logs contenant des identifiants (IP, e-mails, IDs clients) doit être justifié par une finalité et une durée. Sans entrer dans un cadre juridique ici, l’audit de tables ps_connections*/ps_guest/ps_log est aussi un audit de données personnelles potentielles : plus vous gardez longtemps, plus l’exposition augmente en cas d’incident.

Corrélez ensuite la volumétrie à la perf : tables lourdes + requêtes lentes = vrai sujet. Le nettoyage ne remplacera pas un index manquant, mais il peut éviter de “payer” des scans inutiles (notamment sur des requêtes admin qui agrègent des historiques). Si vous n’avez pas encore instrumenté le serveur, activez un slow query log (ou un proxy/observabilité) puis regardez le ratio “queries lentes” sur les tables de stats.

Un réflexe utile : repérer les tables “historiques” qui déclenchent des opérations coûteuses (tri/GROUP BY) et vérifier si elles ont un index cohérent sur date_add. Beaucoup de purges “lentes” viennent d’un DELETE WHERE date_add < ... sans index adapté.

Pour la partie SQL/Index, gardez sous la main : PrestaShop MySQL : analyser slow query log et optimiser index et, côté front, PrestaShop lent : diagnostic TTFB, bots et optimisation serveur.

Mini-scenario concret (fréquent) : une boutique B2C française avec un fort trafic SEO voit ps_connections_page monter à plusieurs dizaines de millions de lignes parce que (a) des bots agressifs crawlent des facettes et (b) la rétention n’a jamais été définie. Résultat : sauvegardes plus lentes, espace disque qui grimpe, et BO “Stats” qui rame. Dans ce cas, le nettoyage SQL est utile, mais il doit idéalement s’accompagner d’un durcissement côté crawl (robots, facettes, paramètres d’URL), sinon vous regonflez la table en quelques semaines.

Nettoyage de la base PrestaShop : SQL ciblé et purge incrémentale

La règle d’or : purge incrémentale (batch) plutôt que DELETE massif, surtout sur tables très écrites. Un DELETE qui supprime des millions de lignes en une transaction va : (1) saturer le buffer pool, (2) gonfler les undo/redo, (3) tenir des verrous plus longtemps que nécessaire, (4) déclencher de la réplication retardée si vous avez un replica. Sur MySQL, faites des batches avec LIMIT, et si possible piloter le job via cron en heures creuses.

Deux points pratiques souvent oubliés :

  • DELETE ... LIMIT n’a pas d’ordre garanti. Pour une purge stable, il est préférable de purger par plage de dates (ou par PK croissante) et de boucler tant que vous supprimez.
  • Sur les tables liées (ex. ps_connections / ps_connections_page), supprimez d’abord les enfants, puis les parents, pour éviter des orphelins et limiter les scans.

Exemples de cibles “safe” dans 95% des shops (à adapter à votre rétention) :

1) Logs applicatifs (ps_log) :

DELETE FROM ps_log
WHERE date_add < (NOW() - INTERVAL 90 DAY)
LIMIT 50000;

2) Connexions / pages vues (ps_connections*) :

DELETE cp FROM ps_connections_page cp
JOIN ps_connections c ON c.id_connections = cp.id_connections
WHERE c.date_add < (NOW() - INTERVAL 30 DAY)
LIMIT 50000;

DELETE FROM ps_connections
WHERE date_add < (NOW() - INTERVAL 30 DAY)
LIMIT 50000;

3) Paniers abandonnés (non liés à une commande) :

DELETE cpr FROM ps_cart_product cpr
JOIN ps_cart c ON c.id_cart = cpr.id_cart
LEFT JOIN ps_orders o ON o.id_cart = c.id_cart
WHERE o.id_order IS NULL
  AND c.date_add < (NOW() - INTERVAL 60 DAY)
LIMIT 50000;

DELETE c 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 < (NOW() - INTERVAL 60 DAY)
LIMIT 50000;

Ces exemples supposent le préfixe ps_. Sur une instance multi-boutique, vous pouvez ajouter AND c.id_shop IN (...) pour maîtriser l’impact (pratique quand un shop “test” pollue plus que le shop principal).

Rétentions recommandées (repères)

Sans imposer une règle universelle (car dépendance business), voici des repères pragmatiques, souvent acceptables en e-commerce :

Table (ou famille) Contenu Rétention typique Pourquoi
ps_log logs applicatifs 30–180 jours debug / audit technique, mais pas besoin d’historique long
ps_connections*, ps_guest navigation / sessions 7–60 jours volumétrie explosive, utile surtout court terme
ps_pagenotfound 404 30–180 jours utile SEO, mais on traite et on purge
ps_statssearch recherches internes 90–365 jours utile merchandising, moins volumineux selon trafic
ps_mail historique mails variable souvent plus un audit interne qu’un besoin métier

Attention aux tables “stats” : certaines sont utiles pour le SEO (ex. ps_pagenotfound pour trouver des 404), mais conserver 3 ans d’historique est rarement rationnel. Idem pour les tables mail : si vous utilisez ps_mail comme audit, fixez une rétention et documentez-la (et vérifiez la présence de données personnelles).

Côté outillage, le module historique pscleaner (Database Cleaner) peut supprimer certaines données (paniers, commandes de test, logs), mais il reste superficiel : il ne gère pas vos modules, et il ne vous protège pas d’un mauvais choix de rétention. Pour du nettoyage “production-grade”, script + métriques + rollback.

Pour exécuter une purge en batch sans écrire de PHP, un pattern simple est un bash + boucle jusqu’à 0 ligne supprimée (avec une pause). Exemple plus robuste (on récupère la dernière ligne retournée) :

while true; do
  affected=$(mysql --batch --skip-column-names -u prestashop -p prestashop_db -e "
    DELETE FROM ps_log
    WHERE date_add < (NOW() - INTERVAL 90 DAY)
    LIMIT 50000;
    SELECT ROW_COUNT();
  " | tail -n 1)

  [ "${affected:-0}" -eq 0 ] && break
  sleep 1
done

Après une purge significative, pensez à exécuter un ANALYZE TABLE sur les tables concernées pour mettre à jour les statistiques d’optimiseur (utile si vous avez constaté des plans de requêtes dégradés) :

ANALYZE TABLE ps_connections, ps_connections_page, ps_log;

Cache natif PrestaShop : Smarty, CCC et cache Symfony (BO + services)

Le “cache natif” PrestaShop, en pratique, c’est au minimum : Smarty cache + compilation, CCC (minify/concat CSS/JS) et, sur PrestaShop 8/9, cache Symfony (container compilé, services, etc.). Le tout se pilote dans Paramètres avancés → Performances. Le piège : activer tout en prod, mais laisser des modules/overrides incompatibles (templates qui varient sur des cookies, JS injecté au runtime, etc.). Résultat : pages cassées, ou cache invalidé en permanence (donc coût CPU sans gain).

Côté Smarty : en production, vous voulez généralement :

  • “Recompiler les templates” = Jamais (sinon compilation .tpl trop fréquente),
  • Cache Smarty = Oui (si votre thème/modules ne cassent pas le rendu),
  • et une discipline de déploiement : invalider lors d’une release, pas “à la main” plusieurs fois par jour.

Une manière simple de raisonner : si vous changez le thème ou un module qui modifie le rendu, vous purgez ; sinon, vous laissez vivre. C’est le seul moyen d’éviter le coût CPU/IO de compilation à chaque requête.

Le comportement est documenté au niveau des mécanismes Smarty, mais l’implémentation PrestaShop a ses particularités (clé de cache selon device/lang/currency, modules qui forcent nocache, hooks qui rendent du contenu “user-specific”). Pour un panorama plus large incluant CDN et impacts front, voir PrestaShop : optimiser performance via cache Smarty, CCC et CDN.

CCC (Combine, Compress and Cache) est utile tant que vous avez une chaîne front qui le tolère. Sur des thèmes modernes qui font déjà du bundling (Webpack/Vite), CCC peut être contre-productif (double minification, ordre de chargement cassé, debug plus difficile). Traitez CCC comme un switch de compatibilité : vous l’activez seulement si vos assets sont “legacy” (multiples fichiers non bundlés) et si vous validez sur un parcours complet (home → catégorie → fiche produit → panier → checkout).

Et n’oubliez pas que le vrai cache “gratuit” côté PHP, c’est l’opcode cache ; si votre serveur n’a pas d’OPcache correctement dimensionné, le cache PrestaShop masquera juste un problème de runtime (cf. Symfony 7 : déploiement PHP-FPM Nginx, cache et opcache pour la logique générale côté PHP-FPM).

Sur PrestaShop 9.x, le cache Symfony doit être traité comme sur n’importe quelle app Symfony :

  • purge/warmup à chaque release,
  • permissions propres (utilisateur PHP-FPM / CLI cohérent),
  • pas de cache:clear “au hasard” en pleine charge.

En pratique, si vous déployez via CI/CD, vous pouvez intégrer un warmup (à adapter selon votre arborescence et l’utilisateur d’exécution) :

php bin/console cache:clear --env=prod
php bin/console cache:warmup --env=prod

La documentation Symfony sur le composant Cache rappelle aussi l’alignement sur les standards PSR-6/PSR-16, utile si vous développez des modules “Symfony-first” et voulez des pratiques cohérentes de pool/invalidation : https://symfony.com/doc/current/components/cache.html.

Backend de cache (Redis/Memcached) et validation des gains (TTFB, DB, hit ratio)

PrestaShop expose un backend de cache via la couche Cache (FS, Memcached, Redis selon versions et configuration). Dans le BO (Performances → Cache), basculer sur Redis/Memcached n’est pas “magique” : ça marche si (1) vos appels Cache::getInstance() sont réellement utilisés par le core/modules, (2) votre serveur Redis/Memcached est proche (latence), (3) vous avez une stratégie d’invalidation. Redis est généralement plus flexible (monitoring, options de persistance), Memcached est minimaliste mais efficace pour du pur volatile.

Point “GEO” simple mais souvent décisif : si votre PHP-FPM est à Paris et votre Redis dans une autre région (ou pire, dans un autre pays), vous pouvez dégrader le temps de réponse par la latence réseau. En France, sur des infrastructures courantes (hébergeurs locaux ou cloud), veillez surtout à la colocalisation (même DC/région) et à la stabilité réseau : le cache n’a de valeur que s’il est plus rapide que le recalcul.

Sur un serveur dédié, Redis se dimensionne d’abord sur la RAM disponible et le taux de hit. Un objectif réaliste est d’avoir un hit ratio > 90% sur les clés “chaudes” (config, règles panier, blocs). Si le hit ratio est bas, vous payez juste un aller-retour + sérialisation, sans bénéfice.

Pour une vérification rapide côté Redis :

redis-cli INFO stats | egrep 'keyspace_hits|keyspace_misses'

Et calculez le ratio :

  • hit ratio = hits / (hits + misses)

Si vous activez Redis mais continuez à vider le cache via BO plusieurs fois par jour, vous annulez les gains. Pour une approche d’architecture plus globale (Nginx/PHP-FPM/MariaDB/Redis), voir Serveur dédié PrestaShop : config Nginx PHP-FPM MariaDB Redis.

La validation ne se fait pas “au feeling”. Mesurez avant/après sur : TTFB, temps SQL, nombre de requêtes, taille des tables et I/O wait.

En pratique :

  • TTFB :
  curl -s -o /dev/null -w '%{time_starttransfer}\n' https://…

Faites plusieurs runs (au moins 10), en séparant “cold cache” et “warm cache”.

  • SQL : slow_query_log, performance_schema ou APM.

  • DB size : information_schema (requête plus haut) après purge et après ANALYZE.

Pour cadrer le “pourquoi” perf/SEO, Google présente les Core Web Vitals comme des métriques centrées utilisateur, basées sur des données terrain (référence officielle) : https://web.dev/vitals/. Dans les faits, un nettoyage DB + cache natif correctement configuré se traduit souvent par une baisse du TTFB et une meilleure stabilité sous charge (moins de timeouts MySQL, moins de “spikes” CPU lors de compilations Smarty).

Exemple réaliste (ordre de grandeur) : un catalogue ~50 000 produits avec fort trafic organique peut accumuler des dizaines de millions de lignes dans ps_connections_page en quelques mois. Une rétention ramenée à 30 jours (avec purge nocturne) réduit fortement l’empreinte disque et limite l’impact sur les requêtes admin/statistiques. En parallèle, passer Smarty sur “recompile jamais” + cache activé stabilise le front, à condition de purger proprement lors des déploiements (plutôt que de laisser le BO le faire à la volée).

Si vous cherchez un cadre méthodologique pour diagnostiquer côté serveur avant d’accuser PrestaShop, reprenez Optimisation de code PrestaShop : diagnostiquer lenteurs et requêtes SQL et verrouillez vos changements via une checklist de release (cf. checklist de mise à jour et tests PrestaShop).


À lire aussi