web-dev-qa-db-ja.com

高度な WP クエリがSQLサーバーを占有する

私は最近不動産業者である顧客のためのウェブサイトを開発しました。このプロジェクトに含まれているのはlistingという投稿タイプです。この投稿タイプはすべてのリストに使用され、追加情報はAdvanced Custom Fields Proを使用してpostmetaテーブルに格納されます。

サイトのフロントエンドで、私はユーザーがいくつかのフィルターを通して選択してフォームを送信することができるフィルターバーを開発しました。そしてそれは順番にマッチするリストを返すために複雑なWP_Queryを生成します。とてもシンプル!ほとんどの検索は完璧に機能しますが、より複雑な検索ではスレッドを占有するSQLクエリが生成されます。データベースがすべてをフラッシュする必要があるとき、誰かがプロセスを殺すまでサーバーは本質的にフリーズします。これは本番環境だけでなく、すべての環境で発生します。

たとえば、価格、寝室、浴室の間隔、リストに家具、どの場所(post_id)、および投稿タイトル "1"が含まれているかを確認するクエリがあります。超高度な検索のように聞こえないでしょうか。 (これは生成された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;

meta_query配列には、実際にはTYPEKEYVALUECOMPAREしか使用していないので、WordPressを使用するようにビルドすることができますが、それでも計算が遅くなります。これが発生すると、サイト全体が応答しなくなるまで要求が蓄積されます。

2
Jonathan

いくつかの選択肢があります。

  • Luceneベースの検索インデックスを使用して、そこでクエリをキャッシュします。最初のクエリの後に何もあなたのDBにヒットしないようにたくさんのRAMでそれを動かしてください。のようなものを使う…
    • エラスティックサーチ
    • Solr
    • スフィンクス
    • Luceneを普通に動かそうとしないでください…あなたは地獄にいるのに気づくでしょう。正当な理由でそれをする人は誰もいません。
  • クエリを書き換えて、\WP_Queryの代わりに生のクエリを使用します。
  • クエリをさまざまなクエリに分割します。結果をキー/値ストレージ(db-server)にキャッシュします。
    • Memcached(meh)を使う…
    • またはRedis(クラスタ化することができ、負荷分散インストールの方がより適切に構成できるため、優れています。)
  • 最も簡単な方法は、300個のオブジェクト/状態をループする更新スクリプトを作成することです。 300ほどではありません。それを分類法に変換します。長期的にはあなたの人生を楽にします。

お勧め:

  • 遅いクエリをログに記録します。
  • スタックに監視を追加します。
  • 問題が発生したときには注意してください。それを修正してください、 あなたのクライアントが知っている の前に。あなたが修正したことについて彼らに知らせてください。長い間幸せなクライアントを持っています。 CLVが過小評価されていることを確認してください。
2
kaiser