it-swarm.com.de

Fortgeschritten WP Abfrage blockiert den SQL Server

Ich habe kürzlich eine Website für einen Kunden entwickelt, der ein Immobilienmakler ist. In dem Projekt ist ein Beitragstyp namens listing enthalten. Dieser Beitragstyp wird für alle Einträge verwendet, und zusätzliche Informationen werden mithilfe von Advanced Custom Fields Pro in der Tabelle postmeta gespeichert.

Am vorderen Ende der Website habe ich eine Filterleiste entwickelt, in der Benutzer durch mehrere Filter auswählen und das Formular senden können. Dadurch wird eine komplexe WP_Query generiert, um die übereinstimmenden Auflistungen zurückzugeben. Einfach genug! Während die meisten Suchvorgänge einwandfrei funktionieren, erzeugen einige komplexere eine SQL-Abfrage, die den Thread blockiert. Wenn die Datenbank geleert werden muss, stapelt sich alles und der Server friert im Wesentlichen ein, bis jemand den Prozess beendet. Dies geschieht in allen Umgebungen, nicht nur in der Produktion.

Hier ist zum Beispiel eine Abfrage, die das Preisintervall, die Schlafzimmer, die Badezimmer, die Möbel und den Ort (post_id) sowie den Post-Titel "1" überprüft. Klingt nicht nach einer super erweiterten Suche, oder? (Dies ist die generierte SQL)

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;

Ich benutze wirklich nur TYPE, KEY, VALUE und COMPARE im meta_query-Array, so dass es so aufgebaut ist, wie es mit WordPress sein sollte, aber es wird dennoch unglaublich langsam. In diesem Fall werden die Anforderungen so lange aufgebaut, bis die gesamte Site nicht mehr reagiert.

2
Jonathan

Es gibt verschiedene Möglichkeiten:

  • Verwenden Sie einen Lucene-basierten Suchindex und speichern Sie Ihre Abfragen dort. Versorgen Sie es mit viel RAM, damit nach der ersten Abfrage nichts in Ihre Datenbank passt. Verwenden Sie etwas wie…
    • Elasticsearch
    • Solr
    • Sphinx
    • Versuchen Sie nicht, Lucene Ebene laufen ... Sie werden sich in der Hölle finden. Das tut niemand aus gutem Grund.
  • Schreiben Sie Ihre Abfrage neu und verwenden Sie eine unformatierte Abfrage anstelle von \WP_Query.
  • Teilen Sie Ihre Abfrage in verschiedene Abfragen auf. Cache das Ergebnis in einem Schlüssel/Wert-Speicher (DB-Server).
    • Benutze Memcached (meh)…
    • oder Redis (besser, da Sie es gruppieren können und es mit Installationen mit Lastenausgleich besser konfiguriert wird.)
  • Der einfachste Ausweg: Schreiben Sie ein Update-Skript, das Ihre 300 Objekte/Anwesen durchläuft. 300 ist nicht viel. Wandle es in eine Taxonomie um. Erleichtern Sie Ihrem Leben auf lange Sicht.

Empfehlungen:

  • Langsame Abfragen protokollieren.
  • Erweitern Sie Ihren Stack um Monitoring.
  • Machen Sie auf sich aufmerksam, wenn etwas los ist. Repariere es, bevor dein Klient weiß. Teilen Sie ihnen mit, was Sie behoben haben. Haben Sie lange Zeit zufriedene Kunden. Finden Sie heraus, dass CLV unterschätzt wird.
2
kaiser