Des pages produits qui rame, un processus de paiement qui hésite, une liste de commandes qui expire dans le back-office : dans neuf cas sur dix, c’est la base de données qui est en cause, et non PHP. Ce qui rend les boutiques très fréquentées prévisibles, c’est que les problèmes se concentrent là où les requêtes SQL lentes se focalisent dans PrestaShop, autour de quatre tables qui s’enrichissent quotidiennement sans jamais se vider : ps_cart, ps_cart_product, ps_orders et ps_order_detail. Quatre étapes permettent de les identifier. Activez le journal des requêtes lentes, triez les données par durée totale, exécutez EXPLAIN sur les entrées les plus problématiques, puis modifiez la requête ou l’index. Aucune conjecture n’est nécessaire. Chaque section ci-dessous aborde une étape, ainsi que les schémas qui pénalisent le plus les données du panier et des commandes.

1.0 Pourquoi les tables de paniers et de commandes deviennent le principal goulot d'étranglement
Votre catalogue reste quasiment inchangé. Une fois indexé, ses performances restent sensiblement les mêmes, que vous vendiez 1 000 ou 50 000 références. En revanche, les données du panier et des commandes ne se comportent pas de la même manière, puisqu'elles ne cessent de croître.
Quatre phénomènes s'accumulent avec l'augmentation du trafic :
- Des rangées qui ne partent jamais. Quasiment chaque visiteur qui consulte le panier se voit attribuer une ligne dans ps_cart, y compris les bots. Ces lignes n'expirent pas automatiquement ; ps_cart et ps_cart_product continuent donc d'augmenter longtemps après la fin des sessions.
- Les indices pointent dans la mauvaise direction. Les clés principales privilégient les recherches par id_cart ou id_order, tandis que les rapports, les modules et les filtres d'administration ont tendance à effectuer des recherches par produit, plage de dates ou statut.
- Des boucles au lieu d'ensembles. Les règles du panier, les promotions et les modules mal conçus traitent un article à la fois, ce qui transforme une simple consultation de page en des dizaines d'allers-retours.
- Le back-office s'ajoute à cette expansion. Les écrans de commande assemblent les clients, les états des commandes, les transporteurs, les pays et les magasins, puis trient et paginent les données renvoyées.
Les pages de paiement et d'administration s'affichent donc avant le catalogue. Utilisez ce détail comme indicateur. Les pages produits rapides, situées à côté de la liste de commandes, renvoient directement aux tableaux du panier et des commandes.
2.0 Commencez par le journal des requêtes lentes de PrestaShop
Commencez par ici. Le journal indique ce que votre serveur a réellement fait en situation de trafic réel, et non ce que vous supposez ; aucun autre outil ne vous offre cette information gratuitement.

3.0 Choisissez un seuil adapté à un magasin
Par défaut, long_query_time est fixé à 10 secondes. Pour le commerce de détail, ce chiffre est insignifiant. Une requête de panier de 900 ms reste totalement invisible et vous coûte silencieusement des conversions. Réduisez ce délai et profitez-en pour capturer les requêtes sans index.
# Exemple de configuration my.cnf — à optimiser avant toute application en production.
[Mysqld]
journal_requête_lente = 1
slow_query_log_file = /var/log/mysql/shop-slow-queries.log
long_query_time = 0.5
log_queries_not_using_indexes = 1
limite_min_lignes_examinées = 1000
log_slow_admin_statements = 1
Commencez par 0.5 seconde. Réduisez à 0.1 seconde après avoir éliminé les causes évidentes. Attention à un effet secondaire : log_queries_not_using_indexes détecte également les lectures triviales sur les tables de configuration, et min_examined_row_limit garantit la lisibilité du fichier une fois ces lectures effectuées.
Les modifications en cours d'exécution fonctionnent également, mais attention : `long_query_time` s'applique aux nouvelles connexions. Par conséquent, les processus PHP-FPM utilisant des connexions persistantes conservent l'ancien seuil jusqu'à leur redémarrage. La documentation relative aux journaux de requêtes lentes de MySQL répertorie toutes les variables concernées.
-- Activation à l'exécution. Affecte uniquement les nouvelles connexions.
DÉFINIR GLOBAL slow_query_log = 'ON';
DÉFINIR GLOBAL long_query_time = 0.5 ;
Vous utilisez MariaDB, comme beaucoup d'hébergeurs PrestaShop ? Ajoutez `log_slow_verbosity = query_plan,explain`. Chaque entrée sera alors accompagnée de son plan d'exécution, ce qui vous évitera une analyse ultérieure.
4.0 Transformer le journal brut en une liste classée
Les journaux bruts étant inutilisables en grande quantité, il est conseillé de les agréger par empreinte de requête. mysqldumpslow est fourni avec MySQL et ne nécessite aucune installation.
mysqldumpslow -st -t 20 /var/log/mysql/shop-slow-queries.log # trié par temps total
mysqldumpslow -sc -t 20 /var/log/mysql/shop-slow-queries.log # trié par nombre d'appels
La fonction pt-query-digest de Percona Toolkit remplit parfaitement sa fonction. Les valeurs littérales sont converties en empreintes digitales, les entrées sont classées par temps total et chacune d'elles compare les lignes examinées aux lignes renvoyées. Portez une attention particulière à cette dernière paire : aucune autre méthode ne signale aussi clairement un index manquant.
Voici l'erreur que commettent souvent les équipes : lire le rapport en fonction du temps total plutôt que du pire scénario. Un rapport de trois secondes exécuté deux fois par jour coûte bien moins cher qu'une requête de panier de 90 ms exécutée quarante fois par transaction. Pourtant, la requête problématique est corrigée, et la coûteuse persiste.
Le journal présente une lacune. Une succession rapide de requêtes individuelles n'atteint jamais le seuil de `long_query_time`, et aucune donnée n'est donc enregistrée. Pour détecter ces requêtes, utilisez plutôt le profilage de débogage de PrestaShop sur une copie de test : `_PS_DEBUG_PROFILING_` affiche un panneau indiquant le nombre de requêtes pour chaque page. Des centaines de requêtes sur une page de panier indiquent une boucle infinie, mais pas nécessairement qu'une seule requête est lente.
5.0 Lire le plan avec MySQL EXPLAIN
Une fois la liste restreinte établie, demandez à l'optimiseur ce qu'il compte faire. Il se contentera d'exécuter la commande EXPLAIN, sans plus. Cette instruction n'est jamais exécutée, ce qui garantit la sécurité de son utilisation en production.

EXPLIQUE
SELECT o.id_order, o.reference, o.total_paid, c.lastname
DE ps_orders o
JOIN ps_customer c ON c.id_customer = o.id_customer
OÙ o.date_add >= '2026-01-01 00:00:00'
ET o.current_state = 3
TRIER PAR o.date_add DESC
LIMITE 50 ;
L'essentiel du sens se trouve dans les quatre colonnes :
- type – la méthode d'accès. Les méthodes `const`, `eq_ref` et `ref` sont valides. Les méthodes `range` et `index` sont acceptables. La méthode `ALL` implique une analyse complète de la table, et sur un grand nombre de tables `ps_orders`, cette valeur unique correspond généralement à la valeur recherchée.
- clé – l’index effectivement choisi. Lorsque possible_keys liste des candidats mais que key affiche NULL, l’optimiseur les a rejetés, souvent parce qu’une fonction encapsule la colonne filtrée.
- lignes et filtrées – le volume estimé de données que MySQL doit traiter. Une valeur élevée pour le nombre de lignes, combinée à un faible pourcentage de données filtrées, signifie que la requête lit beaucoup plus de données qu'elle n'en renvoie.
- Extra – les avertissements. L'utilisation de filesort et d'index temporaires signifie qu'aucun index ne satisfait le tri. L'utilisation d'un index est la solution optimale : un index couvrant répond à la requête sans modifier la table.
La documentation de référence de MySQL EXPLAIN recense chaque valeur, et le guide de PlanetScale sur la lecture de la sortie EXPLAIN propose un modèle mental utile : considérez les types d’accès comme un feu tricolore et traitez d’abord les rouges.
6.0 Là où EXPLAIN ANALYZE trouve sa place
À partir de la version 8.0.18, la commande EXPLAIN ANALYZE est également disponible. Cette variante exécute votre requête et affiche les temps d'exécution mesurés ainsi que le nombre d'itérations à côté des estimations, ce qui est extrêmement utile lorsqu'un plan d'exécution semble correct mais que la requête reste très lente. Attention : cette commande s'exécute. Limitez son utilisation aux requêtes SELECT en lecture seule et pendant les heures creuses.
MariaDB propose la même fonctionnalité avec ANALYZE SELECT, qui affiche le nombre réel de lignes à côté du nombre prédit. Un écart important entre les deux indique généralement des statistiques obsolètes. Exécutez ANALYZE TABLE, vérifiez à nouveau les résultats, puis décidez de l'index à créer.
7.0 Modèles courants de requêtes lentes dans les données de panier et de commande
Quatre schémas couvrent la quasi-totalité des problèmes rencontrés ici. Chacun requiert une solution spécifique ; il est donc important d’identifier celui auquel vous êtes confronté avant de manipuler le schéma.
7.1 Les recherches côté produit analysent ps_cart_product
Vérifiez votre schéma avant de tirer des conclusions, car les installations mises à jour diffèrent souvent des installations propres :
AFFICHER CRÉER LA TABLE ps_cart_product;
AFFICHER L'INDEX DE ps_cart_product;
Dans le schéma actuel, la clé primaire de ps_cart_product commence par id_cart. Les lectures côté panier s'effectuent donc déjà par des recherches dans l'index, et l'ajout d'un autre index (id_cart, id_product) n'apporte rien d'autre qu'un ralentissement des écritures.

Les problèmes commencent dans l'autre sens. MySQL lit un index à partir de sa colonne la plus à gauche ; par conséquent, une clé commençant par id_cart ne peut pas répondre à une requête filtrant uniquement sur id_product. Les outils de récupération des paniers abandonnés, les widgets de gestion des stocks et les suppressions groupées de produits fonctionnent tous de cette manière et impliquent tous une analyse complète de la table.
-- Analyse ps_cart_product lorsqu'aucun index ne commence par la colonne product.
SÉLECTIONNER COUNT(DISTINCT cp.id_cart)
DE ps_cart_product cp
JOIN ps_cart c ON c.id_cart = cp.id_cart
OÙ cp.id_product = 4821
ET c.date_upd > DATE_SUB(NOW(), INTERVAL 7 DAY);
L'optimisation de la table panier de PrestaShop repose sur cette distinction. Indexez ce que vos modules interrogent réellement et laissez tel quel ce que le noyau gère déjà.
7.2 Les règles du panier déclenchent des boucles de requêtes N+1
La logique de promotion gère les règles une par une. PrestaShop collecte les règles de panier applicables, calcule chaque valeur successivement, et chaque évaluation vérifie ses propres restrictions par rapport aux tables produit, catégorie, transporteur, groupe et pays. Dix règles actives nécessitent donc dix évaluations, plus leurs recherches imbriquées, à chaque rendu du panier et à chaque actualisation AJAX.
Chaque requête reste rapide, de sorte que le journal des requêtes lentes ne les signale jamais. Le profileur révèle plutôt la tendance : le nombre de requêtes augmente proportionnellement au nombre de règles actives.
Corrigez le problème à la source plutôt qu'avec les index. Désactivez les règles expirées au lieu de les laisser activées avec une date de fin dépassée, fusionnez les promotions qui se chevauchent et privilégiez les règles ciblées aux règles générales du catalogue comportant de longues listes de restrictions.
7.3 Les filtres de date dans les rapports de tri supplantent les index
Encapsuler une colonne dans une fonction rend son index inutilisable. Les tableaux de bord personnalisés et les exportations CSV procèdent constamment ainsi.
-- Lent : la fonction bloque l'accès à l'index.
SELECT COUNT(*) FROM ps_orders WHERE DATE(date_add) = '2026-03-14';
-- Rapide : une plage semi-ouverte permet de garder l'index en jeu.
SÉLECTIONNER COUNT(*) FROM ps_orders
OÙ date_add >= '2026-03-14 00:00:00'
ET date_add < '2026-03-15 00:00:00';
Les filtres YEAR(date_add) et le regroupement DATE_FORMAT() présentent le même défaut. Il est recommandé de les réécrire sous forme d'intervalles partout où vous contrôlez le code. Lorsque cela s'avère impossible (par exemple, dans un module propriétaire), MySQL 8.0.13 et versions ultérieures prennent en charge les clés fonctionnelles, ce qui permet d'indexer l'expression elle-même. MariaDB ne propose pas d'équivalent, bien qu'une colonne générée indexée permette d'obtenir le même résultat.
7.4 Les écrans de commandes du back-office sont trop nombreux.
La liste des commandes d'administration associe les tables ps_orders et ps_customer (état et traduction des commandes), ainsi que les tables transporteur, pays et boutique. Elle concatène ensuite les noms pour la recherche, applique un filtre « J'aime », trie et pagine les résultats.
Deux problèmes en découlent. Le filtrage ou le tri sur une colonne jointe ou traduite, qu'il s'agisse du nom du client, du statut ou du pays, ne peut pas utiliser un index sur ps_orders. MySQL assemble donc d'abord l'ensemble joint, puis le trie. Pire encore, le coût est calculé en fonction du nombre total de commandes et non de la taille de la page, ce qui explique la dégradation progressive de l'affichage à mesure que la boutique se développe.

Certaines mesures d'atténuation ne coûtent rien : réduire la taille de page par défaut, limiter le filtre d'état et former le personnel à effectuer des recherches par référence de commande au lieu de parcourir les pages au hasard.
7.4 Appliquer des correctifs sûrs : stratégie d’indexation qui résiste aux mises à niveau
Les indices viennent en dernier, jamais en premier. Voici l'ordre qui reste valable :
- Réécrivez votre code SQL. Supprimez les fonctions des colonnes filtrées et les jointures inutiles.
- Réduisez la charge de travail. Supprimez les règles relatives aux paniers inactifs, renforcez les filtres d'administration.
- Actualisez les statistiques à l'aide de ANALYZE TABLE, puis exécutez EXPLAIN une seconde fois.
- Ajoutez un seul indice. Mesurez-le avant et après.

Chaque index ralentit les écritures et consomme de l'espace dans le pool de mémoire tampon, et les tables de panier absorbent un trafic d'écriture important. Il est donc conseillé de les ajouter intentionnellement.
-- Exemples. Testez d'abord sur une copie restaurée de la production, puis confirmez avec
-- SHOW INDEX indique qu'un index équivalent n'existe pas déjà.
CRÉER L'INDEX idx_cp_product_cart SUR ps_cart_product (id_product, id_cart);
CRÉER L'INDEX idx_cart_dateupd_shop SUR ps_cart (date_upd, id_shop);
Quelques règles permettent de garantir la sécurité de l'indexation des tables de commandes PrestaShop lors des mises à jour du noyau et des modules :
- Jamais Modifier une clé primaire ou ajouter une contrainte UNIQUE à une table principale peut poser problème. Le code principal dépend de la structure de la clé existante, et un index unique peut empêcher les insertions légitimes lors du paiement.
- Jamais Suppression d'un index principal car une requête de test l'a ignoré. Un autre contrôleur en dépend très probablement.
- Ajoutez un préfixe distinctif aux noms d'index personnalisés afin que le prochain développeur puisse les différencier de ceux du noyau.
- Conservez chaque index personnalisé dans une migration versionnée avec un script de restauration, puis revérifiez après chaque mise à niveau avec SHOW INDEX.
- Créez des index sur les très grandes tables pendant une fenêtre de maintenance, ou utilisez un outil de modification de schéma en ligne, car une commande ALTER TABLE bloquante peut interrompre les extractions en direct.
8.0 Supprimez et archivez les anciennes données de panier avant d'ajouter du matériel.
Les index sont utiles, mais réduire l'ensemble de travail est souvent plus efficace. Les chariots abandonnés contiennent la majeure partie des éléments inutiles ; commencez donc par les inventorier.
-- Lecture seule. Quelle est la part de ps_cart qui représente un poids mort ?
SÉLECTIONNER COUNT(*) AS paniers_abandonnés
DE ps_cart c
LEFT JOIN ps_orders o ON o.id_cart = c.id_cart
OÙ o.id_order EST NULL
ET c.date_upd < DATE_SUB(NOW(), INTERVAL 12 MONTH);
Certaines règles sont non négociables. Ne supprimez jamais un panier contenant une commande, car les écrans de commande et la régénération des factures utilisent toujours ces données. La condition LEFT JOIN … IS NULL ci-dessus vous protège. Supprimez les lignes enfants avant les lignes parents, y compris ps_cart_product, ainsi que toute table de module indexée par id_cart. Effectuez d'abord une sauvegarde, testez sur une copie restaurée, puis procédez aux suppressions en production par petits lots plutôt qu'en une seule transaction pendant les heures d'ouverture.
Conservez les commandes elles-mêmes. Les règles de conservation des factures et des documents fiscaux varient selon les juridictions ; archivez-les plutôt que de les supprimer lorsque vous devez réduire ce nombre.
Les tables se remplissent à nouveau, ce qui signifie qu'un seul nettoyage vous fait gagner des mois, au mieux. Scriptez la purge, intégrez-la à la vérification d'ordre ci-dessus et confiez la tâche à votre planificateur. Saisir manuellement les suppressions de production à 2 h du matin est source d'erreurs. Terminez par une analyse de la table afin que l'optimiseur prenne en compte la nouvelle distribution des lignes.
9.0 Continuez à surveiller, sinon les performances de la base de données PrestaShop se dégradent.
Les régressions s'installent insidieusement. Une mise à jour de module introduit une requête malencontreuse, personne ne s'en aperçoit, et les tickets d'assistance arrivent une semaine plus tard. Les contrôles réguliers sont toujours plus efficaces que la gestion des incidents.

- Programmez le résumé. Exécutez pt-query-digest chaque semaine sur les journaux lents pivotés et comparez les empreintes d'une semaine à l'autre. Les nouvelles entrées permettent de détecter rapidement les régressions.
- Interrogez les tables de synthèse. Le schéma de performance de MySQL agrège chaque instruction en continu, et le schéma sys expose des vues lisibles telles que statement_analysis et statement_with_full_table_scans.
- Requêtes d'attributs vers le code. Un outil APM indique quel hook, contrôleur ou module a émis une requête. Le journal des requêtes lentes mentionne l'instruction ; l'outil APM indique le code à corriger.
- La balance affiche les résultats séparément. PrestaShop lui-même documentation sur la mise à l'échelle Il est recommandé d'utiliser une seule instance d'écriture avec des réplicas de lecture, car le front-office génère beaucoup plus de lectures que d'écritures. Il est également suggéré d'analyser les tables après les importations importantes afin que l'optimiseur fonctionne à partir de statistiques actualisées.
- Donnez de la place à la zone tampon. Si le serveur héberge la base de données, augmentez la valeur de `innodb_buffer_pool_size` jusqu'à ce qu'elle puisse contenir la plus grande partie possible de l'ensemble de travail. Si elle est insuffisante, une boutique PrestaShop à fort trafic atteindra sa limite en quelques jours.
10.0 Conclusion
Identifier les requêtes SQL lentes dans PrestaShop est un travail méthodique, pas une enquête. Enregistrez les requêtes avec un seuil pertinent, triez-les par temps total et exécutez `EXPLAIN` sur les premières. Analysez ensuite le plan d'exécution objectivement. Un type `ALL`, une clé `NULL` ou l'utilisation de `filesort` dans Extra indiquent la nécessité d'une réécriture ou d'un index.
Ensuite, travaillez sur les données elles-mêmes. Supprimez les paniers abandonnés, archivez les anciens enregistrements, vérifiez les améliorations avec de vraies requêtes plutôt qu'avec des tests synthétiques et planifiez les vérifications afin que les régressions soient détectées avant que les clients ne les remarquent. Les boutiques qui suivent ce processus conservent une page de paiement réactive malgré l'augmentation du volume de commandes et une interface d'administration toujours fonctionnelle.
Si vous ne devez faire qu'une seule chose cette semaine, activez le journal des requêtes lentes sur votre boutique la plus fréquentée et consultez les dix premières entrées. Cette liste vous indiquera généralement où concentrer vos efforts de base de données le mois prochain.
Chez Knowband, nous aidons les propriétaires de boutiques PrestaShop à améliorer leurs performances, leur évolutivité et leur stabilité grâce à des plugins PrestaShop fiables, au développement de fonctionnalités personnalisées pour leurs sites web et au développement d'applications.
Besoin d'aide ? Contactez-nous à [email protected] ou consultez le service d'assistance Knowband.

