Y-110's Wiki
PEAR:Cache_Lite
_ Cache_Lite でお手軽キャッシング
Cache_Lite は手軽にキャッシュ機能を実現できる PEARライブラリです。
出力キャッシュ・関数キャッシュをサポートしています。
メンテナンスも頻繁に行われており, 安心して導入できるライブラリです。*1
_ キャッシュの意義
Webアプリケーションにキャッシュを導入することで, ページを表示するための処理がバイパスできます。
例えば, 毎回データベースに接続して重い処理を行うといった処理がなくなり, 全体的にかなりのパフォーマンス向上が期待できます。
以下のようなページはキャッシュの導入を検討してみて下さい。
- あまり変化のないページ
- 統計データ表示
- リアルタイム性がさほど重要ではないページ
- ショッピングサイトの商品リスト
- 掲示板の投稿・ブログの記事
_ install
現時点(2006/5/30)での最新版は 1.7.1 です。
インストールは pear コマンドで行います。
# sudo pear install Cache_Lite
pear.php.net からアーカイブをダウンロード&解凍して, インクルードパスが通っているディレクトリにコピーしても OK。
_ 使い方
_ 出力キャッシュ
- Cache_Lite
Cache_Lite を使った出力キャッシュの例は以下の様になります。
require_once 'Cache_Lite.php';
// キャッシュの保存場所とキャッシュの生存期間を指定
$options = array(
'cacheDir' => '/tmp/cache/',
'lifeTime' => 600
);
$cache = new Cache_Lite($options);
$id = 'page1';
// 指定id のキャッシュが存在するかどうか
if ($data = $Cache_Lite->get($id)) {
// キャッシュがあるのでキャッシュされたデータを表示
print($data);
// キャッシュがなかったら本来の処理を実行
} else {
$message = 'Hello World';
print($message);
// データをキャッシュする
$cache->save($message);
}
オプションでキャッシュファイルを保存するディレクトリと保存期間を指定します。
最初に Cache_Lite::get() で指定したID のキャッシュファイルが存在するかどうか調べます。
キャッシュが存在すればキャッシュ内容を表示し, 存在しなければ Cache_Lite::save() で指定した変数データをキャッシュします。
- Cache_Lite_Output
Cache_Lite::save() は指定された変数の内容をキャッシュに保存しますが, 出力内容を一つの変数に入れるのが難しい場合もあります。
このようなケースの場合は Cache_Lite_Output を使えば指定した範囲内をキャッシュすることが可能です。
require_once('Cache/Lite/Output.php');
// キャッシュの保存場所とキャッシュの生存期間を指定
$options = array(
'cacheDir' => '/tmp/cache/',
'lifeTime' => 600
);
$cache = new Cache_Lite_Output($options);
$id = 'page1';
// 指定id のキャッシュが存在するかどうか
if (!($cache->start($id))) {
echo "Hello Cache_Lite_Output World !!";
echo "hoge";
echo "fuga";
// start から end までの出力内容をキャッシュする
$cache->end();
}
Cache_Lite::start() で指定したID のキャッシュが存在すればその内容を表示します。
キャッシュが存在しない場合は, Cache_Lite::start() 〜 Cache_Lite::end() の間に出力された内容が全てキャッシュされます。
start 〜 end の組み合わせは何回でもコールできるので, 出力ブロックごとに分けてキャッシュすることもできます。
_ 関数キャッシュ
- Cache_Lite_Function
Cache_Lite は関数の戻り値もキャッシュすることが可能です。
関数の戻り値をキャッシュすることで, 関数を実行することなく結果を返すことができます。
require_once("Cache/Lite/Function.php");
$options = array(
'cacheDir' => '/tmp/cache/',
'lifeTime' => 600
);
$cache =& new Cache_Lite_Function($options);
// 通常関数のキャッシュ
function hoge($a, $b)
{
...
}
$ret = $cache->call('hoge', 100, 200);
// クラス関数のキャッシュ
class Hoge
{
// スタティック関数
function hoge($a, $b)
{
...
}
function fuga($a, $b, $c)
{
}
}
// スタティック関数のキャッシュ
$ret = $cache->call('Hoge::hoge', 100, 200);
// メンバ関数のキャッシュ
$obj = new Hoge();
$ret = $cache->call('obj->fuga', 100, 200, 300);
Cache_Lite_Function::call() は, 基本的には第一引数に関数名, 第二引数以降にコールする関数の引数(可変)を指定します。
キャッシュが無い場合は, 実際に関数が実行されてキャッシュファイルが生成されます。
キャッシュがある場合はその内容を返すだけなので, キャッシュの有効期限の間にコールされれば瞬時に結果が返されます。
オブジェクトのメンバ関数をコールする場合は, 以下の様に第1引数に配列を渡す方法でも可能です。
$cache->call(array(&$this, 'fuga'), 100, 200, 300);
第1引数の配列の要素はオブジェクトの参照と関数名です。*2
オブジェクトのメンバ関数をコールする場合は, こちらの方法が推奨されています。
_ オプション
Cache_Lite の主要なオプションは以下の通りです。
| オプション | デフォルト値 | 説明 |
|---|---|---|
| cacheDir | /tmp/ | キャッシュを保存するディレクトリ。 |
| lifeTime | 3600 | キャッシュの有効期限(秒)。 |
| fileLocking | true | ファイルロックを有効にするかどうか。ロックが有効だと, 同時アクセスによるキャッシュファイルの破壊を防ぎます。 |
| writeControl | true | キャッシュ作成時にキャッシュファイルが壊れていないかどうかをチェックする。キャッシュ作成処理が若干遅くなる。 |
| readControl | true | キャッシュ作成時にハッシュ値を埋め込んで, キャッシュ読込み時のハッシュ値と比較する。データの破壊や改ざん等がチェックできる。 |
| readControlType | crc32 | readControl有効時のハッシュ関数。md5, crc32, strlen の3つが選択でき, 左のハッシュ関数ほど安全だが遅くなる。 |
| automaticSerialization | false | キャッシュを自動的にシリアライズして保存するかどうか。主に文字列以外のデータをキャッシュする場合に使用する。 |
| automaticCleaningFactor | 0 | キャッシュ生成時に期限切れのキャッシュファイルを自動的に削除するかどうか。0 は削除しない。1 以上に指定(x)すると 1/x の確率で削除処理が走る。20〜200 位の間が望ましい。 |
| hashedDirectoryLevel | 0 | キャッシュを保存するディレクトリの階層数。0 はディレクトリを作成しない。キャッシュファイルの数が膨大になる可能性がある場合, ディレクトリ階層を指定すると高速に動作する。1 か 2 位が望ましい。 |
メモリ系のオプション(memoryCachingとか)もありますが, キャッシュをメモリに永続的に保存するのではなく, 単純に変数の中に保存するオプションです。
スクリプトが終了したらキャッシュが消えてしまいますので, 一つのスクリプトで何回も同じデータを使いまわすような時に指定すれば効果がありそうです。
_ 注意点
Cache_Lite の効果を最大限に発揮するためには, ページをキャッシュしない時にのみ 他のパッケージを読み込んで処理を行うようにします。
-悪い例
require_once("Cache/Lite.php");
require_once("...")
require_once("...")
$cache = new Cache_Lite();
if ($data = $Cache_Lite->get($id)) {
echo($data);
} else {
...
$Cache_Lite->save($data);
}
-良い例
require_once("Cache/Lite.php");
$cache = new Cache_Lite();
if ($data = $Cache_Lite->get($id)) {
echo($data);
} else {
require_once("...")
require_once("...")
...
$Cache_Lite->save($data);
}
複数の Webサーバから同時にキャッシュ生成処理が行われる可能性がある場合, cacheDir に NFSマウントしているディレクトリを指定しないで下さい。*3
もし指定してしまうと, 同時にキャッシュが作られた場合に簡単にファイルが壊れてしまいます。
NFSサーバとの通信コストも結構かかりますので, キャッシュファイルを NFSで共有するよりは, 個々のサーバにキャッシュファイルを持たせたほうがいいでしょう。
一応 NFS対応の Cache_Lite 1.3 のパッチがあります。1.7 でも使えるかどうかは保証しませんが, やっていることは単純です。
automaticCleaningFactor が 0 の場合は, cron 等を使って自分で古くなったキャッシュを消す処理が必要です。
_ 裏技Cache_Lite
キャッシュがない場合, Cache_Lite は通常の処理を行って結果をキャッシュします。
しかし, キャッシュが作成されるまでの間に同じページに多数のリクエストが来ると, その都度, 通常の処理が行われます。*4
その通常の処理がかなり重い場合は, それだけでシステムにとってかなりの負荷となる場合もあるでしょう。
処理が何回も実行されてしまうのを回避する方法として, キャッシュ生成用のリクエストと通常のユーザのリクエストを分けて考えます。
キャッシュ生成用リクエストとユーザリクエストの Cache_Liteオプションは以下の様に設定します。
- キャッシュ生成用
$options = array('lifeTime' => 0);
- ユーザリクエスト用
$options = array('lifeTime' => 99999999); // lifeTime は実質無限
キャッシュ生成用リクエストを cron で定期的に送信し, 常にこちらでキャッシュを生成するようにします。
lifeTime は 0 に設定されているので, リクエスト毎にキャッシュを生成します。
ユーザリクエストは lifeTime が実質無限なので, 常にキャッシュを参照し続けます。*5

ちなみにお金の方のキャッシングは cashing です。ご利用は計画的に。
内部的に call_user_func_array 関数が使われています
flock(2) は NFS上のファイルをロックしないからです
キャッシュファイルを作成する処理を行うのは最初の1回だけ
キャッシュファイルが無いとキャッシュ作成処理が走るので, 予めキャッシュは生成されていなければなりません