Le WordPress d'aujourd'hui, décodé pour les développeurs

Accessibilité

Indexer wp_postmeta pour stocker 50 000 résultats d’audit sans ralentir le site

Stocker un score et des anomalies par article accessible depuis WordPress, sans transformer chaque requête de tableau de bord en scan de table complet.

Par WordPress Développement • 13 février 2024 • 5 min de lecture • Aucun commentaire
Indexer wp_postmeta pour stocker 50 000 résultats d'audit sans ralentir le site

50 000 pages, un score d’accessibilité par page, une poignée d’anomalies détaillées par score, et une agence qui veut un tableau de bord filtrable par plage de score, par type de contenu et par date d’audit. La tentation naturelle consiste à stocker tout cela dans wp_postmeta, la table native de métadonnées de WordPress, avec des clés comme _a11y_score et _a11y_issues. Le schéma existe déjà, l’API update_post_meta() est familière, aucune migration de base ne semble nécessaire. C’est précisément ce raisonnement qui, à cette échelle, produit des tableaux de bord d’une lenteur insupportable.

Le problème ne vient pas de WordPress lui-même, mais de la structure de wp_postmeta : une table clé-valeur avec les colonnes meta_id, post_id, meta_key et meta_value, où seule la combinaison post_id/meta_key bénéficie d’un index par défaut. Filtrer ou trier par meta_value, par exemple pour lister les pages dont le score est inférieur à 70, revient à demander à MySQL de parcourir potentiellement l’intégralité de la table, puisque meta_value est typée en longtext et n’est indexée nulle part par défaut.

Ce que révèle EXPLAIN sur une requête de tableau de bord

Une requête typique de filtrage par score ressemble à ceci une fois traduite en SQL brut par WP_Query avec un meta_query :

SELECT p.ID FROM wp_posts p
INNER JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = '_a11y_score'
  AND CAST(pm.meta_value AS UNSIGNED) < 70
ORDER BY CAST(pm.meta_value AS UNSIGNED) ASC;

La conversion CAST() appliquée directement dans la clause WHERE empêche MySQL d’exploiter le moindre index sur meta_value, même s’il en existait un : une fonction appliquée à une colonne rend l’index inutilisable pour cette recherche, sauf à créer un index fonctionnel dédié, disponible depuis MySQL 8.0.13. Sur 50 000 lignes de score, associées à d’autres métadonnées WordPress standard dans la même table (souvent plusieurs millions de lignes toutes clés confondues), le scan complet devient perceptible dès la seconde de chargement du tableau de bord.

Deux options structurantes, pas une rustine

L'essentiel à retenir : wp_postmeta n'est pas indexé pour des requêtes par valeur ; Une table dédiée coûte moins cher qu'un index composite mal ciblé ; EXPLAIN reste l'outil de vérification le plus fiable

La première option, la plus rapide à mettre en œuvre, consiste à créer une table personnalisée dédiée aux audits, avec des colonnes typées et indexées dès la conception :

CREATE TABLE wp_a11y_audits (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  post_id BIGINT UNSIGNED NOT NULL,
  score SMALLINT UNSIGNED NOT NULL,
  issues_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  audited_at DATETIME NOT NULL,
  KEY idx_post_id (post_id),
  KEY idx_score (score),
  KEY idx_audited_at (audited_at)
) ENGINE=InnoDB;

Cette table se crée avec dbDelta() lors de l’activation du plugin d’audit, exactement comme WordPress le fait pour ses propres tables. Le score devient une vraie colonne numérique indexée, les filtres et les tris deviennent des opérations directes pour l’optimiseur MySQL, sans conversion de type ni parcours complet.

La seconde option, plus légère si le volume reste raisonnable et si l’agence veut rester strictement dans l’écosystème natif de métadonnées, consiste à ajouter un index composite ciblé directement sur wp_postmeta via une migration :

ALTER TABLE wp_postmeta
  ADD INDEX idx_meta_key_value (meta_key(20), meta_value(20));

Cet index composite accélère les recherches d’égalité et les préfixes, mais reste inefficace pour les comparaisons numériques (<, >, BETWEEN) sur une colonne texte : dès qu’il faut trier 50 000 scores ou filtrer une plage, la table dédiée reprend l’avantage.

Ce qu’il ne faut pas faire

  • Multiplier les index génériques sur meta_value en longtext : MySQL limite la taille indexable et les performances d’écriture se dégradent sur une table déjà volumineuse et partagée par tout WordPress
  • Stocker le détail des anomalies (souvent un tableau JSON de plusieurs kilo-octets) dans meta_value sans compression ni pagination, ce qui alourdit chaque SELECT * déclenché ailleurs sur la même table
  • Interroger la table sans jamais vérifier le plan d’exécution réel avec EXPLAIN, en se fiant uniquement au temps ressenti en environnement de développement, où le volume de données ne reproduit jamais la charge de production

Ce que change une table dédiée pour l’agence

Avec une table propre, le tableau de bord peut interroger directement l’historique des audits, calculer des moyennes par type de contenu, ou isoler les régressions entre deux dates, avec des requêtes qui restent lisibles :

SELECT post_id, score FROM wp_a11y_audits
WHERE score < 70 AND audited_at >= '2024-01-01'
ORDER BY score ASC LIMIT 50;

Le gain n’est pas seulement une question de vitesse perçue : il conditionne la capacité de l’agence à faire évoluer son tableau de bord (nouveaux filtres, nouveaux graphiques d’évolution) sans repartir d’un schéma de données inadapté à l’échelle visée.

Sur nos projets qui dépassent quelques milliers de contenus audités, la règle est simple : dès que meta_query commence à trier ou filtrer numériquement, c’est le signal qu’une table dédiée devient rentable.

Notre verdict

Réutiliser wp_postmeta pour de la donnée strictement liée à un article (une couleur de mise en avant, une préférence d’affichage) reste tout à fait pertinent. Mais dès qu’un volume important de valeurs numériques doit être filtré, trié et agrégé, comme c’est le cas pour des scores d’accessibilité sur 50 000 pages, une table personnalisée indexée dès sa création évite un ralentissement qui, sinon, finit toujours par se manifester au pire moment : celui où le client consulte enfin son tableau de bord.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi