【テクニカル・上級編】初心者向け:get_postsとWP_Query、どちらを使うべき?クエリ発行の観点から比較 – WordPress 内部コア・データベース構造とパフォーマンス最適化解析バイブル

WP_Queryとget_postsの低レイヤ比較:MySQLオプティマイザを欺くクエリ生成の真実

WordPressのコアアーキテクチャにおいて、データベースとのインタラクションはパフォーマンスのボトルネックになりやすい。特に`WP_Query`クラスと、そのラッパーである`get_posts()`関数の内部挙動の違いを理解していない開発者は多い。

「どちらを使っても同じSQLが生成されるのだろう」という安易な認識は、大規模なトラフィックを扱うシステムにおいて致命的なメモリリークやクエリ遅延を引き起こす。

本稿では、PHPランタイムのメモリ消費量、WordPressのオブジェクトキャッシュ層、そしてMySQLのEXPLAINプランに至るまで、両者の内部メカニズムを徹底的に解剖する。

—

1. 内部アーキテクチャの比較:ラッパーと本体の乖離

まず、大前提として知るべき事実は、`get_posts()`は内部で`WP_Query`のインスタンスを生成しているという点だ。ソースコード(`wp-includes/post.php`)を覗けば一目瞭然だが、`get_posts()`の実体は以下のようになっている。

function get_posts( $args = null ) {
$defaults = array(
‘numberposts’ => 5,
‘offset’ => 0,
‘category’ => 0,
‘orderby’ => ‘date’,
‘order’ => ‘DESC’,
‘include’ => ”,
‘exclude’ => ”,
‘meta_key’ => ”,
‘meta_value’ => ”,
‘post_type’ => ‘post’,
‘suppress_filters’ => true, // <-- ここに注目 ); // 引数のマージとパース処理... $the_query = new WP_Query(); return $the_query->query( $parsed_args );
}

一見すると、単なるエイリアス関数に見える。しかし、両者にはデフォルト引数の設計思想とフックの実行制御において、パフォーマンス上の重大な差異が存在する。

`suppress_filters` の罠

`get_posts()`の最大の特異性は、デフォルトで `’suppress_filters’ => true` が設定されている点である。これにより、`posts_selection` などのデータベースクエリ関連のフィルターフックが意図的にバイパスされる。

一方、`new WP_Query()` を直接インスタンス化した場合は、デフォルトで `’suppress_filters’ => false` となる。これにより、WPMLやPolyglotなどの多言語化プラグイン、あるいは高度なカスタムタクソノミー制御プラグインが発行するフックがすべて有効化され、意図しないJOIN句が勝手にSQLへインジェクションされるリスクが高まる。

—

2. データベース負荷の観点:SQL生成とメモリフットプリント

では、実際に生成されるSQLと、PHPのメモリ空間における挙動をデータベースのレイヤから比較する。

WP_Queryの内部状態保持(Statefulness)

`WP_Query` は、単にクエリ結果の配列を返すだけでなく、オブジェクト自体が膨大な状態を保持する。

  • `$this->request`(生成された最終的なSQL文)
  • `$this->posts`(取得した投稿オブジェクトの配列)
  • `$this->post_count`(取得件数)
  • `$this->current_post`(ループのポインタ)
  • `$this->max_num_pages`(ページネーション用の全マッチ件数計算)

特に注意すべきは、`WP_Query` はデフォルトで `found_posts` を計算するために、SQLに `SQL_CALC_FOUND_ROWS` を付与し、さらに総件数を取得する追加クエリを発行する点である(ページネーションが不要な場合でもだ)。

— WP_Query がデフォルトで生成するクエリのイメージ
SELECT SQL_CALC_FOUND_ROWS wp_posts.
FROM wp_posts
WHERE 1=1
AND wp_posts.post_type = ‘post’
AND wp_posts.post_status = ‘publish’
ORDER BY wp_posts.post_date DESC
LIMIT 0, 5;

この `SQL_CALC_FOUND_ROWS` は、MySQLのオプティマイザに対してインデックスの効率的な利用を放棄させ、一時的なテーブルスキャンを強制することが多いため、データ量が100万件を超えたあたりからデータベースのCPU使用率を跳ね上げる原因となる。

get_posts() による最適化

一方、`get_posts()` は純粋に「データの配列」を取得することに特化している。デフォルトで `no_found_rows => true` が暗黙的に適用されるケースが多く(パラメータ設定による)、不要な `FOUND_ROWS()` の計算を抑制する。

—

3. ベンチマークと選択基準:シニアエンジニアの判断アルゴリズム

では、実際の開発現場において、どのように両者を使い分けるべきか。決定論的な判断基準を提示する。

選択フローチャート

1. メインループ(The Loop)の制御、またはページネーション(`paged`パラメータ)が必要か?

  • Yes $\rightarrow$ `WP_Query` を直接インスタンス化する。
  • No $\rightarrow$ 次のステップへ。

2. ウィジェットやAPIレスポンス用など、単なる「データの配列」が欲しいだけか?

  • Yes $\rightarrow$ `get_posts()` (または `WP_Query` で `no_found_rows => true` を明示)を使用する。

3. サードパーティ製プラグインのフィルター(多言語化等)を完全に排除し、極限まで実行速度を優先したいか?

  • Yes $\rightarrow$ `get_posts()` (`suppress_filters => true`)を選択する。

—

4. 実装コード例:限界までチューニングされたクエリ

パフォーマンスを極限まで高めるため、`WP_Query` を用いて不要なオーバーヘッド(メタデータのキャッシュ、タームのキャッシュ、カウント処理)を完全に排除した実装例を示す。

/

  • データベースの負荷を最小限に抑えた高速な投稿取得パターン

/
$optimized_query = new WP_Query( array(
‘post_type’ => ‘post’,
‘post_status’ => ‘publish’,
‘posts_per_page’ => 10,

// パフォーマンスチューニングの極意
‘no_found_rows’ => true, // SQL_CALC_FOUND_ROWSを排除し、SELECTのオーバーヘッドを削減
‘update_post_meta_cache’ => false, // メタデータを使用しない場合、wp_postmetaへのJOIN/追加クエリを防ぐ
‘update_post_term_cache’ => false, // ターム(カテゴリー・タグ)を使用しない場合、wp_term_relationships等の処理をスキップ

‘orderby’ => array(
‘date’ => ‘DESC’,
‘ID’ => ‘DESC’ // インデックスの効きを安定させるための複合ソート
),
));

$posts = $optimized_query->posts;

// メモリリークを防ぐため、不要になったクエリ変数は速やかに解放またはリセットを意識する
wp_reset_postdata();

このコードがデータベースに与える影響

上記のパラメータを指定することで、WordPressは通常発生させる以下のクエリを完全にスキップする。

  • 全件数を算出する `SELECT FOUND_ROWS()`
  • 取得した全ポストのメタデータを一括ロードする `SELECT FROM wp_postmeta WHERE post_id IN (…)`
  • タクソノミー情報を一括ロードするオブジェクトキャッシュ用のクエリ

結果として、MySQLサーバへのラウンドトリップ(通信往復)回数を `1` に抑え込むことが可能となる。

—

結言

「`get_posts()` と `WP_Query`、どちらを使うべきか」という問いに対する最終解答は明快である。

  • UIの構築(ページネーションやグローバルクエリの書き換え)には `WP_Query` を使う。
  • バックグラウンド処理、REST APIのカスタムエンドポイント、ウィジェットでのデータフェッチなど、純粋なデータ処理には `get_posts()`(あるいは `no_found_rows` をチューニングした `WP_Query`)を使う。

システムアーキテクトとして、我々はフレームワークが提供する抽象化レイヤの裏側で何が実行されているかを常に把握し、不必要なSQLやメモリ消費を削ぎ落とし続けなければならない。コードの1行、配列の1つのキー選択が、数百万アクセスの耐性を決めるのだ。

タイトルとURLをコピーしました