こんにちは!WordPressの裏側の仕組みやデータベースとの対話に興味を持ってくれて嬉しいです。他の言語からやってくると、「WordPressってなんだかマジックが多すぎて、裏で何が起きているか見えにくいな…」と感じることも多いですよね。すごくよく分かります。
今回は、実務でも非常によく使う階層構造の検索、その中でも`WP_Query`の`post_parent`検索を爆速にするインデックスの貼り方について、データベースの内部構造まで踏み込んで一緒に見ていきましょう!
ここをクリアできれば、単なる「使い方を知っている人」から「データベースの挙動までコントロールできるエンジニア」へと一歩ステップアップできますよ。ぜひ最後までついてきてくださいね。
—
1. なぜ `post_parent` 検索は重くなるのか?
WordPressで固定ページ(Page)やカスタム投稿タイプを使って、親子関係(階層構造)を表現することはよくありますよね。例えば、「親ページIDが `123` の子ページ一覧を取得したい」という時、私たちは次のようなコードを書きます。
// 親IDが 123 の子ページを取得するおなじみの書き方
$args = array(
‘post_type’ => ‘page’,
‘post_parent’ => 123,
‘posts_per_page’ => -1,
);
$child_query = new WP_Query( $args );
このコード、数件程度の投稿数であれば一瞬で終わります。でも、サイトが成長して投稿数が数万件、数十万件になってくると、このクエリが急に重くなり、サーバーのCPU使用率が跳ね上がったり、スロークエリとしてログに記録されたりするようになります。
一体、データベース(MySQL / MariaDB)の内部では何が起きているのでしょうか?
裏側で発行されているSQLの正体
`WP_Query` が実行されると、WordPressは内部で次のようなSQL文を組み立ててデータベースに投げます(※分かりやすく簡略化しています)。
SELECT
FROM wp_posts
WHERE post_parent = 123
AND post_type = ‘page’
AND post_status = ‘publish’;
データベースの気持ちになって考えてみましょう。もし、`wp_posts` テーブルの `post_parent` カラムにインデックス(索引)が貼られていなかったらどうなるでしょうか?
データベースは、お目当ての親IDを見つけるために、テーブルの一番上から一番下まで、すべての行を1件ずつ順番に確認していきます(これをフルテーブルスキャンと呼びます)。数万行のフルテーブルスキャンが走るたびに、サーバーは汗をかいて処理遅延を引き起こしてしまうんです。
—
2. データベースのインデックスってなに?(イメージ図解)
インデックス(Index)は、本でいうところの「巻末の索引(さくいん)」だと思ってください。
【インデックスなし(フルテーブルスキャン)】
1行目を見に行く → 違う → 2行目を見に行く → 違う → …(全行走査)
【インデックスあり(高速検索)】
「post_parent = 123」はどこにある?
↓ 索引を見る
「この行番号(アドレス)に飛べば一発だよ!」と瞬時に特定
WordPressのコアテーブルである `wp_posts` には、デフォルトでいくつかのインデックス(プライマリキーや `post_name` など)が用意されていますが、実は単体の `post_parent` カラムにはインデックスが標準では貼られていません。
そのため、階層構造を多用する大規模サイトでは、ここをチューニングしてあげる必要があるのです。
—
3. 実践!高速化のための「複合インデックス」設計
単に `post_parent` だけにインデックスを貼るのも一つの手ですが、実務の `WP_Query` を見ると、大抵は次のような条件がセットになっています。
1. `post_type` (投稿タイプは何か)
2. `post_status` (公開されているか)
3. `post_parent` (親は誰か)
ここで登場するのが、複数のカラムを組み合わせて作る「複合インデックス(Composite Index)」です。
最適なインデックスの作成SQL
データベース管理ツール(phpMyAdminやWP-CLI、Sequel Aceなど)を使って、`wp_posts` テーブルに以下のSQLを実行します。
— post_type, post_status, post_parent の順番で複合インデックスを追加する
ALTER TABLE wp_posts
ADD INDEX idx_type_status_parent (post_type, post_status, post_parent);
なぜこの順番(`post_type` -> `post_status` -> `post_parent`)なのか?
データベースの複合インデックスは、左側のカラムから順番に絞り込みを行っていくという性質(左端プレフィックスの法則)があります。
`WP_Query` は通常、特定の投稿タイプ(例: `page`)と公開ステータス(例: `publish`)を指定することが多いため、カーディナリティ(データの重複度合いが低い=種類が多いもの)や検索条件の絞り込みやすさを考慮して、左側に `post_type` と `post_status` を置き、その上で特定の `post_parent` をスパッと特定できるようにこの並び順が最も効率的になります。
—
4. 陥りやすい文法・設計エラーと注意点
データベースのチューニングには、初心者の方がやりがちな「落とし穴」がいくつかあります。ここでしっかり押さえておきましょう。
1. プレフィックス(接頭辞)の確認を忘れる
ご自身のWordPress環境のテーブル名が、デフォルトの `wp_` ではなく、セキュリティ対策などで `wp_abc123_` のようになっている場合があります。SQLを書く際は、必ず実際のテーブル名(`$wpdb->posts` が指す名前)を確認してくださいね。
2. インデックスを「とりあえず全部に貼る」のはNG
「じゃあ、すべてのカラムにインデックスを貼れば最強じゃん!」と思いがちですが、それは大きな間違いです。
インデックスは検索を速くする一方で、新しい投稿を追加(INSERT)したり、更新(UPDATE)したりする時には、インデックスのツリー構造も毎回更新し直す必要があるため、書き込み速度が低下します。
「頻繁に検索されるけれど、更新頻度はそこまで爆発的ではない条件」を見極めてピンポイントで貼るのが、シニアエンジニアの技の見せ所です。
3. `EXPLAIN` でクエリの実行計画を確認する癖をつけよう
「本当にインデックスが効いているのか?」を確認するには、SQL文の頭に `EXPLAIN` をつけて実行します。
EXPLAIN
SELECT
FROM wp_posts
WHERE post_parent = 123
AND post_type = ‘page’
AND post_status = ‘publish’;
実行結果の `key` カラムに、今回作成した `idx_type_status_parent` が表示されていれば、データベースが無事にインデックスを使って検索してくれている証拠です!やったね!
—
5. まとめ
今回は、`WP_Query` の `post_parent` 検索を高速化するためのインデックス設計について解説しました。
- 課題: 投稿数が増えると `post_parent` の検索(フルテーブルスキャン)がボトルネックになる。
- 解決策: `post_type`, `post_status`, `post_parent` を網羅した複合インデックスを貼る。
- 本質: クエリが裏側でどういうSQLを発行し、データベースがどうデータを走査しているかを想像する。
ここを理解できれば、単に動くコードを書くだけでなく、「スケールする(耐えうる)WordPressサイト」を設計できるようになります。
「ここをクリアすれば、WordPressのデータベース周りの基本はバッチリマスターできますよ!」
ぜひ、ステージング環境などで試してみてくださいね。あなたのエンジニアライフを応援しています!