【WV.4】動的WordPressサイトでどこまでTTFBを下げられるか

Core Web Vital に関する連載の第4回目では、ページキャッシュを導入することが難しいサイトでどのようにTTFB(Time To First Byte)つまりユーザーがHTMLを受け取るまでの時間を短縮することができるのかを紹介したい。以前紹介した通り、ページキャッシュのない状態でTTFBを0.2秒以下にするのはけっこう難しいのだが、がんばればやってやれないことはない。

なにより、ページが早く表示されることはユーザーにとってよい体験なので、Page Speed Insightsがどうこう以前にWordPressのチューニングをしておくことはよいことである。本稿で紹介する情報は、一般的なWordPressサイト構築におけるTipsとしても役立つはずだ。

基本方針は以下の通り。

  1. オブジェクトキャッシュの利用
  2. 再利用される要素のキャッシュ
  3. 逆にページキャッシュする

一点補足。「サーバーのチューニング」というのも速度向上に関わってくるが、レンタルサーバーの読者も想定しているので、今回はWordPressをいじることで向上できる部分に限定する。ディスクを揮発性エフェメラレルストレージにする、インスタンスサイズを上げる、PHP-FPMやNginxの設定をいじるなど色々やれることはあるにはある。

それでは、一つ一つ紹介していこう。

オブジェクトキャッシュの利用

オブジェクトキャッシュはWordPressにデフォルトで備えられたキャッシュ機能であり、データベース以外に永続化されたデータを保持することができる。WordPressの開発者ドキュメントおよびCapital Pのキャッシュの記事も参考にしてほしい。

まず、WordPress開発に限らず、Webアプリケーション開発全般にいえるのだが、データベース接続へのアクセスは信じられないほど遅いと考えておこう。検索やユーザーデータなどの「その時の最新の情報を返す必要があるもの」以外は極力データベースへアクセスしたくないものだ。ここでオブジェクトキャッシュが活躍するのである。

オブジェクトキャッシュとは

オブジェクトキャッシュのデータの保存先としては以下のようなものがある。

データストア対応プラグイン対応環境
ファイルシステムW3 Total Cacheなどレンタルサーバー
APCuWP LCacheレンタルサーバー
MemcachedMemcached Object Cacheクラウド、VPS
RedisRedis Object Cacheクラウド、VPS
オブジェクトキャッシュのデータストアと対応環境

まず、ファイルシステムへの保存だが、これはあまりお勧めしない。うっかりキャッシュデータを公開可能な領域においてしまうと、情報漏洩が懸念されるからだ。共有レンタルサーバーだとPHP拡張のAPCuぐらいしか選択肢がない(e.g. さくらのレンタルサーバー)ので、その場合は WP LCache を使おう。ただし、あまり熱心にはメンテされていない様子だが……。

VPSあるいはクラウドホスティングの場合は、MemcachedあるいはRedisを利用できる。AWSならElasticacheのようなサービスを使うとサーバーのお守りが楽だ。また、キャッシュサーバーを用意することにより、複数台構成のWordPressでもデータの一貫性が保たれる——というより、複数台構成の場合は必ずキャッシュサーバーを用意しなければならない

複数台構成の場合、キャッシュサーバーとWebサーバーの間には通信が発生するので、一台の場合と比較するとネットワーク遅延レイテンシが増えることは覚えておこう。一台の巨大なサーバーから複数台構成に移った場合、この部分はどうしても遅くなる。

MemcachedとRedisの場合はそれほど違いはなく、あとはプラグインなどがメンテされているかどうかだ。筆者の知る限り、AutomatticはMemcachedを使っており、Human MadeなどはRedisを利用している。AWSが得意なところはRedisを好む印象だ。筆者の運営するKunoichi Hostingでは、ElasticacheのRedisを利用している。

オブジェクトキャッシュを使うメリット

オブジェクトキャッシュを利用しただけで、次の2つのメリットがすぐに発生する。

  1. オプション関数 get_option などがキャッシュされる。
  2. WP_Query がキャッシュされる。 User_Query などの *_Query は全部対象になる。間違い。シングルページで使うget_post() やユーザーデータがデフォルトでキャッシュされる。WP_Queryは特別な処理が必要。

ユーザーAがサイトを訪れたあとにユーザーBがサイトを訪問したとしよう。その場合、Aに対してはデータベースアクセスが発生するが、Bにはオブジェクトキャッシュでデータを返すため、高速になる。また、全ページのサイドバーに新着投稿リストがあるようなサイトでは、オブジェクトキャッシュの効果は大きくなる。

言語ファイルのキャッシュ

その他、おすすめのオブジェクトキャッシュ活用法としては、言語ファイルのキャッシュだ。WordPressの言語ファイルはgettextという技術をベースにしているが、すべてのリクエストでこのファイルを毎回パースしている。この部分をオブジェクトキャッシュに格納するプラグイン MO Cache を利用することで、速度の向上が見込める。メンテされていないプラグインだが、コードが非常に短いので特に問題はないだろう。

再利用される要素のキャッシュ

さて、多くのサイトではフッター、ヘッダーなどの再利用されるパーツがある。これらは滅多に変わることはないが、たとえばサイトのヘッダーなどは全ページで表示される。

赤はサイトの共通パーツ

ページキャッシュが使えない場合、これらのパーツの出力をキャッシュすることで速度の向上が見込める。具体的にやり方を説明しよう。次のようなサイドバーがあったとする。

<aside id="sidebar" class="sidebar">
    <?php dynamic_sidebar( 'general' ); ?>
</aside>

このサイドバーは滅多に変更されないので、60分のキャッシュを設定することにしよう。

<aside id="sidebar" class="sidebar">
<?php
$cache = wp_cache_get( 'sidebar_general', 'my-theme' );
if ( false === $cache ) {
    // キャッシュはないので出力バッファを開始
    ob_start();
    // サイドバー出力
    dynamic_sidebar( 'general' );
    // 出力を変数に格納し、出力バッファ終了で出力するはずだったものを削除。
    $output = ob_get_contents();
    ob_end_clean();
    // 出力を60分キャッシュに保存。
    wp_cache_set( 'sidebar_genral', $output, 'my-theme', 60 * 60 );
    // 再度出力。
    echo $output;
} else {
   // キャッシュがあるので、そのまま出力。
   echo $cache;
}
?>
</aside>

上記のようにすれば、サイドバーは60分間キャッシュされる。もちろん、実際の運用ではウィジェットに変更のあったタイミングでキャッシュが消されたりする方が都合が良いだろう。このサイトのテーマ Side Business では自作のcomposerライブラリ kunoichi/set-menu でウィジェットおよびメニューのキャッシュができるようにしてある。

例えばメガメニューのようなものがあるサイトだと、実はメニューの出力だけで結構な時間がかかっていたりするので、オブジェクトキャッシュに格納することで大幅な速度向上が見られる。

Query Monitorというプラグインを利用すると、「何回データベース読み込みが発生しているか」「遅いクエリはどれか」「オブジェクトキャッシュは使われているか」ということがすぐにわかるので、お勧めである。XHProf拡張を入れると、さらに詳細がわかるようになる。

逆にページキャッシュする

最後になってこんなことを言うのもなんだが、動的なサイトでもページキャッシュを活用することは可能だ。ただし、ある程度の開発力は求められる。

有料会員専用の記事を持つこのサイトでもCDN(CloudFlare)によるページキャッシュは行っている。利用プラグインはCookie Tastingで、実装概要についてはダイナミックページのキャッシュについての記事をご覧いただきたい。

またヘッドレスCMSとして作成することも一つの選択肢だ。Shifter Headlessでは、WordPressをヘッドレスCMSとして利用できる。上記のダイナミックページキャッシュでの開発方法は「WordPressに詳しくPHPもJavaScriptもかなり書ける」というレアな人材を必要とする一方、ヘッドレスCMSであれば最新のJAMstackフレームワークに詳しい人材がいればよい(多分)ので、そちらの方がチームビルディングは簡単かもしれない。

なんにせよ、キャッシュできるものはなんでもキャッシュするに越したことはないので、開発力があればそちらの方に舵を切った方が良いだろう。

まとめ

それでは、動的なサイトにおいてTTFBを下げための方法をまとめる。

  1. オブジェクトキャッシュを使う
  2. サイトで共通のパーツ(メニュー・ウィジェット)は丸ごとキャッシュする
  3. つよつよエンジニアがチームにいるならページキャッシュできるように頑張る

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください