2026年8月11日
2026年8月11日
WordPressのWP_Queryのキャッシュを手動でクリアして最新データを取得する方法
WP_Query が古い結果を返すとき、原因はたいてい「データを WordPress の API を通さずに書き換えたため、キャッシュの無効化が働いていない」か「WP_Query より外側のキャッシュ(ページキャッシュやトランジェント)が古い HTML や結果を返している」かのどちらかです。この記事では、どの層のキャッシュが古いのかを切り分ける方法と、層ごとの正しいクリア方法(clean_post_cache()、cache_results 引数、グループ単位のフラッシュ、WP-CLI)を説明します。
カスタムキャッシュを自分で設計・実装する方法は、別記事の wp_cache_set/get() の実装ガイドで扱っています。この記事は「いま古いデータが出ていて、それを直したい」場面に絞った内容です。
WP_Queryのキャッシュはどう動いているか
WP_Query には、1回のリクエストの中だけで働くキャッシュと、リクエストをまたいで残るキャッシュがあります。
- オブジェクトキャッシュ(リクエスト内):取得した投稿やメタデータは、そのリクエストの間メモリに保持され、同じ投稿を何度読んでも DB には1回しか問い合わせません。永続オブジェクトキャッシュがない環境では、リクエストが終われば消えます。
- 永続オブジェクトキャッシュ(Redis や Memcached):
object-cache.phpドロップインが入っていると、上の内容がリクエストをまたいで保存されます。 - クエリ結果のキャッシュ:WordPress 6.1 以降、WP_Query は「どの投稿 ID が該当したか」という結果もオブジェクトキャッシュに保存します。キャッシュのキーには、投稿データが最後に変わった時刻を表す値(
postsグループのlast_changed)が含まれていて、投稿が更新されるとこの値が変わり、古い結果は使われなくなる仕組みです。タクソノミーを条件にしたクエリでは、ターム側の変更時刻も考慮されます。
この仕組みのおかげで、wp_update_post() や update_post_meta() など WordPress の関数でデータを更新していれば、通常はキャッシュを手動で消す必要はありません。手動のクリアが必要になるのは、この「変更の通知」が届かない更新をしたときです。内部のキー構成やグループ名はバージョンによって変わることがあるので、現時点の実装の説明として読んでください(2026年10月現在)。
まず「どの層が古いのか」を切り分ける
キャッシュを消す前に、古いデータがどこから来ているのかを確かめます。やみくもに全キャッシュを消すと、原因が分からないまま再発します。上から順に、可能性の高いものから確認してください。
- ページキャッシュ(キャッシュプラグイン・サーバー・CDN):ログインした状態で同じページを開いて最新の内容が出るなら、ページキャッシュが原因の可能性が高いです。多くのキャッシュプラグインはログインユーザーにキャッシュを返さないためです。開発者ツールのネットワークタブでレスポンスヘッダーを見て、
ageやx-cache、cf-cache-statusのようなキャッシュ由来のヘッダーがあるかも確認します。 - トランジェントや独自キャッシュ:テーマやプラグインが WP_Query の結果を
set_transient()で保存していることがあります。該当するテンプレートやショートコードのコードでget_transientやwp_cache_getを検索します。 - 永続オブジェクトキャッシュ:
wp cache typeを実行すると、使われているオブジェクトキャッシュの種類が分かります。Default 以外(Redis など)と表示され、かつ直接 SQL や外部ツールでデータを書き換えた心当たりがあれば、ここが疑わしいです。 - キャッシュ以外の原因:サブループの後に
wp_reset_postdata()を呼んでいないためにメインの投稿データがずれている、予約投稿の時刻やタイムゾーンの設定がずれている、といった問題も「古いデータが出る」ように見えます。
Query Monitor プラグインを入れると、ページ内で実行されたクエリとオブジェクトキャッシュのヒット率を確認でき、切り分けがしやすくなります。
直接SQLで更新した後は clean_post_cache() を呼ぶ
永続オブジェクトキャッシュがある環境で最も多い原因は、$wpdb で投稿テーブルを直接更新したことです。この場合、キャッシュには更新前の投稿データとクエリ結果が残ります。更新した投稿ごとに clean_post_cache() を呼ぶと、その投稿のキャッシュが削除され、投稿データの変更時刻も更新されるため、以後の WP_Query は新しい結果を返します。
<?php
global $wpdb;
$post_id = 123;
$updated = $wpdb->update(
$wpdb->posts,
array( 'post_status' => 'draft' ),
array( 'ID' => $post_id ),
array( '%s' ),
array( '%d' )
);
if ( false !== $updated ) {
clean_post_cache( $post_id ); // 投稿・メタ・ターム関連のキャッシュを削除し、変更時刻を更新
}
投稿メタだけを直接 SQL で書き換えた場合は、wp_cache_delete( $post_id, 'post_meta' ) でその投稿のメタのキャッシュを消します。メタの値を条件にしたクエリの結果も更新したい場合は、あわせて clean_post_cache() を呼んでおくと手堅いです。タームを直接書き換えた場合は clean_term_cache() を使います。
ただし、そもそも直接 SQL を使わずに wp_update_post() や update_post_meta() で更新できるなら、そのほうがキャッシュの扱いを WordPress に任せられて安全です。直接 SQL を使う前には DB のバックアップを取ってください。
大量インポートの後にまとめて無効化する
数千件の投稿を取り込むようなバッチ処理では、1件ごとにキャッシュを無効化すると処理が重くなります。wp_suspend_cache_invalidation() で一時的に無効化を止め、処理が終わったらまとめてクリアする方法があります。
<?php
wp_suspend_cache_invalidation( true );
foreach ( $rows as $row ) {
// wp_insert_post() などで取り込む
}
wp_suspend_cache_invalidation( false );
// 変更時刻を更新して、投稿を対象にしたクエリ結果のキャッシュを無効にする
wp_cache_set_posts_last_changed();
無効化を止めている間は clean_post_cache() も何もしないので、処理の途中でエラーが起きた場合でも、最後のクリアが実行されるようにしておくことが大切です。取り込んだ個々の投稿の古いキャッシュが残っている可能性がある場合は、該当する投稿 ID に対して clean_post_cache() を呼び直します。
特定のクエリだけキャッシュを使わない
管理用の集計や、外部システムが頻繁に書き換えるデータを読む処理など、「このクエリだけは毎回 DB から読みたい」場合は、WP_Query の引数で指定できます。
<?php
$query = new WP_Query( array(
'post_type' => 'news',
'posts_per_page' => 5,
'no_found_rows' => true, // ページ送りをしないなら総件数の計算を省く
'cache_results' => false, // 投稿とクエリ結果をキャッシュから読まず、保存もしない
'update_post_meta_cache' => false, // メタを使わないなら読み込みを省く
'update_post_term_cache' => false, // タームを使わないなら読み込みを省く
) );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
echo '<li>' . esc_html( get_the_title() ) . '</li>';
}
wp_reset_postdata();
}
cache_results を false にすると、そのクエリはキャッシュの恩恵を受けなくなり、アクセスが多いページでは DB の負荷が上がります。原因が直接 SQL での更新なら、クエリ側でキャッシュを切るより、更新側で clean_post_cache() を呼ぶほうが筋のよい直し方です。cache_results は一時的な回避策か、本当に毎回最新が必要な処理に限定してください。
グループ単位・全体でクリアする
原因の投稿が特定できない、または外部ツールでまとめて書き換えられた場合は、範囲を広げてクリアします。
<?php
// クエリ結果のキャッシュをまとめて無効にする(投稿データの変更時刻を更新)
wp_cache_set_posts_last_changed();
// キャッシュの実装がグループ単位の削除に対応していれば、グループごと消す
if ( function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_group' ) ) {
wp_cache_flush_group( 'post-queries' );
}
wp_cache_flush_group() と wp_cache_supports() は WordPress 6.1 で追加された関数です。グループ単位の削除に対応しているかどうかは、使っている object-cache.php ドロップイン(Redis Object Cache などのプラグインが提供するもの)次第なので、wp_cache_supports() で確認してから呼びます。
WP-CLI では次のコマンドが使えます。
# 使われているオブジェクトキャッシュの種類を確認
wp cache type
# 特定のキーを削除(キーとグループを指定)
wp cache delete my_key my_group
# グループ単位で削除(対応する WP-CLI とドロップインが必要)
wp cache flush-group post-queries
# トランジェントを削除
wp transient delete my_transient_key
# オブジェクトキャッシュをすべて削除(影響範囲に注意)
wp cache flush
wp cache flush(PHP では wp_cache_flush())はオブジェクトキャッシュ全体を空にします。Redis を複数のサイトで共有している構成では、設定によっては他のサイトのキャッシュまで消えることがあり、またキャッシュが空になった直後は DB へのアクセスが一時的に集中します。本番では、より狭い範囲のクリアで解決しないときの最後の手段にしてください。トランジェントの一括削除についても、永続オブジェクトキャッシュを使っている環境ではトランジェントが DB ではなくキャッシュ側にあるため、コマンドの説明(wp help transient delete)で対象範囲を確認してから実行します。
ページキャッシュ側も連動してクリアする
WP_Query のキャッシュを正しく消しても、ページキャッシュが古い HTML を返していれば画面は変わりません。主なキャッシュプラグインは投稿の更新時に関連ページを自動で削除しますが、直接 SQL や外部システムでデータを変えた場合は、その通知が届きません。
この場合は、キャッシュプラグインの管理画面や WP-CLI 連携でキャッシュを削除するか、CDN の管理画面や API でパージします。削除用の関数やコマンドの名前はプラグインごとに異なり、バージョンでも変わるため、利用中のプラグインのドキュメントで確認してください。データ更新の処理の最後に、オブジェクトキャッシュのクリアとページキャッシュのパージをまとめて実行する仕組みにしておくと、取りこぼしが減ります。
確認方法
クリアの効果は次の手順で確かめます。
- ログアウトした状態(またはシークレットウィンドウ)で対象ページを開き、最新のデータが表示されるか確認する。
- Query Monitor で、対象のクエリが実行されているか、オブジェクトキャッシュのヒット・ミスがどう変わったかを見る。
- 同じ更新手順をもう一度実行し、手動のクリアなしで反映されるか(仕組みとして直ったか)を確認する。
まとめ
- 古いデータの原因は、ページキャッシュ・トランジェント・永続オブジェクトキャッシュ・キャッシュ以外の問題の順に切り分ける。
- WordPress の関数で更新していれば WP_Query のキャッシュは自動で無効になる。手動のクリアが必要なのは直接 SQL や外部ツールで更新したとき。
- 直接 SQL の後は
clean_post_cache()、大量インポートの後はwp_cache_set_posts_last_changed()で無効化する。 cache_results => falseは特定のクエリだけの回避策として使い、常用しない。wp cache flushは影響範囲が広いので最後の手段にし、ページキャッシュのパージも忘れずに連動させる。