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

Hébergement & serveurs

Mandats immobiliers : un catalogue qui sature un serveur mutualisé

Huit cents biens en vente, des filtres de recherche multiples, et un mutualisé qui commence à ralentir dès que plusieurs visiteurs consultent le catalogue en même temps.

Par WordPress Développement • 10 novembre 2021 • 5 min de lecture • Aucun commentaire
Mandats immobiliers : un catalogue qui sature un serveur mutualisé

Huit cent quarante fiches de biens, chacune avec une dizaine de champs personnalisés — surface, nombre de pièces, prix, ville, type de mandat — et un formulaire de recherche permettant de combiner plusieurs de ces critères à la fois. Sur le papier, ce volume reste modeste. Dans les faits, le temps de génération d’une page de résultats filtrés dépassait fréquemment la seconde et demie, avec des pics au-delà de trois secondes lors des consultations simultanées.

Le premier réflexe, face à ce type de ralentissement, consiste souvent à conclure que le nombre de fiches dépasse ce qu’un hébergement mutualisé peut raisonnablement absorber. L’analyse des requêtes générées a montré une réalité différente : le volume de données n’était pas en cause, la façon de les interroger l’était.

Ce que révèle l’analyse des requêtes lentes

L’activation du journal des requêtes lentes de MySQL, disponible même sur ce mutualisé via le tableau de bord de l’hébergeur, a mis en évidence des requêtes combinant plusieurs conditions meta_query sur des champs personnalisés non indexés, générées par le moteur de recherche du thème utilisé pour le catalogue.

SELECT wp_posts.* FROM wp_posts
INNER JOIN wp_postmeta AS mt1 ON wp_posts.ID = mt1.post_id
INNER JOIN wp_postmeta AS mt2 ON wp_posts.ID = mt2.post_id
INNER JOIN wp_postmeta AS mt3 ON wp_posts.ID = mt3.post_id
WHERE mt1.meta_key = 'prix' AND mt1.meta_value BETWEEN 150000 AND 300000
AND mt2.meta_key = 'ville' AND mt2.meta_value = 'Angers'
AND mt3.meta_key = 'nb_pieces' AND mt3.meta_value >= 3

Chaque critère de recherche supplémentaire ajoutait une nouvelle jointure sur la table wp_postmeta, une table par nature non optimisée pour ce type de filtrage combiné, puisqu’elle stocke toutes les métadonnées de tous les contenus dans une structure clé-valeur générique.

L'essentiel à retenir : Un grand nombre de fiches ne suffit pas seul à expliquer une lenteur ; Des requêtes meta_query mal indexées coûtent cher à grande échelle ; Un mutualisé peut absorber un catalogue volumineux si les requêtes sont maîtrisées

Pourquoi meta_query devient coûteux à cette échelle

Ce comportement n’est pas propre à cet hébergement : il s’agit d’une limite bien connue de la structure relationnelle de WordPress, où les champs personnalisés stockés via add_post_meta ne bénéficient pas d’index adaptés à des recherches combinées par plage de valeurs. Sur quelques dizaines de fiches, l’effet reste imperceptible. Sur plusieurs centaines, avec des filtres combinés, chaque jointure supplémentaire multiplie le coût de la requête.

Les corrections apportées, par ordre d’impact

  1. Création d’une table personnalisée dédiée aux critères de recherche fréquents (prix, ville, nombre de pièces), avec des index adaptés, alimentée à chaque enregistrement d’une fiche via le hook save_post
  2. Réécriture du formulaire de recherche pour interroger cette table directement via $wpdb, plutôt que de passer par une WP_Query avec meta_query imbriquée
  3. Ajout d’un cache de résultats pour les combinaisons de filtres les plus fréquentes, avec une durée de vie courte compte tenu de la mise à jour régulière du catalogue

Le résultat mesuré après correction

IndicateurAvant correctionAprès correction
Temps moyen de génération1,8 s0,3 s
Requêtes MySQL par recherche filtrée4 à 6 jointures1 requête indexée
Charge PHP-FPM en heure de pointePool régulièrement saturéMarge confortable

Sur un mutualisé, la question n’est presque jamais « combien de fiches puis-je afficher ? » mais « combien de jointures ma requête de recherche génère-t-elle réellement ? ». Un catalogue de mille biens bien indexé consomme souvent moins de ressources qu’un catalogue de cent biens interrogé avec des meta_query combinées.

Le hook retenu pour maintenir la table à jour

La table dédiée n’a d’intérêt que si elle reste synchronisée avec le contenu réel des fiches. Le choix technique s’est porté sur le hook save_post, filtré sur le type de contenu concerné, plutôt que sur une reconstruction périodique complète de la table, jugée trop coûteuse à mesure que le catalogue grandirait :

add_action( 'save_post_bien_immobilier', function ( $post_id ) {
    global $wpdb;
    $wpdb->replace(
        $wpdb->prefix . 'recherche_biens',
        array(
            'post_id'   => $post_id,
            'prix'      => get_post_meta( $post_id, 'prix', true ),
            'ville'     => get_post_meta( $post_id, 'ville', true ),
            'nb_pieces' => get_post_meta( $post_id, 'nb_pieces', true ),
        )
    );
} );

Ce que ce dossier change pour les prochains catalogues

Aucune migration vers un serveur plus puissant n’a été nécessaire pour résoudre ce dossier. La leçon retenue porte sur la conception même du moteur de recherche : dès qu’un catalogue combine plusieurs critères de filtrage sur des champs personnalisés, une table dédiée correctement indexée devient préférable à une accumulation de meta_query, quelle que soit la puissance de l’hébergement sous-jacent.

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