自作の WordPress テーマ Celtis-one を少しずつ改善したりウィジェット等の限られた範囲を部分的にキャッシュして高速化を図ってきたのですが、先日試しに WP Super Cache を入れてみたら、応答時間がガツンと速くなりページキャッシュの飛び道具的な効果の仕組みを知りたくなりました (^^)
ページキャッシュについて
ページキャッシュというとサーバーレベルでキャッシュする方法とPHP(WordPress)でキャッシュする方法があるようです
今回は、WordPressでのページキャッシュについて調べてみます
仕組みとしては、リクエストされたページのブラウザへの出力をキャッシュとして保存し、次に同じページヘのリクエストがあった場合は保存してあるキャッシュデータを出力するだけです
これだけで、ページキャッシュを使用するかどうかで数百ミリ応答時間が速くなります
ちなみに応答時間とは、ブラウザの開発ツールで見た場合の最初のリクエストデータを受信するまでの時間です
この待機時間の部分はPHPプログラムによるWordPressの起動から応答処理を行うまでの処理時間となります
ページキャッシュの目的は、この待機時間を短くすることです
ページキャッシュの効果を確かめる
例えば、ある投稿ページをリクエストすると通常1秒前後の応答時間がかかったとします

同じページを WP SUper Cache を入れて見るとなんと 30ms しかかかりません (^^)

ページキャッシュにより1秒近く応答時間を短縮できています
※ページキャッシュは、最初の待機時間が短くなるだけなので、キャッシュを入れたのにあまり速くならないという場合は、その後の画像ファイルやCSS,JSファイル等が大きすぎる、多すぎることが考えられます
ページキャッシュの副作用
ページキャッシュは、高速化の対策の1つとして大変効果的なため多くのプラグインが公開されているのですが、良いことばかりでなく副作用があります
それは、キャッシュを作成したら、適切なタイミングでキャッシュを破棄、再生成しなければ、古いコンテンツがいつまでも出力されてしまうということです
例えば、下記のような状態の変更に対応する必要があります
- 記事内容を更新
- コメントが投稿されスパムでなければキャッシュを更新
- プラグインの変更
- CSS,JSの変更
- ウィジェットの変更
- テーマの変更
- WordPress のオプション設定の変更が関連する場合
- 人気記事等のカウンターが変わった場合(外部サイトからスクリプトで取得する場合以外)
- ログインユーザーに対してはキャッシュしない
- 特定ページのキャッシュからの除外指定
これらの処理を適切に処理するためにはその設定と処理が必要になり、処理するプログラムや管理のための設定も複雑化していきます
WordPress起動とキャッシュ処理のタイミング
かなり大雑把ですが WordPress 起動時の処理はこんな感じで行われています

キャッシュプラグインの多くは、WordPress メイン構築処理の一番最初のコア読み込み前にキャッシュがヒットしたかを判定して、ヒットすればキャッシュデータをブラウザに出力して終了です
ヒットしなければ、それ以降の処理を通常どうり行って、リクエストされたページデータのブラウザへの出力とページキャッシュデータの生成を行います
※WordPress メイン構築処理は、wp-settings.php というファイルで行われていますので、詳細は WordPress のソースコードを参照して下さい
待機時間=PHP処理時間
先ほどのページキャッシュの効果で見た待機時間の違いは、このWordPressの起動シーケンスにおけるPHP処理時間の差ということです
メイン構築処理の最初の Drop in プラグインで行った WP Super Cache のページキャッシュの出力までが 30ms で、その後の応答処理までの時間に1秒近くかかっていることになります
※この1秒近い時間の多くは、プラグインプログラムの読み込みと翻訳ファイルの読み込みが占めています。素の WordPress なら環境にもよりますが 200ms 前後と思われます
※待機時間にはサーバー間との通信時間も含まれます。WP Super Cache のページキャッシュの出力までが 30ms のうちの 10~20ms は通信時間と思われます
キャッシュの目的と有効期間の関係
高速化の為にページキャッシュは使いたいけど、いろいろ副作用もあるし、設定も面倒と思っていたのですが、視点をかえて目的をアクセス集中時の負荷低減とすれば、キャッシュの有効期間を短くして副作用を減らすことが可能です
高速化のためでなく、なんちゃら砲のようなアクセス集中時の負荷低減用に3分間のキャッシュします(アクセスがあればその都度延長する)
副作用も気にならないし、こんなのもありかなと思いプラグインにしてみました
ちなみに私のブログではアクセス集中したことがないので、どの程度効果があるかは未知数です
あくまで保険という意味での導入です (^^)
ultra-help-cache プラグイン
スーパーに対抗してウルトラとしたわけではありません。いざというときだけ助けにきてくれるウルトラマンのようなページキャッシュという意味合いでタイマーも3分です (^^)
※アクセス数が常時高い場合は、データ転送容量の制限等も有り、スペックの高いサーバーを利用する必要があると思いますが、たまーにアクセスが集中する程度でしたら、JetPackのPhotonを併用して画像データを外部サーバーから読み込むようにすればなんとか凌げるのではないかと思います
plugin-load-filter プラグインと組み合わせて使って頂ければ、普段もそれほど遅くなく、いざというときに速くなるはずです (^^)
Plugin Load Filter
自作ページキャッシュプラグインの効果検証
plugin-load-filter を使って読み込むプラグインを最適化した状態で同じ投稿記事を読み込んでみると応答時間 340ms 程度という許容できる時間(Google PageSpeed は 300ms 以内にしろと行ってくるかも知れませんが)です
表示ページに関係ないプラグインを無効にするだけでも結構高速化しています

次に ultra-help-cache を使い1回アクセスしてから3分以内に再度アクセスしてみます

WP Super Cache の処理位置より後で処理しているので効果は少し劣りますが 50ms 程度で応答出来ました
保険としては十分な時間と思います (^^)
プログラムインストール
より高性能なページキャッシュプラグインが既に有りますので、興味のある方はほとんどいないと思いますが、せっかく作成したので公開します (^^)
この機能は、celtispack プラグインをインストールして Ultra help cache モジュールを有効化すると使用できるようになります
ここで紹介している ultra-help-cache プラグインは、Celtispack プラグインの一つの機能として公開していましたが、2016/8 にこれをベースにもっと高性能なページキャッシュプラグインを作り公開したのに伴い Celtispack からも機能を削除しました
記事としてはそのまま公開していますが、実際にページキャッシュプラグインを使う場合には YASAKANI Cache プラグインをお使いください
WordPress Plugin : YASAKANI Cache
使い方
有効にするだけで設定もないので簡単です (^^)
ログインしていない状態でホーム、投稿、固定ページのアクセスがあればページキャッシュを3分間の期限で保存します
同じページのリクエストが3分以内にあれば、キャッシュを出力して負荷を低減し有効期間を延長します
3分以内にアクセスが無ければキャッシュは破棄されます
普段はなにも効果がありませんが、忙しい時だけ助けてくれるはずです
プログラムコード
全部で60行程度なので、特に説明しなくとも追いかければわかると思います
<?php
/*
Plugin Name: Ultra help cache
Description: 負荷低減用のお助けページキャッシュ(アクセス集中時の忙しい時にとりあえず3分間助けにきます (^^))
Version: 0.9.0
Author: enomoto@celtislab
Author URI: https://celtislab.net/
License: GPLv2
*/
/*
* このファイルを wp-content/mu-plugins へアップロードすれば必須プラグインとして動作します
*/
$ultra_help_cache = new ultra_help_cache();
class Ultra_help_cache {
const EXPIRATION = 3; //キャッシュの有効時間(min)
public function __construct() {
if(!is_admin()) {
add_filter( 'theme_root', array(&$this, 'page_cache_read'), 9);
add_filter( 'template_include', array(&$this, 'page_cache_write'), 9);
}
}
//ログインしていなければ、ホーム、投稿、固定ページのページキャッシュデータを生成する
public function page_cache_write( $template )
{
if(!is_user_logged_in() && (is_home() || is_singular())) {
global $post, $page, $posts, $paged;
ob_start();
include( $template );
$keyid = md5('pagecache_'. (string)wp_is_mobile() . $_SERVER['REQUEST_URI'] );
$cache = ob_get_flush();
set_transient( $keyid, $cache, Ultra_help_cache::EXPIRATION * 60);
return false;
}
else {
return $template;
}
}
//ホーム、投稿、固定ページへのアクセス集中時に負荷低減の為にページキャッシュデータを表示する
public function page_cache_read( $theme_root )
{
if (did_action( 'plugins_loaded' ) === 0 ) {
$keyid = md5('pagecache_'. (string)wp_is_mobile() . $_SERVER['REQUEST_URI'] );
$cache = get_transient( $keyid );
if($cache !== false){
//ログイン状態を取得するために本来はプラグインロード後に読み込まれる pluggable.php をロード前に読み込む
require( ABSPATH . WPINC . '/pluggable.php' );
if(!is_user_logged_in()){
echo $cache;
set_transient( $keyid, $cache, Ultra_help_cache::EXPIRATION * 60);
exit; //キャッシュヒット時はキャッシュ期限延長のみ(これ以降の処理は不要)
}
}
}
return $theme_root;
}
}
最新のプログラムファイルは、最新版の Celtispack プラグインの modules/mu-plugins/ultra-help-cache.php を参照して下さい。
副作用の少ないお手軽なキャッシュを紹介しました (^^)