2026年7月16日
2026年7月16日
WordPressのDBにインデックスを追加してWP_Queryを速くする手順と注意点
WP_Query が遅いとき、最初に手を入れるべきなのはインデックスではなくクエリの書き方です。WordPress の主要テーブルには必要なインデックスが最初から付いており、遅さの多くは「不要な件数計算」「メタデータでの絞り込み」「取りすぎ」から生まれます。この記事では、WP_Query 側でできる改善を先に済ませ、それでも遅い場合に wp_postmeta へインデックスを追加する手順と、その限界・戻し方までを説明します。
まず遅いクエリを特定する
推測でインデックスを足すと、効かないうえに書き込みだけ遅くなることがあります。手を入れる前に、どのクエリが遅いのかを数字で確認します。
- Query Monitor プラグイン: 管理バーからページごとの SQL、実行時間、呼び出し元(テーマかプラグインか)を確認できます。開発・ステージング環境での利用が基本です。
- SAVEQUERIES: wp-config.php に
define( 'SAVEQUERIES', true );を入れると$wpdb->queriesに SQL と所要時間が記録されます。負荷が増えるため、確認が終わったら外します。 - スロークエリログ: サーバー側で一定時間を超えたクエリを記録します。実際のアクセスで何が遅いかを把握するのに向いています。
WP_Query が発行した SQL は、実行後のオブジェクトの request プロパティで確認できます。
$q = new WP_Query( $args );
error_log( $q->request ); // 発行された SELECT 文をログへ(確認後は削除)
取り出した SQL の先頭に EXPLAIN を付けて実行すると、インデックスが使われているかが分かります。読み方の詳細は別記事に譲り、ここでは「type が ALL」「rows が数十万」「Using filesort / Using temporary」が出ていたら改善の余地がある、と覚えておけば十分です。
インデックスより先に試すWP_Queryの改善
件数計算をやめる(no_found_rows)
WP_Query は既定で、ページ送り用に全件数を数えます。トップページの「新着5件」のようにページ送りが不要な一覧では、この計算は無駄です。'no_found_rows' => true を指定すると件数計算が省かれ、投稿数が多いサイトほど効果が出ます。
必要な列とキャッシュだけ取る
ID だけ欲しい場面では 'fields' => 'ids' にすると、投稿オブジェクト全体を組み立てずに済みます。また、ループ内でカスタムフィールドやタームを使わないなら、メタとタームのキャッシュ読み込みを止められます。
$args = array(
'post_type' => 'post',
'posts_per_page' => 5,
'no_found_rows' => true, // ページ送りが不要なら件数計算を省く
'ignore_sticky_posts' => true,
'update_post_meta_cache' => false, // ループでメタを使わない場合のみ
'update_post_term_cache' => false, // ループでタームを使わない場合のみ
);
$recent = new WP_Query( $args );
update_post_meta_cache を false にしたうえでループ内で get_post_meta() を呼ぶと、投稿ごとに個別のクエリが走って逆に遅くなります。使うかどうかを確認してから外してください。
meta_query を大きなテーブルで多用しない
カスタムフィールドでの絞り込み(meta_query)は、wp_posts と wp_postmeta の JOIN になります。wp_postmeta は投稿数×フィールド数の行を持つため、すぐに数百万行に達します。特に次の書き方は遅くなりがちです。
- 複数条件を
ORでつなぐ 'compare' => 'LIKE'で部分一致させる'type' => 'NUMERIC'で数値比較する(内部でCAST(meta_value AS SIGNED)となり、インデックスが効きません)'orderby' => 'meta_value_num'で並べ替える
「おすすめフラグ」「カテゴリ的な分類」のように値の種類が少ないものは、カスタムタクソノミーに移すと tax_query で絞り込めるようになり、インデックスの効いたテーブルで処理できます。価格や日付のような数値で範囲検索が多いなら、専用のカスタムテーブルを作る方が根本的な解決になります。
結果をキャッシュする
同じ一覧を毎回組み立てる必要がなければ、Transients API やオブジェクトキャッシュに結果の ID 配列を保存し、投稿更新時に削除する方法もあります。WordPress 6.1 以降は、永続オブジェクトキャッシュ(Redis など)がある環境では WP_Query の結果自体もキャッシュされる仕組みが入っています。
WordPressに最初からあるインデックスを確認する
インデックスを足す前に、既存のものを確認します。テーブル接頭辞が wp_ 以外なら読み替えてください。
SHOW INDEX FROM wp_posts;
SHOW INDEX FROM wp_postmeta;
標準の wp_posts には type_status_date(post_type, post_status, post_date, ID)や post_author、post_parent などがあり、投稿タイプと公開状態、日付順での一覧はこれで処理されます。wp_postmeta には主キーの meta_id のほか、post_id と meta_key(先頭191文字のプレフィックス)にインデックスがあります。一方、meta_value にはインデックスがありません。meta_value は longtext 型で、全体にはインデックスを張れないためです。
つまり「meta_key で絞ったあと、大量の行を meta_value で一行ずつ比較している」状態が遅さの典型です。
wp_postmetaにインデックスを追加する手順
1. バックアップとステージング
インデックスの追加はデータを書き換えませんが、大きなテーブルでは処理中に負荷がかかり、ディスク容量も消費します。作業前にデータベース全体のバックアップを取り、本番と同程度のデータを入れたステージング環境で効果と所要時間を測ってから本番に適用してください。ディスクの空き容量も確認しておきます。
2. 追加する
meta_key と meta_value の先頭部分を組み合わせた複合インデックスの例です。
-- 例: meta_key と meta_value の先頭32文字で複合インデックスを作成
ALTER TABLE wp_postmeta
ADD INDEX idx_meta_key_value (meta_key(191), meta_value(32)),
ALGORITHM=INPLACE, LOCK=NONE;
meta_value(32) はプレフィックスインデックスで、先頭32文字だけを索引にします。完全一致や前方一致の比較には効きますが、数値として CAST する比較、部分一致の LIKE、meta_value での並べ替えには効きません。プレフィックス長は、実際に絞り込みに使う値の長さに合わせて調整します。
ALGORITHM=INPLACE, LOCK=NONE は、InnoDB でセカンダリインデックスを追加する際に読み書きを止めずに処理するための指定です。対応状況は MySQL / MariaDB のバージョンによって異なり、条件を満たさない場合はエラーで止まります(勝手にロックする方法へ切り替わることはありません)。非常に大きなテーブルでは pt-online-schema-change のようなツールを検討します。
3. 効果を確認する
追加後、同じ SQL に EXPLAIN をかけ、key 列に新しいインデックス名が出ているか、rows の見積もりが減っているかを確認します。実行時間は Query Monitor やスロークエリログで追加前と比べます。使われていなければ、条件の書き方がインデックスの効かない形(CAST や LIKE '%…%')になっていないかを見直します。
4. 効かなければ戻す
ALTER TABLE wp_postmeta DROP INDEX idx_meta_key_value;
インデックスは書き込みのたびに更新されるため、使われないインデックスは保存処理を遅くし、容量も消費します。効果がなかったものは削除してください。
コアテーブルを変更するときの注意
- 変更は記録しておく: 誰が何のインデックスを足したかを残しておかないと、移行やトラブル対応の際に混乱します。名前に
idx_のような接頭辞を付けると区別しやすくなります。 - 既存のインデックスは消さない・変えない: コアの更新処理(dbDelta)はテーブル定義を前提に動きます。標準のインデックスを削除・改名するのは避けてください。
- プラグインの選択肢もある: wp_postmeta などのキー構成をまとめて見直す「Index WP MySQL For Speed」というプラグインがあります。変更内容が大きいため、導入前に説明をよく読み、ステージングで検証してから使ってください。対応する MySQL のバージョンや動作は更新で変わる可能性があります(2026年10月現在)。
- サーバー側の設定も見る: innodb_buffer_pool_size が小さいと、インデックスがあってもディスク読み込みが増えます。共用サーバーでは変更できないことが多いため、その場合はクエリ側の改善が中心になります。
まとめ
- 遅いクエリを Query Monitor・SAVEQUERIES・スロークエリログで特定し、EXPLAIN で確認してから手を入れる
- まず WP_Query を見直す。no_found_rows、fields、メタ/タームキャッシュの制御、meta_query の削減、結果のキャッシュで多くは改善する
- wp_postmeta の meta_value は longtext なのでプレフィックスインデックスになり、CAST・LIKE・並べ替えには効かない
- インデックス追加はバックアップとステージング検証のあとで行い、効果がなければ削除する。コアの既存インデックスは変更しない