web-dev-qa-db-fra.com

Avancée WP Requête raccroche le serveur SQL

J'ai récemment développé un site Web pour un client qui est un agent immobilier. Le projet comprend un type de message appelé listing. Ce type de publication est utilisé pour toutes leurs listes et des informations supplémentaires sont stockées dans la table postmeta via l'utilisation de Advanced Custom Fields Pro.

Sur le front-end du site, j'ai développé une barre de filtrage dans laquelle les utilisateurs peuvent sélectionner plusieurs filtres et soumettre le formulaire, qui génère à son tour un WP_Query complexe pour renvoyer les listes correspondantes. Assez simple! Bien que la plupart des recherches fonctionnent parfaitement, certaines plus complexes génèrent une requête SQL qui cache le fil. Lorsque la base de données doit tout effacer, le serveur se bloque jusqu'à ce que quelqu'un tue le processus. Cela se produit sur tous les environnements, pas seulement en production.

Par exemple, voici une requête qui vérifie l'intervalle de prix, les chambres, les salles de bain, que la liste comprend les meubles et l'emplacement (post_id) et le titre du message "1". Ne ressemble pas à une recherche super avancée, n'est-ce pas? (c'est le SQL généré)

SELECT DISTINCT   4x5LbPZ_posts.* FROM 4x5LbPZ_posts  LEFT JOIN 4x5LbPZ_term_relationships ON (4x5LbPZ_posts.ID = 4x5LbPZ_term_relationships.object_id) INNER JOIN 4x5LbPZ_postmeta ON ( 4x5LbPZ_posts.ID = 4x5LbPZ_postmeta.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt1 ON ( 4x5LbPZ_posts.ID = mt1.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt2 ON ( 4x5LbPZ_posts.ID = mt2.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt3 ON ( 4x5LbPZ_posts.ID = mt3.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt4 ON ( 4x5LbPZ_posts.ID = mt4.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt5 ON ( 4x5LbPZ_posts.ID = mt5.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt6 ON ( 4x5LbPZ_posts.ID = mt6.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt7 ON ( 4x5LbPZ_posts.ID = mt7.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt8 ON ( 4x5LbPZ_posts.ID = mt8.post_id )  INNER JOIN 4x5LbPZ_postmeta AS mt9 ON ( 4x5LbPZ_posts.ID = mt9.post_id ) LEFT JOIN 4x5LbPZ_term_relationships AS trel ON (4x5LbPZ_posts.ID = trel.object_id) LEFT JOIN 4x5LbPZ_term_taxonomy AS ttax ON (  ( ttax.taxonomy = 'category' )  AND trel.term_taxonomy_id = ttax.term_taxonomy_id) LEFT JOIN 4x5LbPZ_terms AS tter ON (ttax.term_id = tter.term_id)  LEFT JOIN 4x5LbPZ_postmeta AS m ON (4x5LbPZ_posts.ID = m.post_id)  WHERE 1=1 AND ( ( (
  4x5LbPZ_term_relationships.term_taxonomy_id IN (3)
) AND (((((4x5LbPZ_posts.post_title LIKE '%1%') OR (4x5LbPZ_posts.post_content LIKE '%1%'))) OR ((tter.slug LIKE '%1%'))  OR ((ttax.description LIKE '%1%'))  OR ((m.meta_value LIKE '%1%')) ))  AND (
  ( 4x5LbPZ_postmeta.meta_key = 'listing_status' AND 4x5LbPZ_postmeta.meta_value NOT IN ('pending') )
  AND
  ( mt1.meta_key = 'listing_status' AND mt1.meta_value IN ('for_sale') )
  AND
  ( mt2.meta_key = 'listing_price' AND CAST(mt2.meta_value AS SIGNED) >= '0' )
  AND
  ( mt3.meta_key = 'listing_price' AND CAST(mt3.meta_value AS SIGNED) <= '7500000' )
  AND
  ( mt4.meta_key = 'listing_bedrooms' AND CAST(mt4.meta_value AS SIGNED) >= '0' )
  AND
  ( mt5.meta_key = 'listing_bedrooms' AND CAST(mt5.meta_value AS SIGNED) <= '36' )
  AND
  ( mt6.meta_key = 'listing_bathrooms' AND CAST(mt6.meta_value AS SIGNED) >= '0' )
  AND
  ( mt7.meta_key = 'listing_bathrooms' AND CAST(mt7.meta_value AS SIGNED) <= '7' )
  AND
  ( mt8.meta_key = 'listing_furniture' AND mt8.meta_value = 'true' )
  AND
  ( mt9.meta_key = 'listing_location' AND mt9.meta_value = '80' )
) AND 4x5LbPZ_posts.post_type = 'listing' AND (4x5LbPZ_posts.post_status = 'publish' OR 4x5LbPZ_posts.post_status = 'acf-disabled')) AND post_type != 'revision') AND post_status != 'future' GROUP BY 4x5LbPZ_posts.ID ORDER BY 4x5LbPZ_posts.post_date DESC;

En réalité, je n'utilise que TYPE, KEY, VALUE et COMPARE dans le tableau meta_query, de sorte qu'il est construit comme il se doit, à l'aide de WordPress, mais il devient pourtant incroyablement lent. Lorsque cela se produit, les demandes s'accumulent jusqu'à ce que l'ensemble du site ne réponde plus.

2
Jonathan

Il y a plusieurs options:

  • Utilisez un index de recherche basé sur Lucene et mettez en cache vos requêtes. Utilisez-le avec beaucoup de RAM pour que rien ne vienne frapper votre base de données après la première requête. Utilisez quelque chose comme…
    • Elasticsearch
    • Solr
    • Sphinx
    • N'essayez pas de courir la plaine de Lucene… vous allez vous retrouver en enfer. Personne ne fait ça pour une bonne raison.
  • Réécrivez votre requête et utilisez une requête brute au lieu de \WP_Query.
  • Divisez votre requête en différentes requêtes. Cache le résultat dans un stockage clé/valeur (serveur de base de données).
    • Utilisez Memcached (meh)…
    • ou Redis (mieux, car vous pouvez le mettre en cluster et il se configure mieux avec des installations équilibrées en charge.)
  • La solution la plus simple: écrivez un script de mise à jour qui parcourt vos 300 objets/propriétés. 300 n'est pas beaucoup. Convertissez-le en taxonomie. Facilitez votre vie à long terme.

Recommandations:

  • Journal des requêtes lentes.
  • Ajoutez la surveillance à votre pile.
  • Alertez-vous lorsque les choses se passent mal. Corrigez-le, avant votre client le sait. Informez-les de ce que vous avez corrigé. Avoir des clients heureux depuis longtemps. Découvrez que le CLV est sous-estimé.
2
kaiser