Version audio :
Table des matières :
- Quand la base de données WordPress devient le goulot d’étranglement (et pas “le thème”)
- Diagnostic MySQL : mesurer avant de bricoler (sinon c’est du folklore)
- WordPress côté données : là où ça gonfle (et où ça finit par casser)
- Diagnostic “données” : repérer les tables et requêtes toxiques
- Réparation MySQL : quand ce n’est plus “lent” mais “cassé”
- Optimisation MySQL/InnoDB pour WordPress : les leviers qui marchent (pas les incantations)
- Runbook de maintenance : rendre l’optimisation répétable (sinon vous allez souffrir à nouveau)
Quand la base de données WordPress devient le goulot d’étranglement (et pas “le thème”)
Un WordPress qui rame, c’est presque toujours la même histoire : TTFB qui explose, admin qui freeze sur “Mise à jour…”, requêtes AJAX interminables, et un CPU qui fait du cardio à chaque visite. Oui, parfois c’est un plugin qui fait n’importe quoi. Mais très souvent, le vrai coupable est plus banal et plus sournois : une base MySQL mal entretenue, surdimensionnée en métadonnées, et sous-indexée.
Dans WordPress, tout finit en base. Les contenus (wp_posts), les taxonomies, les users, les options (wp_options), et surtout la décharge publique qu’est wp_postmeta (et ses cousins WooCommerce). À chaque page, WordPress enchaîne des SELECT, des JOIN, du tri, et parfois des scans complets de tables parce que “un index, c’est surfait”. Sur un site e-commerce, ajoutez le panier, les sessions, les hooks… et vous obtenez une charge MySQL bien plus significative que ce que les gens imaginent.
Ce qui trompe souvent, c’est que “ça marche”… jusqu’au jour où :
- une table de meta passe de 300k à 3M de lignes,
- un plugin stocke des blobs dans
wp_options, - une recherche en admin déclenche un
LIKE '%...'sur une colonne non indexée, - un pic marketing (newsletter, ads, Black Friday) transforme un “petit problème” en incident.
Pour cadrer rapidement, voici un pense-bête utile avant de partir à la chasse aux plugins :
| Symptôme | Indice MySQL fréquent | Ce que vous cherchez |
|---|---|---|
| TTFB élevé et surtout instable | pics de latence + Threads_running |
saturation / verrous / manque de cache |
| Admin lente (listes, filtres) | requêtes lourdes sur wp_posts/postmeta |
manque d’index + rows_examined énorme |
| “Mise à jour…” interminable | écritures et verrous | tables trop grosses, index mal choisis, I/O |
| CPU DB haut, disque pas forcément | scans + tri (filesort) |
requêtes non sélectives, tri sur colonnes non indexées |
| CPU bas, mais latence élevée | I/O + buffer pool insuffisant | reads disque, temp tables sur disque |
Définition rapide (pour cadrer) : une optimisation base de données WordPress n’est pas “cliquer sur Optimiser dans phpMyAdmin”. C’est un ensemble de pratiques : diagnostic (mesurer, tracer, identifier), réparation MySQL (corriger corruption et incohérences), et tuning (schéma, index, moteur, configuration, cache). Si vous sautez l’étape “diagnostic”, vous faites du tuning à l’aveugle — et ça finit souvent en “on a augmenté la RAM donc on a gagné… jusqu’à la prochaine chute”.
Diagnostic MySQL : mesurer avant de bricoler (sinon c’est du folklore)
Commencez par instrumenter. Le principe du slow query log est simple : journaliser les requêtes qui dépassent un seuil de durée.
En pratique :
- activez-le en production sur une fenêtre maîtrisée (ex. quelques heures en période représentative, ou pendant un pic contrôlé) ;
- choisissez un seuil réaliste : par exemple 200–500 ms sur un WordPress “standard”, et plutôt 50–150 ms sur une infra déjà bien optimisée (PHP-FPM réglé, cache objet, DB dimensionnée) ;
- activez
log_queries_not_using_indexesuniquement pour une courte fenêtre (sinon vous allez loguer la planète et remplir le disque).
Deux points “terrain” qui évitent des faux diagnostics :
- Rotation et rétention des logs : un slow log qui grossit sans rotation devient… un problème disque, donc un problème MySQL, donc un problème WordPress. Planifiez une rotation (logrotate côté Linux, ou mécanisme équivalent selon votre distro).
- Distinguer latence DB et latence réseau : si votre serveur web est en France (Paris) et la DB dans une autre région (ou chez un autre hébergeur), quelques millisecondes de RTT se multiplient vite par 100 requêtes. Avant de “tuner” MySQL, mesurez les allers-retours réseau et le nombre de requêtes par page.
Ensuite, corrélez côté applicatif. Le couple “slow query log + APM” permet de répondre à la seule question qui compte : quelle requête, déclenchée par quelle page, à quel moment ? Si vous avez déjà une démarche d’observabilité, vous pouvez pousser plus loin avec des traces et métriques (latences, erreurs, saturation). Pour industrialiser ça proprement, les approches décrites dans OpenTelemetry : unifier métriques, traces et logs pour l’observabilité sont directement transposables à un stack PHP/MySQL.
Côté WordPress pur, un outil très utile pour relier “page → requêtes” (surtout en pré-prod) est le plugin Query Monitor : Query Monitor sur WordPress.org. Il aide à repérer les requêtes lentes, les appels AJAX, les hooks trop bavards, et les pages admin qui déclenchent des monstres SQL. (À éviter sur un gros trafic en prod si vous ne maîtrisez pas l’impact, mais excellent en staging ou en session d’audit contrôlée.)
Troisième étape : regarder MySQL par MySQL. performance_schema (activé correctement) et le schéma sys donnent des vues utiles : top digest, waits, mutex, I/O. Ajoutez des métriques d’infra : buffer pool hit ratio, temp tables on disk, threads running, lock time, Innodb_buffer_pool_reads vs Innodb_buffer_pool_read_requests, etc. Si vous êtes dans une logique SRE, vous pouvez exposer tout ça via mysqld_exporter et l’intégrer à Prometheus ; le sujet est bien posé dans Prometheus monitoring : quickstart en 5 minutes et mise en production.
Enfin, ne confondez pas “site lent” et “MySQL lent”. Mesurez le ressenti utilisateur. Un RUM (Real User Monitoring) vous dira si le problème est surtout réseau, JS, CDN, ou backend. Pour cadrer l’approche, voir Real User Monitoring : RUM vs monitoring synthétique pour la performance web. Spoiler : quand le TTFB est mauvais et fluctuant, la base revient souvent dans la discussion — mais ce n’est pas toujours elle seule.
Le comportement documenté de l’Options API est utile à connaître : Options API (developer.wordpress.org).
Enfin, pour identifier les pires requêtes à grande échelle, utilisez pt-query-digest (Percona Toolkit) sur vos slow logs. Vous obtenez la vraie hiérarchie : total time, avg time, percentiles, rows examined. Pas un ressenti, des chiffres.
WordPress côté données : là où ça gonfle (et où ça finit par casser)
Premier classique : wp_options et le champ autoload. WordPress charge automatiquement les options marquées en autoload (c’est un comportement documenté dans l’Options API) : Options API (developer.wordpress.org)
Traduction opérationnelle : si vos plugins stockent 5–20 Mo en autoload (oui, ça arrive), vous imposez un coût fixe à toutes les pages, même celles qui n’ont aucun besoin de ces options. Diagnostic rapide :
SELECT SUM(LENGTH(option_value)) AS autoload_bytes
FROM wp_options
WHERE autoload='yes';
SELECT option_name, LENGTH(option_value) AS bytes
FROM wp_options
WHERE autoload='yes'
ORDER BY bytes DESC
LIMIT 20;
Remédiation (prudente) : quand une option énorme est chargée partout “par défaut”, posez-vous deux questions avant de la modifier :
- À quoi sert-elle ? (plugin, thème, cache, builder…)
- Est-elle nécessaire sur toutes les pages ?
Si non, un correctif courant consiste à basculer l’option enautoload='no'après validation (staging + test fonctionnel). Exemple (à adapter, et à faire après sauvegarde) :
UPDATE wp_options
SET autoload='no'
WHERE option_name='nom_option_cible';
Deuxième bombe à retardement : wp_postmeta. Les meta non normalisées, les clés arbitraires, et les requêtes meta_query font plaisir aux développeurs pressés, mais MySQL déteste ça. Cas réel (e-commerce WooCommerce) : 2,5 millions de lignes dans wp_postmeta, index par défaut insuffisant, et des pages catégorie qui passent de 300 ms à 3–5 s sous charge. Résultat : CPU DB à 90% à chaque pic de trafic. La solution n’est pas “plus de cache” (même si ça aide), mais réduire les meta inutiles, indexer intelligemment (avec prudence), et parfois refondre certaines requêtes (ou éviter des filtres meta sur des champs texte).
Une source fréquente d’inflation “invisible” sur des sites FR/UE : les plugins qui stockent des traces, des paniers abandonnés, des événements, ou des journaux marketing en base par défaut. Ce n’est pas illégitime, mais si ce sont des données volumineuses, elles méritent souvent une table dédiée (ou une solution de logs), pas wp_options et wp_postmeta.
Troisième source d’obésité : révisions, brouillons auto, transients expirés, logs applicatifs stockés en DB (le grand classique : “on a mis les logs dans wp_options, c’est pratique”). Sur WordPress, l’hygiène passe par une politique :
- limiter
WP_POST_REVISIONS(selon votre usage éditorial), - purger les transients expirés,
- auditer les tables spécifiques plugins (beaucoup créent leurs propres tables, parfois sans index utiles).
Si vous n’avez pas de démarche globale de maintenance, vous allez rejouer cette scène tous les trimestres. Pour remettre les responsabilités au bon endroit (et arrêter de confondre “maintenance” avec “mise à jour de thème”), lisez De l’importance de la maintenance sur WordPress.
Diagnostic “données” : repérer les tables et requêtes toxiques
Commencez par une cartographie des tailles et fragmentation. Sans poésie : information_schema.tables est votre ami. L’objectif est de repérer 1) les tables hors normes, 2) les tables qui grossissent anormalement vite, 3) les moteurs (InnoDB vs MyISAM), 4) l’écart data_length vs index_length.
SELECT table_name,
engine,
table_rows,
ROUND((data_length+index_length)/1024/1024, 1) AS size_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length+index_length) DESC
LIMIT 20;
Ajoutez deux requêtes très utiles dans un audit WordPress “classique” :
1) Transients (souvent oubliés) : sur un site sans cache objet persistant, beaucoup de transients sont stockés dans wp_options. Les expirés s’accumulent parfois si le nettoyage n’est pas correctement exécuté (cron bloqué, trafic faible, etc.).
SELECT COUNT(*) AS transients
FROM wp_options
WHERE option_name LIKE '\_transient\_%';
SELECT COUNT(*) AS expired_transients
FROM wp_options
WHERE option_name LIKE '\_transient\_timeout\_%'
AND option_value < UNIX_TIMESTAMP();
2) Orphelins dans wp_postmeta (symptôme d’imports/exports, suppressions, ou plugins qui “oublient” de nettoyer) :
SELECT COUNT(*) AS orphan_postmeta
FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;
Ensuite, cherchez les patterns WordPress qui causent du full scan. Exemple typique : requêtes sur meta_value (texte long) sans contrainte, ou LIKE '%...%' sur des colonnes non indexées. Pour éviter de “deviner”, prenez une requête lente réelle et passez-la au révélateur :
EXPLAINpour voir l’index (ou l’absence d’index) et l’estimation de lignes,- et, si vous êtes en MySQL 8,
EXPLAIN ANALYZE(quand possible) pour mesurer l’exécution réelle.
Enfin, pour identifier les pires requêtes à grande échelle, utilisez pt-query-digest (Percona Toolkit) sur vos slow logs :
Mini-scenario typique (et très réaliste) : une page “catégorie” WooCommerce devient lente uniquement quand on active un filtre (taille/couleur/prix). Le slow log montre une requête avec JOIN wp_postmeta multiplié, ORDER BY et LIMIT. rows_examined est énorme parce que MySQL scanne des meta_values texte et trie en mémoire, puis bascule en temp table sur disque quand tmp_table_size est trop bas ou que la requête génère trop de lignes. Là, “augmenter le CPU” ne résout pas la cause : c’est la structure de données et les index (ou l’approche fonctionnelle du filtre) qu’il faut revoir.
Enfin, ne négligez pas l’effet “admin”. WooCommerce, WPML, constructeurs de pages… l’interface d’admin déclenche parfois des requêtes monstrueuses (listes paginées, filtres, recherches). Le trafic admin est faible, donc on l’ignore… jusqu’au jour où votre équipe marketing a 12 onglets ouverts le jour du Black Friday. Pour cadrer une démarche d’audit complète (perf, SEO, sécu), l’article Audit de site web : technique, SEO, UX et sécurité pour une roadmap pose une méthode que vous pouvez appliquer au volet base de données.
Réparation MySQL : quand ce n’est plus “lent” mais “cassé”
La réparation MySQL sur WordPress, c’est le moment où l’on arrête de faire des optimisations “sympas” et où l’on traite des erreurs : tables marquées comme crashed, incohérences d’index, corruption après panne disque, ou crash mysqld après OOM.
Signaux d’alerte concrets (côté logs MySQL / WordPress) :
- erreurs “table is marked as crashed” (souvent MyISAM),
- erreurs InnoDB répétées au démarrage,
- crash
mysqldpendant des écritures (import massif, mise à jour WooCommerce), - lenteurs extrêmes après un incident infra (disque plein, FS en read-only, reboot sauvage).
Première règle : backup avant tout (dump logique + snapshot si possible). Et non, “on a un plugin de sauvegarde” n’est pas un plan de reprise. À minima, assurez-vous de pouvoir restaurer (test en staging) : un backup non testé est une théorie.
Pour un check/repair “soft”, utilisez les outils standards. En CLI :
mysqlcheck -u root -p --all-databases --check
mysqlcheck -u root -p --databases wp_db --auto-repair
Côté WordPress, WP-CLI peut automatiser une partie :
wp db check
wp db repair
wp db optimize
Attention : REPAIR TABLE est surtout pertinent pour MyISAM. Sur InnoDB, on est plutôt sur CHECK TABLE, ANALYZE TABLE, et parfois reconstruction (ALTER TABLE ... ENGINE=InnoDB) selon les cas.
Autre point pratique : certaines opérations “d’entretien” sont bloquantes ou coûteuses (rebuild de table, gros ALTER TABLE). Sur un site en production, planifiez une fenêtre, ou utilisez des approches online quand c’est possible (selon version MySQL/MariaDB, taille, contraintes, et outils).
Quand c’est réellement sale (InnoDB corrompu), on entre dans la zone où l’ego doit sortir de la pièce. innodb_force_recovery peut permettre de démarrer pour extraire les données, mais chaque niveau augmente le risque de perte/corruption logique. La procédure raisonnable : 1) passer en read-only, 2) exporter ce qui est exportable, 3) reconstruire une instance propre, 4) réimporter et vérifier (tests applicatifs, et pas seulement “ça démarre”). Si vous êtes sur une infra critique, ce genre d’opération doit être cadré comme un incident MCO/MCS (et pas comme “un mardi soir à 23h”). À ce sujet, les pages MCO/MCS – Maintien en Conditions Opérationnelles / Maintien en Conditions de Sécurité et Urgence cybersécurité sont utiles pour cadrer les responsabilités et les délais.
Optimisation MySQL/InnoDB pour WordPress : les leviers qui marchent (pas les incantations)
Côté MySQL, WordPress aime InnoDB, et InnoDB aime la RAM. Le levier n°1 est souvent innodb_buffer_pool_size : si votre base fait 5–20 Go et que vous donnez 512 Mo de buffer pool, vous forcez des reads disque et vous vous étonnez que ça “gratte”. Visez un buffer pool dimensionné (souvent 60–75% de la RAM sur un serveur DB dédié), puis observez : hit ratio, pages dirty, flushing. L’objectif est de stabiliser la latence, pas de gagner 3 ms en micro-benchmark.
Deuxième levier : indexation… mais pas à l’aveugle. Sur WordPress, ajouter un index sur (meta_key, meta_value) “parce qu’on a lu ça sur un forum” peut exploser la taille des index et ralentir les écritures. On indexe à partir des requêtes réelles (slow log + digest). Exemple de gain classique : index composite sur une table de plugin qui filtre toujours WHERE site_id=? AND created_at>?. Dans un cas WooCommerce, indexer correctement une table de sessions peut diviser par 10 le rows_examined et faire passer une requête de 800 ms à 60–120 ms sous charge. La règle : mesurer → indexer → re-mesurer.
Un guide de décision simple avant d’ajouter un index :
- Est-ce que la requête apparaît souvent (fréquence) ?
- Est-ce qu’elle coûte cher (temps total,
rows_examined) ? - Est-ce que la colonne filtrée est suffisamment sélective ?
- Est-ce que l’index va pénaliser des écritures très fréquentes ?
- Est-ce que l’index correspond à l’ordre des conditions (
WHERE) et/ou auORDER BY?
Troisième levier : réduire la charge DB au niveau applicatif, sans se raconter d’histoires. Un object cache persistant (Redis/Memcached) réduit drastiquement les lectures répétées (options, résultats de requêtes, fragments). Ce n’est pas magique : si votre code fait 200 requêtes uniques par page, vous resterez lent. Mais sur WordPress, beaucoup de requêtes sont répétitives (menus, options, taxonomies), donc le ROI est souvent élevé.
Quatrième levier (souvent sous-estimé) : éviter les écritures inutiles.
- WP-Cron déclenché à chaque visite peut générer des écritures et des verrous en heure de pointe ; le basculer vers un cron système est souvent plus stable.
- Certains plugins écrivent des statistiques à chaque pageview : utile, mais ça se traite mieux via batch, queue, ou table dédiée.
Et si vous êtes dans une approche performance plus globale (front + back), vous pouvez compléter avec du CDN ; pour situer le sujet, Cloudflare : notre avis sur cet opérateur CDN donne un bon cadre (et non, un CDN ne répare pas une requête SQL en full scan).
Enfin, un rappel versioning qui évite des impasses : sur MySQL 8, il n’y a plus de Query Cache (supprimé), donc tout “tuning” qui tourne autour de ça est obsolète. Concentrez-vous sur InnoDB, les index, et le cache applicatif.
Runbook de maintenance : rendre l’optimisation répétable (sinon vous allez souffrir à nouveau)
La meilleure optimisation base de données WordPress est celle qui survit à votre turnover. Concrètement : mettez en place un runbook avec fenêtres de maintenance, seuils d’alerting, procédures de backup/restore testées, et tâches planifiées. Exemple simple : purge mensuelle des révisions et transients expirés, audit trimestriel des autoloads, contrôle hebdo des top slow queries. Oui, ce n’est pas “sexy”. Non, ce n’est pas optionnel.
Un format efficace (copiable dans un wiki d’équipe) :
| Périodicité | Tâche | Objectif | Outil / sortie attendue |
|---|---|---|---|
| Hebdo | Revue slow queries (top 10) | supprimer les “régressions” | slow log + digest (Percona) |
| Mensuel | Audit autoload |
éviter coût fixe par page | top 20 options autoload |
| Mensuel | Purge transients expirés | limiter wp_options |
requêtes SQL + vérif applicative |
| Trimestriel | Cartographie tailles de tables | détecter croissance anormale | information_schema.tables |
| Trimestriel | Test restore en staging | valider PRA | restauration + tests fonctionnels |
| Continu | Alerting DB (SLO) | prévenir avant incident | Prometheus/Grafana |
Côté monitoring, définissez des SLO techniques : p95 des requêtes MySQL, temps moyen des top 10 digests, taux de temp tables sur disque, nombre de verrous, Threads_running, espace disque, et erreurs InnoDB. Branchez ça à une stack d’observabilité (Prometheus/Grafana, logs centralisés). Et, idéalement, corrélez avec des métriques business (checkout success rate, temps panier) parce que l’e-commerce ne vit pas de SHOW GLOBAL STATUS. Sur l’industrialisation DevOps, la page DevOps : création et optimisation d’infrastructures d’hébergement et l’article VPS Linux : guide de déploiement, durcissement et maintenance proactive aident à formaliser les bases (comptes, firewall, backups, mises à jour, supervision).
Dernier point, parce qu’on parle quand même d’une base de données : sécurité. MySQL exposé sur Internet, comptes trop permissifs, pas de TLS, dumps traînant sur /tmp… c’est le bingo.
- appliquez le moindre privilège (un user WordPress n’a pas besoin de
GRANT) ; - limitez l’exposition réseau (bind local/VPC, firewall) ;
- chiffrez et protégez les sauvegardes (rotation + accès restreint) ;
- et si vous opérez en contexte RGPD (très fréquent en France/UE), documentez où vont les dumps et qui y a accès. En complément, le guide CNIL “sécurité des données personnelles” est une référence utile pour cadrer les mesures organisationnelles et techniques : Guide CNIL : sécurité des données personnelles
Pour un cadrage plus global des erreurs courantes, Sécurité web : 5 erreurs à éviter pour PME et ETI en 2025 reste tristement actuel en 2026.
Si vous voulez une morale : le diagnostic et la réparation MySQL sur WordPress, ce n’est pas une “opération ponctuelle”, c’est un cycle. Mesurer, corriger, prévenir. Et si votre site est un actif business (e-commerce, génération de leads, média), alors traiter la base comme un composant critique n’est pas une lubie d’admin sys : c’est juste du bon sens — celui qui évite de découvrir vos problèmes… quand Google et vos clients les ont déjà trouvés.