WP_Queryを「発行」するな、キャッシュを「叩け」:DB負荷を極限まで削る設計哲学
多くのエンジニアが犯す最大の過ちは、`WP_Query` を「データを取得するための道具」としてしか認識していないことだ。
`WP_Query` は、内部で複雑なSQLを生成し、`wp_posts` や `wp_postmeta` を走査する「コストの高い操作」である。プロダクション環境において、リクエストごとに同じクエリを走らせることは、データベースに対して無駄な負荷を強いるだけでなく、スケーラビリティを殺す行為に等しい。
真のエンジニアは、「なぜデータベースを叩く必要があるのか? そのデータはメモリ上に存在しないのか?」を常に自問する。今回は、WordPressのオブジェクトキャッシュ(`wp_cache`)を掌握し、DBクエリを最小化するための極限の設計パターンを伝授する。
—
1. データベースが悲鳴を上げる「N+1問題」の正体
ループ内で `WP_Query` をインスタンス化したり、`get_post_meta` を何度も呼び出すコードを見たことはないだろうか。
// 悪い例:ループ内でクエリを発行している(アンチパターン)
foreach ($categories as $cat) {
$recent_posts = new WP_Query([‘cat’ => $cat->term_id, ‘posts_per_page’ => 5]);
// …
}
これは致命的だ。カテゴリーの数だけDBクエリが発行され、さらに `WP_Query` の内部で実行される `found_posts` の計算(SQL_CALC_FOUND_ROWSに相当する処理)が全クエリで発生する。これでは、同時接続数が増えた瞬間にサーバーはフリーズする。
—
2. オブジェクトキャッシュを「武器」にする設計パターン
WordPressの `wp_cache_set` と `wp_cache_get` を活用すれば、永続化されたメモリ(RedisやMemcached)を介して、DBへの到達を遮断できる。
実務で即戦力となる、「キャッシュの有無を確認し、なければ生成する」という堅牢な設計パターンがこれだ。
/
- 高速かつ堅牢なデータ取得パターン
- キャッシュキーは名前空間を意識し、複雑な条件をハッシュ化する
/
function get_optimized_recent_posts(int $category_id): array {
$cache_key = “recent_posts_cat_{$category_id}”;
$cached_data = wp_cache_get($cache_key, ‘my_theme_group’);
// キャッシュが存在すれば、即座に返す
if (false !== $cached_data) {
return $cached_data;
}
// キャッシュがない場合のみクエリを発行
$query = new WP_Query([
‘cat’ => $category_id,
‘posts_per_page’ => 5,
‘no_found_rows’ => true, // ページネーション不要なら必ずtrueにする
‘update_post_meta_cache’ => false, // メタデータが不要ならfalseにして負荷軽減
‘update_post_term_cache’ => false,
]);
$posts = $query->posts;
// 1時間(3600秒)キャッシュする
wp_cache_set($cache_key, $posts, ‘my_theme_group’, 3600);
return $posts;
}
このコードが「美しい」理由
1. `no_found_rows => true`: これが重要だ。WordPressはデフォルトで全件数をカウントしようとするが、単なるリスト表示なら不要。この設定だけでSQLの負荷は劇的に下がる。
2. グループ化: `wp_cache_get` の第二引数に独自のグループを指定することで、管理画面や他のプラグインとのキャッシュ衝突を防ぎ、一括削除(`wp_cache_flush_group`)を容易にしている。
3. メタキャッシュの制御: `update_post_meta_cache => false` にすることで、`wp_postmeta` テーブルへの無駄な追加クエリを抑制している。
—
3. データの整合性を担保する「キャッシュパージ」の重要性
キャッシュを導入した瞬間、次の課題は「情報の鮮度」だ。記事が更新されたのに古い情報が表示され続けてはならない。
`save_post` フックを用いて、データが更新されたタイミングでキャッシュを確実にパージ(削除)する設計が必要だ。
add_action(‘save_post’, function($post_id) {
// 関連するキャッシュグループを丸ごと破棄する
// ただし、特定のキャッシュを狙い撃ちするのがベスト
wp_cache_delete(‘recent_posts_cat_1’, ‘my_theme_group’);
}, 10, 1);
—
4. プロダクションへの提言:まとめ
WordPressのパフォーマンスチューニングとは、いかに「クエリを発行しないか」という引き算の芸術である。
- 無駄な `WP_Query` を作らない: 必要なカラムだけを取得する、あるいは `get_posts` を適切に使う。
- ページネーションが不要なら `no_found_rows` を必ず設定する: これはDB負荷削減の基本中の基本だ。
- キャッシュは「更新」ではなく「破棄」: 更新タイミングでパージするロジックを設計段階で組み込むこと。
エンジニアとして、コードを書くときは常に「このクエリは何度実行される可能性があるか?」を想像してほしい。その問いの先にこそ、高速で安定したWordPressサイトがある。
さあ、あなたのコードから不要なSQLを削除し、サーバーに安らぎを与えてやってくれ。それが、プロの仕事だ。