WordPress のキャッシュ3層 — ページキャッシュ・オブジェクトキャッシュ・OPcache は何が違うのか
「キャッシュを消したのに直らない」「キャッシュプラグインを入れたのに管理画面の表示が変わらない」。WordPress の保守でよく出る相談だが、原因の多くは「キャッシュ」という一語で別々の層をまとめて考えていることにある。
WordPress の周りには、少なくとも3つの異なる層のキャッシュがある。ページキャッシュ、オブジェクトキャッシュ、OPcache である。それぞれ「何を保存しているか」「どこに置かれるか」「どう消すか」がまったく違う。この記事では、3層の役割と見分け方を整理する。特定の製品の実装ではなく、WordPress と PHP の一般的な仕組みの解説である。
補足: トランジェント API と Cache-Control / ETag は別の記事で扱った。この記事はそれらと重ならないよう、サーバー側の3つの層の違いに絞る。
3層の全体像
リクエストが届いてから表示されるまでを、大まかに上から下へ並べると次のようになる。
| 層 | 保存するもの | 保存先 | 効く範囲 |
|---|---|---|---|
| ページキャッシュ | 完成した HTML(ページ丸ごと) | ディスクまたはメモリ | PHP 自体を動かさずに返せる |
| オブジェクトキャッシュ | データベース問い合わせの結果やオプション値 | メモリ(既定ではリクエスト内のみ) | PHP は動くが DB への問い合わせが減る |
| OPcache | コンパイル済みの PHP コード | PHP プロセスの共有メモリ | PHP ファイルの読み込み・解析をやり直さない |
大事なのは、上の層ほど手前で処理を打ち切るという点である。ページキャッシュに当たれば PHP も DB もほとんど動かない。オブジェクトキャッシュに当たれば PHP は動くが DB への問い合わせが減る。OPcache は PHP が動くこと自体は変えず、PHP のコードを毎回読み直して解釈する手間を省く。
1. ページキャッシュ — 完成品の HTML を保存する
ページキャッシュは、WordPress が組み立て終えた HTML をそのまま保存しておき、次の同じ URL へのアクセスにはそれを返す仕組みである。ログインしていない訪問者が同じ記事を見るなら、毎回テーマやプラグインのコードを動かして DB から記事を取り出す必要はない。
実装の場所はいくつかある。
- キャッシュ系のプラグインが、HTML ファイルをディスクに書き出し、Web サーバーのルールで直接返す
- Web サーバー側(リバースプロキシや FastCGI キャッシュなど)で保持する
- ホスティング事業者が用意した仕組みで保持する
ページキャッシュで押さえておきたい性質は次の3つ。
- 訪問者ごとに内容が変わるページには向かない。ログイン中の画面、カート、フォームの送信結果などは、通常キャッシュの対象から外される
- 古い内容が残りうる。記事を更新しても、キャッシュが破棄されるまでは古い HTML が返る。多くのキャッシュ系プラグインは記事の更新時に該当ページを自動で破棄するが、テーマの変更やウィジェットの変更では全体の破棄が必要になることがある
- 管理画面は対象外が基本。管理画面の表示が変わらないとき、ページキャッシュは原因ではないことが多い
2. オブジェクトキャッシュ — DB の答えを使い回す
WordPress は、1回のページ表示で多くの値を取り出す。サイト名、テーマの設定、投稿の一覧、ユーザー情報。これらの多くは DB に入っており、取り出すたびに問い合わせると時間がかかる。
そこで WordPress 本体には、取り出した値をメモリ上に一時的に置いて使い回す仕組み(WP_Object_Cache)が最初から入っている。wp_cache_get() や wp_cache_set() といった関数がその入口である。
ただし、標準の状態では、このキャッシュはそのリクエストが終わると消える。つまり1回のページ表示の中での使い回しにとどまる。次のアクセスでは、また最初から DB に問い合わせる。
これを、リクエストをまたいで残るようにするのが「永続オブジェクトキャッシュ」である。Redis や Memcached といった外部のメモリ型ストアを用意し、WordPress の wp-content/object-cache.php というドロップイン(置き換え用ファイル)経由でつなぐ。こうすると、次のアクセスでも DB への問い合わせを減らせる。
見分け方と注意点は次のとおり。
- 永続オブジェクトキャッシュがあるかどうかは、
wp-content/object-cache.phpの有無や、WP-CLI のwp cache typeで確認できる - 値の保存先が DB の外(Redis など)になるため、DB の値を直接書き換えても、キャッシュの中身が古いまま画面に反映されないことがある
wp cache flushで破棄できるが、永続キャッシュを共有している環境では、同じストアを使う他のサイトにも影響が及びうる。共有環境では事前に確認が要る
3. OPcache — PHP のコード自体を保存する
ここまでの2つは「WordPress が出力・取得したデータ」のキャッシュだった。OPcache は性質が異なり、PHP のコードそのものを対象にする。
PHP のソースファイルは、実行のたびにテキストから内部の命令(オペコード)へ変換される。OPcache はこの変換結果を共有メモリに保存しておき、次回以降は変換を省く。WordPress のように PHP ファイルが多いアプリケーションでは、この差が小さくない。PHP の標準的な拡張として含まれており、多くのレンタルサーバーで既定で有効になっている。
ここで保守担当者が引っかかりやすいのは、ファイルを書き換えても、すぐには反映されない場合があることだ。OPcache には「ファイルの更新時刻を確認する間隔」を決める設定(opcache.revalidate_freq など)があり、その間は古いコードが使われ続けることがある。さらに、更新時刻の確認自体を止める設定(opcache.validate_timestamps=0)にしている環境では、PHP の再起動やキャッシュのリセットを行うまで古いコードが動く。
プラグインやテーマの PHP を差し替えたのに挙動が変わらないとき、疑うべきはこの層である。OPcache のリセットは、PHP-FPM の再読み込み(reload)や、ホスティングの管理画面にあるリセット機能などで行う。WP-CLI の wp cache flush では OPcache は消えない点に注意したい。それはオブジェクトキャッシュの操作であり、層が違う。
「消したのに直らない」の切り分け
症状から、どの層を疑うかを整理すると次のようになる。
| 症状 | まず疑う層 |
|---|---|
| 訪問者に見える記事やデザインが古い | ページキャッシュ(および、ブラウザ側のキャッシュ) |
| 管理画面や設定値が古いまま、DB を直接更新した | オブジェクトキャッシュ |
| 差し替えた PHP ファイルの挙動が変わらない | OPcache |
| 特定の外部 API の結果だけ古い | トランジェント(別の記事を参照) |
切り分けの基本は、層を1つずつ外して確かめることである。たとえばログインした状態(ページキャッシュを通らない)で確認し、次に永続オブジェクトキャッシュを一時的に外し、それでも変わらなければ OPcache を疑う。全部を一度に消すと、どの層が原因だったのか分からなくなる。
3層を同時に消すリスク
「とりあえず全部消す」は手っ取り早いが、避けたい場面もある。
- ページキャッシュを全消去すると、直後のアクセスが一斉に PHP と DB に流れる。アクセスの多い時間帯に行うと、一時的にサーバーへの負荷が高まる
- 共有ストアのオブジェクトキャッシュを消すと、他のサイトにも影響が出ることがある
- OPcache のリセット直後は、PHP ファイルの再コンパイルが走るため、一時的に応答が遅くなる
どの層に対する操作なのかを意識して、必要な層だけを、影響の少ない時間帯に行う。これが基本になる。
まとめ
- ページキャッシュは完成した HTML、オブジェクトキャッシュは DB から取り出した値、OPcache は PHP のコードを保存する。保存するものも、消す方法も違う
- 標準のオブジェクトキャッシュはリクエスト内でしか残らない。リクエストをまたぐには、Redis などと
object-cache.phpによる永続化が要る - PHP ファイルを差し替えても反映されないときは OPcache を疑い、
wp cache flushでは消えないことを覚えておく - 「全部消す」より、症状から層を絞って1つずつ確かめるほうが、原因の特定にも負荷の管理にも向いている
更新作業の前後に「どの層のキャッシュが効いているか」を把握しておくと、更新後の表示確認で迷いにくくなる。更新前にバックアップを取る考え方は3-2-1 ルールの記事にまとめている。