cakephpのfindをmemcacheする
[お詫び]間違えがありましたので修正しました。getのとこの条件間違えてました。
新年初投稿で。よろしくお願いします。
今回はmemcacheとfindをからめたお話です。
Webサイトでキャッシュを使うことはパフォーマンスを高めるのにも有効です。
特にWebサイトのほとんどはDBも使うでしょう。
最近そんな私もDBを使用したサイトをcakeで作ってて、うまくキャッシュ使えないかなーと思ってて若干(?)むりやりですが、findメソッドをオーバーライドしてfindの結果をmemcacheに入れるようなプログラムを書いてみた。
cakephpのfind系のメソッド(findAllとか、findCountなど)はすべてfindを通しているので、findの部分をちょっと変更する。
app_model.php
ちと長いですが勘弁してください><findAllとfindCountはよく使うので一緒に書きました
uses("cache/Memcache"); class AppModel extends Model{ var $_mem_obj = null; var $_memcache_key = null; var $_mem_server = null; var $_mem_expire = null; var $_mem_timeout = null; /** * memcacheKey用のコントローラ名とアクション名を登録する */ function setMemcacheKey($controller, $action) { if(empty($controller)) return false; if(empty($action)) return false; $this->_memcache_key = $controller . "_" . $action; return true; } /** * memcache keyの作成 * * @param string $memkey ユニークなmemcache key * @return string */ function createMemKey($memkey) { if(empty($memkey)) return ""; return sha1($this->_memcache_key."_".$memkey); } function find($conditions = null, $fields = array(), $order = null, $recursive = null, $memcache=false) { // get if(!empty($memcache)) { $this->_mem_obj = $this->getMemcacheObj(); $memcache_key = $this->createMemKey($memcache); if(!empty($memcache_key)) { $result = $this->_mem_obj->read($memcache_key); if($result!==false) return $result; }else{ $this->log("Memcache Read key Error: key -> " . $memcache); } } $result = parent::find($conditions, $fields, $order, $recursive); // regist if(!empty($memcache)) { if(!empty($memcache_key)) { $mem_result = $this->_mem_obj->write($memcache_key, $result, $this->_mem_expire); if(!$mem_result) { $this->log("Memcache write Error: key -> " . $memcache); } }else{ $this->log("Memcache key Error: key -> " . $memcache); } //$this->_mem_obj->clear(); // memcache all clear } return $result; } /** * findAllオーバーライド */ function findAll($conditions = null, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null, $memcache=false) { return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive'), null, null, $memcache); } /** * findCountオーバーライド */ function findCount($conditions = null, $recursive = 0, $memcache=false) { return $this->find('count', compact('conditions', 'recursive'), null, null, $memcache); } /** * memcacheオブジェクトの生成と設定する */ function getMemcacheObj() { // memcache設定 $this->decideMemInfo(); if(empty($this->_memcache)) { $this->_memcache = new MemcacheEngine(); $setting = array( "servers" => array($this->_mem_server), ); $this->_memcache->init($setting); } return $this->_memcache; } /** * 開発環境を見て、つなぐmemcacheサーバを変える。 */ function decideMemInfo() { switch(php_uname('n')) { default: $server_name = 'localhost:11211'; $connect_timeout = 1; $cache_expire = 3; } $this->_mem_server = $server_name; $this->_mem_expire = $cache_expire; $this->_mem_timeout =$connect_timeout; return true; } }
app_controller.php
/** * memcacheを使う準備 * * @param array $models memcacheを使うモデル名 * @return boolean */ function setMemkey($models) { if(empty($models)) return false; foreach($models as $val) { $this->$val->setMemcacheKey($this->name, $this->action); } return true; }
使い方。
class UsersController extends AppController { function index() { $this->setMemkey(array("User")); $result = $this->User->find("all", null, null, null, "index"); }
findの最後の変数に、文字列を入れるとmemcacheに登録して、ある場合はmemcacheから返します。
keyの形はapp_model.phpのcreateMemKeyで生成していて
sha1(コントローラ名_アクション名_ユニークな値)
としています。かぶらないような値を考えた結果こんな感じになりました・・・。(memcacheのkeyの設計って皆さんどうしてるんですかね?><)
keyの長さが255文字までなので、sha1でhash値にしてます。そうすればユニークな値が長すぎてエラーになるってこともないので。
↑の例だと、controllerでfindを発行していますが、自分は基本controllerでfindを発行するようなことはなく、model内で整形して、controllerに返すようにしています。
そのため、modelからも使えるようにするため、modelからcontroller名やアクション名を登録することができないので、controllerからコントローラ名とアクション名を渡してます。
modelからfindする場合は、そのfunction名とかをmemcacheのkeyにするかとよいと思います。
こんな感じ(実際のコードではなく適当に参考程度に)
user.php
function getaUserFromID($id) { if(empty($id)) return false; $conditions = array("User.id"=>$id); $result = $this->find($conditions, null, null, null, "getaUserFromID_{$id}"); if(empty($result)) return false; return $result; }
とか書いてて思ったけど、idから取得するならreadでもいいのか。まぁいいか。
以下は随時追加していきます。
○メリット
- 最初の導入が手間だけど、そのあとはお手軽にfindの結果をmemcacheできる。
○デメリット
- ある程度app_modelをいじってからだとめんどいかも。
- expireを各findで設定できない。
- memcacheオブジェクトが各モデル内に生成されるのでちと微妙。
- findBy***では使えない。まぁそこは普通にconditionsで設定するようにすればいいかなーとか思うけど。
使うのはかまいませんが、自己責任でお願いします。
memcacheだとこんな使い方もあるよーってのがあったら教えてくださいませ。><
Tags: cakephp,memcache
Nice site really!
コメント by gambling casino — 2009 年 2 月 1 日 @ 5:07 AM