注意!! この投稿は2年と5ヶ月くらい前に公開したものです。
そのため正常に動作しないかもしれないので、ご注意ください。

10upのWordPressのベストプラクティスから「効率的なデータベースクエリー」の章を訳してみた

10up Engineering Best Practicesの「Efficient Database Queries(効率的なデータベースクエリー)」を訳してみた。おかしなところ、より良い訳、誤字脱字がありましたらお知らせいただきたく候。

なお、この訳文のライセンスは原文と同じくMITです

※冒頭の「パフォーマンス」の部分を追加で訳しました。さんきゅー、ヒッシー!

パフォーマンス

効率の良いコードを書くのは、特にエンタープライズのレベルでは、極めて重要です。高トラフィックな状況用に確実にコードを最適化するため、私たちが採用しなければならない方略やベストプラクティスは多くあります。

効率的なデータベースクエリー

WordPressでクエリーを行う場合、通常はWP_Query オブジェクトを使うべきです。WP_Query オブジェクトは多くの有用な引き数を取り、get_posts()などの他のデータベースにアスセスするメソッドが行わないようなことを裏で行っています。

いくつか重要なポイントをご紹介しましょう:

  • posts_per_page => -1 は使わない。

これはパフォーマンスの低下を招く原因になります。もし10万件の投稿記事があったらどうでしょう?そのサイトをクラッシュさせるかもしれません。例えばもしウィジェットを作成していてあるカスタム投稿タイプのすべてを取得したい場合、状況に応じて適切な上限を決めましょう。

<?php
// Query for 500 posts.
new WP_Query( array(
  'posts_per_page' => 500,
));
?>
  • 相応の理由がない限りは $wpdb もしくは get_posts() を使わない。

じっさいのところget_posts()WP_Queryを呼び出していますが、get_posts() を直接呼び出すとデフォルトではたくさんのフィルターを迂回してしまいます。こうしたものが必要かどうか分からないですか?おそらく必要ではないでしょう。

  • クエリー結果をページ分割する予定がなければ、常に no_found_rows => trueWP_Query に渡すようにする。

これによりWordPressがSQLクエリー上で SQL_CALC_FOUND_ROWS を実行しなようにし、クエリーを劇的にスピードアップします。 SQL_CALC_FOUND_ROWS は、ページ分割用の総ページ数を得るために必要とされるクエリーで列の総数を計算します。

<?php
// Skip SQL_CALC_FOUND_ROWS for performance (no pagination).
new WP_Query( array(
  'no_found_rows' => true,
));
?>
  • タクソノミー は投稿をグループ化もしくは分類するためのツールである。

ポストメタは特定の投稿についての一意な情報を保存します。ポストメタはこのように保存されるため、効率的な投稿の検索には向きません。通常はポストメタで投稿を検索するのは避けるべきです(時には避けられないこともありますが)。使わざるを得ない場合は、メインクエリーではなく、キャッシュされるようにしましょう。

  • cache_results => falseWP_Query に渡すのは通常は良い考えではない。

cache_results => true の場合(キャッシングが有効でオブジェクトキャッシュがセットアップされていれはデフォルトでtrueです)、WP_Query はその他で見つかった投稿をキャッシュします。cache_results => false を使うのが理にかなっているのはとてもまれな場合でしょう(WP-CLIコマンドではありえます)。

  • 多次元クエリーは避けるべき。

多次元クエリーの例には以下が含まれます:

  • 複数のタクソノミーにまたがるタームをベースにした投稿のためにクエリー
  • 複数のポストメタキーへのクエリー

クエリーの追加の次元ごとに追加のデータベーステーブルがジョインされます。その代わりに、クエリーは可能な限り次元の数は最小にし、必要な結果はPHPを使ってフィルタリングするようにします。

以下は2次元クエリーの例です:

<?php
// Query for posts with both a particular category and tag.
new WP_Query( array(
  'category_name' => 'cat-slug',
  'tag' => 'tag-slug',
));
?>