WordPressを掌握する:`term_relationships`の呪縛から脱却する「脱JOIN」戦略
こんにちは。WordPressの深淵を覗き込もうとしている皆さん。
WordPressを使い始めると、必ず一度はぶつかる壁があります。それが「タクソノミー検索の遅延」です。特に記事数が数万件を超え、複雑なカテゴリやタグで絞り込みを始めた瞬間、サイトは悲鳴を上げ始めます。
なぜか? それはWordPressのデータベース構造の心臓部、`wp_term_relationships` テーブルが巨大化し、MySQLがJOIN地獄に陥っているからです。
今日は、この「エンジニア殺し」のボトルネックを、コアの仕組みを理解することでスマートに回避する極意を伝授します。ここをマスターすれば、WordPressのパフォーマンスチューニングは一段階上のステージへ進めますよ。
—
1. なぜ `WP_Query` は遅いのか?(内部構造の解剖)
まず、皆さんが普段何気なく書いているこのコードを見てください。
$query = new WP_Query([
‘tax_query’ => [
[
‘taxonomy’ => ‘category’,
‘field’ => ‘slug’,
‘terms’ => ‘tech-news’,
]
]
]);
このコードを実行したとき、WordPressは何をしているのでしょうか? 内部では以下のような「重いSQL」が発行されています。
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE wp_term_relationships.term_taxonomy_id IN (10)
AND wp_posts.post_type = ‘post’
…
ここがポイントです。
`INNER JOIN` を行っている `wp_term_relationships` テーブルには、`object_id`(記事ID)と `term_taxonomy_id`(タクソノミーID)しか入っていません。記事が10万件あれば、このテーブルには10万行以上のレコードが存在し、毎回フルスキャンに近い負荷がかかります。
特に「複数のタクソノミーで絞り込む」場合、JOINの回数だけ負荷は指数関数的に跳ね上がります。これが「WordPressが遅い」と言われる最大の原因の一つなのです。
—
2. 禁断の「JOIN回避」テクニック:メタデータへの埋め込み
JOINが重いなら、「JOINしなければいい」。これが結論です。
エンジニアリングの世界では、正規化(データベースを綺麗に分けること)とパフォーマンスは時にトレードオフの関係にあります。特定の条件で頻繁に検索されるタクソノミーがあるなら、その情報を `wp_posts` テーブルや `wp_postmeta` に「非正規化」してコピーしておくのが、プロの定石です。
実践:保存時にタクソノミーIDをメタデータに書き出す
`save_post` フックを使って、記事が保存されるタイミングで、必要なタクソノミー情報を `post_meta` にフラットに保存してみましょう。
add_action(‘save_post’, ‘sync_taxonomy_to_meta’, 10, 2);
function sync_taxonomy_to_meta($post_id, $post) {
// リビジョンや自動保存は無視
if (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) return;
// 必要なタクソノミーだけを取得
$terms = wp_get_post_terms($post_id, ‘category’, [‘fields’ => ‘ids’]);
// 検索用としてメタデータに保存(カンマ区切り文字列などでOK)
update_post_meta($post_id, ‘_searchable_cat_ids’, implode(‘,’, $terms));
}
これで、検索時は `wp_term_relationships` を一切見ずに、`wp_postmeta` テーブルのインデックスだけを使って高速に記事を取得できるようになります。
—
3. `meta_query` での検索を極限まで速くする
上記のメタデータ戦略をとった場合、`WP_Query` は以下のように書き換わります。
$query = new WP_Query([
‘meta_query’ => [
[
‘key’ => ‘_searchable_cat_ids’,
‘value’ => ’10’, // カテゴリID: 10
‘compare’ => ‘LIKE’ // または FIND_IN_SET を使う工夫も
]
]
]);
注意点: `meta_query` もデフォルトではそれなりに重いですが、`wp_postmeta` テーブルはもともと `meta_key` でインデックスが張られているため、`wp_term_relationships` の巨大なテーブルとJOINするより遥かに軽快です。
さらに上級者は、`posts_clauses` フィルタを使って、`LIKE` 検索を `FIND_IN_SET` に置き換えるなどのSQL最適化を行います。
—
4. プロのキャッシュ戦略:Transient APIの併用
データベースへのクエリそのものを発行しないのが最強の高速化です。
複雑なタクソノミー検索の結果は、Transient API(一時的なキャッシュ) に保存しましょう。
$cache_key = ‘tech_news_query_’ . $page;
$results = get_transient($cache_key);
if (false === $results) {
$results = new WP_Query($args);
set_transient($cache_key, $results, 12 HOUR_IN_SECONDS);
}
キャッシュ戦略の鉄則は、「記事が更新されたらキャッシュをクリアする」こと。`save_post` フックでキャッシュを `delete_transient` すれば、常に最新かつ高速な状態を維持できます。
—
最後に:WordPressをマスターするということは
「WordPressは遅い」と嘆く人は多いですが、それはコアが提供する標準的な機能だけで戦おうとしているからです。
1. データベースの構造を可視化する(JOINを減らす)
2. 必要な計算は保存時に済ませておく(非正規化)
3. 最後はキャッシュで蓋をする
この3ステップを意識するだけで、あなたの作るWordPressサイトは、数万件のデータを抱えてもミリ秒単位でレスポンスを返すようになります。
WordPressは、単なるブログツールではありません。皆さんの設計思想次第で、どんな巨大なシステムにも化ける「巨大なフレームワーク」なんです。
ここをクリアした皆さんなら、もうWordPressの「初心者」ではありません。次の壁が見えてきたら、またいつでも尋ねてくださいね。一緒に深淵の先へ行きましょう。