Votre base WordPress rame et vous ne savez plus quoi faire ? Au bout de quelques années d’utilisation, les révisions qui s’accumulent, les métadonnées orphelines et la fragmentation des tables peuvent littéralement plomber les performances de votre site. Dans cet article, on va voir comment remettre MySQL sur les rails avec des techniques avancées de diagnostic, nettoyage et optimisation que j’utilise régulièrement sur mes projets.
Diagnostic et analyse de la base de données
Avant de se lancer dans l’optimisation, il faut d’abord savoir où on va ! Diagnostiquer l’état de votre base WordPress, c’est comme faire un bilan de santé : on identifie les problèmes pour mieux les traiter.
Identifier les tables problématiques
Première étape : repérer les tables qui posent problème. WordPress génère énormément de données au fil du temps, et certaines tables deviennent de véritables gouffres.
Commencez par vérifier la taille de vos tables avec cette requête :
SELECT
table_name AS 'Table',
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'Size (MB)'
FROM information_schema.TABLES
WHERE table_schema = 'votre_db_name'
ORDER BY (data_length + index_length) DESC;
En général, les coupables habituels sont :
- wp_posts (surchargée de révisions et de brouillons)
- wp_options (avec des données d’autoload excessives)
- wp_postmeta (métadonnées qui s’accumulent)
- Les tables de plugins (logs, cache, etc.)
Avec WP-CLI, vous pouvez aussi utiliser wp db size --tables pour avoir une vue d’ensemble rapide.
Mesurer la fragmentation avec SHOW TABLE STATUS
La fragmentation, c’est le fléau des bases MySQL ! Quand vous supprimez des données, MySQL ne réorganise pas automatiquement l’espace disque… résultat : des trous partout.
Utilisez cette commande pour analyser une table :
SHOW TABLE STATUS LIKE 'wp_posts'\G
Les métriques importantes à surveiller :
- Data_length : taille des données réelles
- Index_length : taille des index
- Data_free : espace fragmenté (le problème !)
Si Data_free représente plus de 10% de Data_length + Index_length, il faut optimiser. Par exemple, si vous voyez ça :
Data_length: 52428800 (50 MB)
Index_length: 10485760 (10 MB)
Data_free: 8388608 (8 MB)
Vous avez 8MB de fragmentation sur 60MB total, soit 13% – c’est trop !
Analyser les requêtes lentes avec le slow query log
Les requêtes lentes sont souvent la cause de performances dégradées. Activez le slow query log dans votre configuration MySQL :
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
Après quelques heures, analysez le fichier avec mysqldumpslow :
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
Les requêtes WordPress typiquement problématiques :
- Recherches sans index sur wp_posts
- Jointures complexes sur wp_postmeta
- Requêtes sur wp_options sans limite
Pour analyser une requête spécifique, utilisez EXPLAIN :
EXPLAIN SELECT * FROM wp_posts
WHERE post_status = 'publish'
AND post_type = 'post'
ORDER BY post_date DESC;
Si vous voyez « Using filesort » ou « Using temporary » dans les résultats, c’est mauvais signe ! Bon, maintenant qu’on sait où ça coince, on peut passer au nettoyage…
Techniques de nettoyage et optimisation des données
Bon, maintenant qu’on a diagnostiqué l’état de notre base de données WordPress, il est temps de passer aux choses sérieuses : le nettoyage et l’optimisation. Mais attention, je vais être direct avec vous : sauvegardez votre base avant toute intervention ! C’est la règle numéro un, et croyez-moi, j’ai déjà vu des développeurs le regretter amèrement.
Nettoyage des révisions et contenus obsolètes
Les révisions de posts, c’est utile… jusqu’à un certain point. Au bout de quelques années, elles s’accumulent et alourdissent considérablement votre base. Voici comment faire le ménage intelligemment :
-- Suppression des révisions anciennes (plus de 30 jours)
DELETE FROM wp_posts
WHERE post_status='revision'
AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY);
-- Nettoyage des brouillons auto-sauvegardés
DELETE FROM wp_posts
WHERE post_status='auto-draft'
AND post_date < DATE_SUB(NOW(), INTERVAL 7 DAY);
-- Suppression des posts dans la corbeille (plus de 30 jours)
DELETE FROM wp_posts
WHERE post_status='trash'
AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY);
Personnellement, je garde toujours les révisions des 30 derniers jours par sécurité. C’est un bon compromis entre performance et récupération possible.
Optimisation et réparation des tables
MySQL propose des outils intégrés pour maintenir vos tables en bon état. Voici les commandes essentielles :
-- Optimisation d'une table spécifique
OPTIMIZE TABLE wp_posts;
-- Optimisation de toutes les tables WordPress
OPTIMIZE TABLE wp_posts, wp_postmeta, wp_users, wp_usermeta,
wp_comments, wp_commentmeta, wp_options, wp_terms,
wp_term_taxonomy, wp_term_relationships;
-- Réparation d'une table corrompue
REPAIR TABLE wp_posts;
-- Vérification de l'intégrité
CHECK TABLE wp_posts;
Attention : OPTIMIZE TABLE peut prendre du temps sur de gros volumes et verrouille la table pendant l’opération. Prévoyez une maintenance en dehors des heures de pointe !
Nettoyage des métadonnées orphelines
Les métadonnées orphelines, c’est un fléau silencieux. Elles s’accumulent quand on supprime des posts sans nettoyer leurs métadonnées associées :
-- Suppression des post_meta orphelines
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts wp ON wp.ID = pm.post_id
WHERE wp.ID IS NULL;
-- Nettoyage des user_meta orphelines
DELETE um FROM wp_usermeta um
LEFT JOIN wp_users u ON u.ID = um.user_id
WHERE u.ID IS NULL;
-- Suppression des comment_meta orphelines
DELETE cm FROM wp_commentmeta cm
LEFT JOIN wp_comments c ON c.comment_ID = cm.comment_id
WHERE c.comment_ID IS NULL;
Cette requête utilise un LEFT JOIN pour identifier les métadonnées qui n’ont plus de parent. C’est redoutablement efficace !
Gestion des transients expirés
WordPress utilise les transients pour le cache temporaire, mais ne nettoie pas toujours les expirés automatiquement :
-- Suppression des transients expirés
DELETE FROM wp_options
WHERE option_name LIKE '_transient_timeout_%'
AND option_value < UNIX_TIMESTAMP();
-- Suppression des transients correspondants
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_%'
);
Automatisation avec WP-CLI et PHP
Pour automatiser ces tâches, rien de tel que WP-CLI ou un script PHP personnalisé :
# Nettoyage via WP-CLI
wp db optimize
wp transient delete --expired
wp post delete $(wp post list --post_status=revision --format=ids --posts_per_page=-1)
<?php
// Script PHP pour nettoyage automatisé
function clean_wordpress_database() {
global $wpdb;
// Sauvegarde recommandée ici !
// Nettoyage des révisions anciennes
$wpdb->query(
"DELETE FROM {$wpdb->posts}
WHERE post_status='revision'
AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY)"
);
// Nettoyage des métadonnées orphelines
$wpdb->query(
"DELETE pm FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} wp ON wp.ID = pm.post_id
WHERE wp.ID IS NULL"
);
// Optimisation des tables principales
$wpdb->query("OPTIMIZE TABLE {$wpdb->posts}, {$wpdb->postmeta}");
}
// Exécution via cron WordPress
add_action('wp_cleanup_database', 'clean_wordpress_database');
if (!wp_next_scheduled('wp_cleanup_database')) {
wp_schedule_event(time(), 'weekly', 'wp_cleanup_database');
}
?>
Et je le répète encore : toujours sauvegarder avant d’intervenir ! Un simple mysqldump peut vous éviter des nuits blanches.
Optimisation avancée des index et requêtes
Maintenant que votre base est propre, on passe aux choses sérieuses : l’optimisation des performances ! Car oui, une base WordPress peut vraiment voler si on s’y prend bien. Et ça commence par les index.
Créer des index personnalisés pour WordPress
WordPress utilise des index basiques, mais pour des sites avec beaucoup de contenu, ça ne suffit pas. Prenons un exemple concret : si vous affichez souvent des pages par leur slug ET leur statut, l’index par défaut sur post_name ne sera pas optimal.
Voici comment créer un index composite efficace :
CREATE INDEX idx_post_name_status ON wp_posts(post_name, post_status);
Cet index va considérablement accélérer les requêtes qui filtrent par ces deux critères. Attention cependant : l’ordre des colonnes compte ! Mettez en premier la colonne la plus sélective.
Pour les métadonnées (le vrai fléau des performances WordPress), on peut faire ça :
CREATE INDEX idx_meta_key_value ON wp_postmeta(meta_key, meta_value(50));
Le (50) limite l’index aux 50 premiers caractères de meta_value – parfait pour les requêtes qui cherchent des valeurs spécifiques sans indexer des textes énormes.
Optimiser les requêtes WP_Query gourmandes
Les meta_query et tax_query, c’est pratique… mais ça peut tuer votre serveur ! Voici comment bien s’y prendre.
Au lieu de cette horreur :
$query = new WP_Query([
'meta_query' => [
[
'key' => 'prix',
'value' => 100,
'compare' => '>'
],
[
'key' => 'ville',
'value' => 'Paris'
]
]
]);
Optimisez avec des index spécifiques et limitez les résultats :
$query = new WP_Query([
'posts_per_page' => 20, // TOUJOURS limiter !
'meta_query' => [
'relation' => 'AND',
'prix_clause' => [
'key' => 'prix',
'value' => 100,
'compare' => '>',
'type' => 'NUMERIC'
],
'ville_clause' => [
'key' => 'ville',
'value' => 'Paris'
]
],
'orderby' => [
'prix_clause' => 'DESC'
]
]);
Et créez les index correspondants :
CREATE INDEX idx_prix_meta ON wp_postmeta(meta_key, meta_value) WHERE meta_key = 'prix';
CREATE INDEX idx_ville_meta ON wp_postmeta(meta_key, meta_value(20)) WHERE meta_key = 'ville';
Configurer les paramètres MySQL pour WordPress
WordPress a ses petites particularités, et MySQL doit être configuré en conséquence. Voici les paramètres essentiels dans votre my.cnf :
# Pour WordPress spécifiquement
innodb_buffer_pool_size = 1G # 70% de votre RAM disponible
innodb_log_file_size = 256M # Pour les gros imports/exports
max_connections = 200 # Ajustez selon votre trafic
# Cache des requêtes (attention, controversé !)
query_cache_type = 1
query_cache_size = 128M
query_cache_limit = 2M
# Optimisations InnoDB
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
Pour vérifier que vos optimisations fonctionnent, utilisez EXPLAIN ANALYZE :
EXPLAIN ANALYZE
SELECT p.* FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = 'prix' AND pm.meta_value > '100';
Si vous voyez « Using index » dans le résultat, c’est gagné ! Sinon, revoyez vos index.
Monitoring et maintenance automatisée
Maintenir une base de données WordPress performante, c’est un travail de tous les jours. Et franchement, on n’a pas envie de passer nos weekends à surveiller nos tables ! C’est là que l’automatisation devient votre meilleure amie.
Scripts de surveillance des performances
Pour surveiller efficacement votre base de données, vous avez besoin de scripts qui collectent les bonnes métriques. Voici un script PHP que j’utilise pour logger les performances :
<?php
function log_db_performance() {
global $wpdb;
// Taille des tables principales
$tables_size = $wpdb->get_results("
SELECT table_name,
ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
ORDER BY size_mb DESC
");
// Requêtes lentes
$slow_queries = $wpdb->get_var("SHOW STATUS LIKE 'Slow_queries'");
error_log("DB Performance: " . json_encode([
'timestamp' => current_time('mysql'),
'tables_size' => $tables_size,
'slow_queries' => $slow_queries
]));
}
Ce script surveille la taille des tables et compte les requêtes lentes. Vous pouvez l’intégrer dans un plugin ou l’exécuter via WP-CLI.
Automatisation avec WP-CLI et cron
WP-CLI, c’est vraiment le couteau suisse pour automatiser WordPress. Voici comment j’organise mes tâches de maintenance :
# Nettoyage hebdomadaire le dimanche à 2h
0 2 * * 0 wp db optimize --path=/var/www/html
# Sauvegarde avant maintenance
0 1 * * 0 wp db export /backups/$(date +\%Y\%m\%d)_backup.sql --path=/var/www/html
# Nettoyage des révisions (tous les mois)
0 3 1 * * wp post delete $(wp post list --post_type=revision --format=ids --path=/var/www/html) --path=/var/www/html
Attention : toujours faire une sauvegarde avant toute maintenance automatisée ! C’est une règle d’or que j’ai apprise à mes dépens.
Pour les alertes, voici un script qui vérifie les métriques critiques :
#!/bin/bash
# Check query time
QUERY_TIME=$(mysql -e "SHOW STATUS LIKE 'Slow_queries'" | awk 'NR==2{print $2}')
if [ $QUERY_TIME -gt 100 ]; then
echo "Alert: Too many slow queries ($QUERY_TIME)" | mail admin@monsite.com
fi
Métriques clés à surveiller
Alors, quelles métriques surveiller vraiment ? Après plusieurs années d’optimisation, voici celles que je considère comme critiques :
Temps de réponse des requêtes : Si vous avez régulièrement des requêtes qui prennent plus d’une seconde, c’est un signal d’alarme. Query Monitor est excellent pour ça en développement, mais pour la production, j’utilise plutôt New Relic.
Taille de wp_options : Cette table devient souvent problématique. Si elle dépasse 10MB, il faut creuser. Souvent, ce sont les transients qui s’accumulent ou des plugins mal codés qui stockent trop de données.
Fragmentation des tables : Surveillez le ratio de fragmentation avec cette requête :
SELECT table_name,
ROUND(data_free/1024/1024, 2) AS fragmented_mb
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
AND data_free > 0
ORDER BY fragmented_mb DESC;
Connexions simultanées : Trop de connexions ouvertes peut saturer votre serveur. Surveillez Threads_connected et Max_used_connections.
Pour intégrer tout ça avec des outils professionnels comme New Relic, ajoutez ces lignes dans votre wp-config.php :
// New Relic custom metrics
if (extension_loaded('newrelic')) {
newrelic_custom_metric('Custom/DB/QueryTime', $query_time);
newrelic_custom_metric('Custom/DB/TableSize', $table_size_mb);
}
Et n’oubliez pas : la surveillance sans action, ça ne sert à rien. Définissez des seuils d’alerte et des procédures d’intervention claires !
