読者です 読者をやめる 読者になる 読者になる

setchi’s blog

コードに埋もれてます。

【FuelPHP】 minifyしたHTMLをデータベースにキャッシュする

前回FuelPHPからHTMLをminifyして出力するメモをしましたが、
圧縮処理に時間がかかっているようだったので、圧縮した結果をデータベースにキャッシュすることにしました。

【条件】

  • ボトルネックになっているのは、HTMLの圧縮処理そのもの
  • 同じURLでも、ユーザーごとに表示される内容が違う
  • 一度生成したページの内容が更新されることは少ないが、更新された場合は即反映したい

【データベース】

CREATE TABLE t_page_cache (
    f_user_id VARCHAR(115),
    f_uri VARCHAR(100),
    f_page MEDIUMTEXT,
    f_cache MEDIUMTEXT,
    f_date DATETIME,
    PRIMARY KEY (f_user_id, f_uri)
) ENGINE=MYISAM DEFAULT CHARSET=utf8 collate = utf8_unicode_ci;

FuelPHP

FuelPHPのモデルは、前回のものを拡張します。

<?php
require_once APPPATH.'classes/model/vendor/autoload.php';

class Model_Html extends Model
{
    public static function minify($view, $uri = false)
    {
        // viewオブジェクトを強制レンダリングして文字列化
        $view = $view->render();
        // minify時のオプション
        $minifyOption = array(
            'doctype' => 'html5',
            'optimizationLevel' => 1
        );
        // uriが指定されていなかった場合は、強制的にminifyしてリターン
        if ($uri === false)
        {
            return zz\Html\HTMLMinify::minify($view, $minifyOption);
        }

        // ユーザーID取得
        $user_id = Session::get('user_info.id', '');

        // $user_idと$uriをもとに、該当する圧縮データを取得
        $query = DB::select('f_page', 'f_cache')
            ->from('t_page_cache')
            ->where('f_user_id', $user_id)
            ->and_where('f_uri', $uri)
            ->limit(1)->execute()->as_array();

        // 検索結果が存在した場合
        if (0 < count($query))
        {
            // 圧縮前HTMLが等しかったら、キャッシュを返す
            if ($query[0]['f_page'] === $view)
            {
                return $query[0]['f_cache'];
            }
            // 圧縮前HTMLが違っていたら、圧縮し直し&DB更新
            else
            {
                $minview = zz\Html\HTMLMinify::minify($view, $minifyOption);
                DB::update('t_page_cache')
                    ->set(array(
                        'f_page' => $view,
                        'f_cache' => $minview,
                        'f_date' => date('Y-m-d H:i:s')
                    ))->where('f_user_id', $user_id)
                    ->and_where('f_uri', $uri)
                    ->execute();

                return $minview;
            }
        }
        // 検索結果が存在しない場合、DBにデータを追加する
        else
        {
            $minview = zz\Html\HTMLMinify::minify($view, $minifyOption);
            DB::insert('t_page_cache')
                ->set(array(
                    'f_user_id' => $user_id,
                    'f_uri' => $uri,
                    'f_page' => $view,
                    'f_cache' => $minview,
                    'f_date' => date('Y-m-d H:i:s')
                ))->execute();

            return $minview;
        }
    }
}

コントローラ

<?php
...
    $view = View::forge('index');
    $view->header = View::forge('header');
    $view->footer = View::forge('footer');

    // viewとURLを渡して、minifyした結果をリターン
    return Response::forge(Model_Html::minify(
        $view,
        Uri::current()
    ));

FuelPHPでURLを取得するメソッドは色々ありますが、その中でもUri::current()は、routes.phpのパターン内部で定義されているベースURLが取得できます。
例えば、存在しないURLにアクセスされた場合は、404ページのURLが返されるので、データベースのキャッシュ数が少なくなります。

ベンチマーク

Ajaxで500回アクセスして、レスポンスタイムの平均(ms)を求めました。
キャッシュ適用前: 304.8196100000467
キャッシュ適用後: 132.88778999996836

レスポンスタイムは1/2以下になって、体感的にも速くなったと感じられました。
ただ、あくまでリリース前の状態なので、定期的に計測しながら様子を見たいと思います。