【実務・中級編】wp_postsのpost_statusとpost_typeを組み合わせた複合インデックスの設計とクエリプランナーの挙動 – WordPress 内部コア・データベース構造とパフォーマンス最適化解析バイブル

「まだデフォルトの `wp_posts` インデックスで消耗しているのか?」

WordPressを単なるCMSとしてではなく、大規模なアプリケーションプラットフォームや、数百万件のレコードを扱うデータハブとして扱うなら、避けては通れない壁がある。それが `wp_posts` におけるクエリパフォーマンスの限界だ。

標準のWordPressが提供するインデックスは、汎用性を重視するあまり、特定のユースケース――例えば「特定のカスタム投稿タイプかつ公開済みの記事を、日付順で高速に取得する」といった実務で最も頻出するパターンにおいて、クエリプランナーが最適とは言えない選択をする原因になっている。

今日は、テクニカルリードとして、`post_status` と `post_type` を組み合わせた複合インデックス(Composite Index)の設計理論と、MySQLのクエリプランナーを完全に制御するための実践的なアプローチを伝授する。

—

1. なぜデフォルトのインデックスでは「遅い」のか

まず、`wp_posts` の標準的なインデックス構造を思い出してほしい。多くの環境では `type_status_date` という複合インデックスが存在するが、これだけで十分だと考えるのは素人だ。

問題はカーディナリティ(選択性)にある。

  • `post_type`: `post`, `page`, `attachment`, そして数個のカスタム投稿タイプ。
  • `post_status`: `publish`, `inherit`, `private`, `draft` 等。

これらはカラム単体で見れば非常にカーディナリティが低い。MySQLのクエリプランナーは、インデックスを使っても結果セットが十分に絞り込めないと判断すると、インデックスをフルスキャンするか、あるいは最悪の場合、テーブルフルスキャンを選択する。

特に、数百万件の `attachment`(inherit)が蓄積されたデータベースで、特定のカスタム投稿タイプ `product` の `publish` 記事を検索する場合、標準のインデックスでは「まずタイプで絞り、次にステータスで絞り…」という工程で、依然として膨大な中間データセットをメモリ上に展開することになる。

2. 複合インデックス設計の黄金律:LHS(Left-Hand Side)原則

複合インデックスを設計する際、カラムの順序は「等価比較(=)」を行うカラムを先頭に、そして「範囲比較(<, >, BETWEEN)」や「ソート(ORDER BY)」を行うカラムを最後に配置するのが鉄則だ。

実務で最も多いクエリはこれだ:

SELECT FROM wp_posts
WHERE post_type = ‘product’
AND post_status = ‘publish’
ORDER BY post_date DESC
LIMIT 20;

このクエリを最速にするための複合インデックスは以下の通りになる。

推奨される複合インデックス

ALTER TABLE wp_posts ADD INDEX idx_type_status_date (post_type, post_status, post_date);

なぜこの順番か?

1. `post_type`: 最初に最も大きなカテゴリでフィルタリングする。
2. `post_status`: 次にステータスで絞り込む。ここまでで、読み込むべき行数は劇的に減少する。
3. `post_date`: 最後に日付。インデックス内にこのカラムが含まれていることで、MySQLはFilesort(外部ソート)を回避し、インデックスの順序をそのまま利用して `LIMIT` 分のデータを返すことができる。

これが、いわゆる Covering Index(カバリングインデックス) の一歩手前の最適化だ。

—

3. 実践:インデックスの追加とクエリプランの検証

では、実際に現場で使える「インデックス追加スクリプト」と、その効果を確認する方法を見てみよう。

プロダクション用:インデックス追加スニペット

/

  • wp_postsのパフォーマンスを極限まで引き出すためのカスタムインデックス作成
  • 注意: 数百万件のデータがある場合、この操作には数分〜数十分かかる可能性がある。
  • メンテナンスモード、またはステージング環境での検証を推奨する。

/
function optimize_wp_posts_indexes() {
global $wpdb;

$index_name = ‘idx_type_status_date_custom’;
$table_name = $wpdb->posts;

// インデックスが存在するかチェック
$existing_indices = $wpdb->get_results(“SHOW INDEX FROM $table_name”, ARRAY_A);
$has_index = false;
foreach ($existing_indices as $index) {
if ($index[‘Key_name’] === $index_name) {
$has_index = true;
break;
}
}

if (!$has_index) {
// post_type, post_statusで絞り込み、post_dateでソートするクエリに特化
$wpdb->query(“ALTER TABLE $table_name ADD INDEX $index_name (post_type, post_status, post_date)”);
error_log(“Performance Optimization: Index $index_name added to $table_name.”);
}
}
// 開発のライフサイクルに合わせて一度だけ実行させる(例:プラグイン有効化時など)
// register_activation_hook(__FILE__, ‘optimize_wp_posts_indexes’);

クエリプランナーの挙動を確認する

インデックスを追加しただけで満足してはいけない。`EXPLAIN` 文でクエリプランナーの挙動を「監視」しろ。

EXPLAIN SELECT ID FROM wp_posts
WHERE post_type = ‘product’
AND post_status = ‘publish’
ORDER BY post_date DESC LIMIT 10;

チェックすべき項目:

  • `type`: `ref` または `range` になっているか?(`ALL` は論外だ)
  • `key`: 先ほど追加した `idx_type_status_date_custom` が使われているか?
  • `Extra`: `Using filesort` が消えているか? ここが重要だ。`post_date` をインデックスの最後に含めたことで、ソート処理がインデックススキャンのみで完結している証拠だ。

—

4. 高度な応用:非同期API連携における「不都合な真実」

非同期通信や外部システムとのAPI連携で、`post_id` ではなく `post_name`(スラッグ)や独自の `meta_key` でレコードを頻繁に参照する場合、`wp_posts` の標準インデックスは無力化する。

例えば、「特定の `post_type` の中で `post_name` が一致するものを探す」場合、標準の `post_name` インデックス(単一カラム)では、全投稿タイプを横断して検索してしまう。

解決策:複合ユニークインデックス

/ post_typeとpost_nameを組み合わせることで、スラッグの検索効率を最大化する /
ALTER TABLE wp_posts ADD INDEX idx_type_name (post_type, post_name);

これにより、APIエンドポイントからの `GET /v1/products/{slug}` のようなリクエストに対し、データベースはミリ秒以下のレイテンシで応答可能になる。

—

5. テクニカルリードの視点:保守性とトレードオフ

「インデックスを増やせば増やすほど速くなる」というのは幻想だ。インデックスは Write(書き込み)の代償 を伴う。

1. 挿入・更新のオーバーヘッド: `wp_insert_post` が走るたびに、すべてのインデックスが更新される。高頻度でバッチ処理を行う場合、過剰なインデックスはスループットを低下させる。
2. ストレージ容量: インデックスは物理メモリ(InnoDB Buffer Pool)を占有する。

設計の指針:

  • 80/20の法則: アプリケーションで最も頻繁に実行され、かつスロークエリログに記録されている上位20%のクエリに対してのみ、専用の複合インデックスを設計せよ。
  • 不要なインデックスの削除: WordPress標準のインデックスが、新しく作った複合インデックスに包含されている(プレフィックスが一致している)場合、古いインデックスは削除を検討しても良い。

結論:システムを掌握せよ

WordPressのパフォーマンスを「プラグイン」で解決しようとするのは、エンジニアとしての敗北だ。

真の最適化は、OSに近いレイヤー、すなわちデータベースの物理構造から始まる。`post_type` と `post_status` のカーディナリティを理解し、クエリプランナーが迷わず最適なパスを選択できるよう、インデックスを「彫刻」しろ。

この設計ができるようになれば、WordPressは単なるブログツールではなく、エンタープライズに耐えうる堅牢なデータプラットフォームへと昇華する。

「コードは嘘をつかないが、インデックスのないクエリは真実(データ)にたどり着くのが遅すぎる。」

次にスロークエリログを見たとき、君がこの記事を思い出し、即座に `ALTER TABLE` を打てることを期待している。

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