2.5以前の WordPress には Object Cache という機能がありました。
最近の WordPress では、この機能はデフォルトでは使えない状態になっているのですが、wp-content フォルダに object-cache.php というファイルを置き、wp-config.php に "define(’ENABLE_CACHE’,true);" を追加することで、この機能が使用できるようになります。
この object-cache.php は、WordPress のコアには含まれていないので、どこからか調達してくる必要があります。
バックエンドに何を使うかにも寄りますが、ファイルや memcached に Cache 情報を持たせるモジュールが提供されています。
参考URL:
- File-Based Extension to the WordPress Object Cache@dogmap.jp
- Memcached backend for the WP Object Cache@hiromasa.another
で、この object-cache.php を書いてしまえば、Memcached やファイルだけでなく、軽量データベースライブラリ Tokyo Cabinet なんかもバックエンドのDBとして利用できるのです。
ってなわけで、レンタルサーバに Tokyo Cabinet をインストールして、WordPress から利用してみましょう。
Tokyo Cabinet のインストール
さくらのレンタルサーバにインストールする方法をざっくりと紹介。
参考にしたのは、BLOG::broomie.net さんの「さくらのレンタルサーバにTokyo Promenadeをインストールする方法」というエントリ。
まず、Tokyo Cabinet Files on SourceForge.net から、Tokyo Cabinet のソースをダウンロードしましょう。
2009年11月2日現在では、Ver.1.4.9 が最新バージョンのようです。
次に、ダウンロードしたファイルを展開後、configure -> make してインストールです。
ざっと、こんな感じ。
$ tar zxvf tokyocabinet-1.4.9.tar.gz $ cd tokyocabinet-1.4.9 $ ./configure -prefix=$HOME/usr/local $ gmake $ gmake install
注意点は、configure するときにプリフィックスをつけることくらいかな。
Tokyo Cabinet の PHP バインディングモジュールのインストール
次に Tokyo Cabinet の PHP バインディングモジュールphp_tokyocabinet をインストールしましょう。
最新バージョンは、php_tokyocabinet-0.3.0 のようなので、それをダウンロードします。
ざっと、こんな感じ。
$ tar zxvf php_tokyocabinet-0.3.0.tgz $ cd php_tokyocabinet-0.3.0 $ phpize $ setenv PKG_CONFIG_PATH $HOME/usr/local/lib/pkgconfig $ configure --enable-tokyocabinet $ gmake
# configure内でpkg-configを使うので、PKG_CONFIG_PATH をセットしています。
で、ルート権限があれば、このあと gmake install としてしまえば良いですが、共用レンタルサーバでは、それはできません。
なので、modules/tokyocabinet.so を ~/lib/ext とかにコピーしておいてあげましょう。
その状態で php.ini に以下の記述を追加すれば、OKです。
[tokyocabinet] extension_dir = /home/username/lib/ext extension = tokyocabinet.so
モジュールが読み込まれているかどうかは phpinfo() で確認できます。
object-cache の設置
あとは Object Cache のバックエンドとして、Tokyo Cabinet を使う object-cache.php を作成しましょう。
とりあえず、適当に書いたのが、こんな感じ。
<?php /* Name: Tokyo Cabinet Description: Tokyo Cabinet backend for the WP Object Cache. Version: 0.1.0 URI: Author: wokamoto Install this file to wp-content/object-cache.php */ function wp_cache_add($key, $data, $flag = '', $expire = 0) { global $wp_object_cache; return $wp_object_cache->add($key, $data, $flag, $expire); } function wp_cache_incr($key, $n = 1, $flag = '') { global $wp_object_cache; return $wp_object_cache->incr($key, $n, $flag); } function wp_cache_decr($key, $n = 1, $flag = '') { global $wp_object_cache; return $wp_object_cache->decr($key, $n, $flag); } function wp_cache_close() { global $wp_object_cache; return $wp_object_cache->close(); } function wp_cache_delete($id, $flag = '') { global $wp_object_cache; return $wp_object_cache->delete($id, $flag); } function wp_cache_flush() { global $wp_object_cache; return $wp_object_cache->flush(); } function wp_cache_get($id, $flag = '') { global $wp_object_cache; return $wp_object_cache->get($id, $flag); } function wp_cache_init() { global $wp_object_cache; $wp_object_cache = new WP_Object_Cache(); } function wp_cache_replace($key, $data, $flag = '', $expire = 0) { global $wp_object_cache; return $wp_object_cache->replace($key, $data, $flag, $expire); } function wp_cache_set($key, $data, $flag = '', $expire = 0) { global $wp_object_cache; if ( defined('WP_INSTALLING') == false ) return $wp_object_cache->set($key, $data, $flag, $expire); else return $wp_object_cache->delete($key, $flag); } function wp_cache_add_global_groups( $groups ) { global $wp_object_cache; $wp_object_cache->add_global_groups($groups); } function wp_cache_add_non_persistent_groups( $groups ) { global $wp_object_cache; $wp_object_cache->add_non_persistent_groups($groups); } class WP_Object_Cache { var $global_groups = array( 'users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss' ); var $no_hdb_groups = array( 'posts', 'comment', 'counts' ); var $autoload_groups = array( 'options' ); var $cache = array(); var $db = array(); var $stats = array(); var $group_ops = array(); var $pre; var $cache_enabled = true; var $default_expiration = 0; function WP_Object_Cache() { __construct(); } function __construct() { global $table_prefix; $this->pre = $table_prefix; foreach ( $this->autoload_groups as $group ) { $this->get_hdb($group); } } function add($id, $data, $group = 'default', $expire = 0) { $key = $this->key($id, $group); if ( in_array($group, $this->no_hdb_groups) ) { $this->cache[$key] = $data; return true; } $hdb =& $this->get_hdb($group); if ( $hdb !== FALSE ) { $result = $hdb->put($key, $this->maybe_serialize($data)); @ ++$this->stats['add']; $this->group_ops[$group][] = "add $id"; } else { $result = FALSE; } if ( false !== $result ) $this->cache[$key] = $this->maybe_serialize($data); return $result; } function add_global_groups($groups) { if ( ! is_array($groups) ) $groups = (array) $groups; $this->global_groups = array_merge($this->global_groups, $groups); $this->global_groups = array_unique($this->global_groups); } function add_non_persistent_groups($groups) { if ( ! is_array($groups) ) $groups = (array) $groups; $this->no_hdb_groups = array_merge($this->no_hdb_groups, $groups); $this->no_hdb_groups = array_unique($this->no_hdb_groups); } function close() { foreach ( $this->db as $bucket => $hdb ) $hdb->close(); } function delete($id, $group = 'default') { $key = $this->key($id, $group); if ( in_array($group, $this->no_hdb_groups) ) { unset($this->cache[$key]); return true; } $hdb =& $this->get_hdb($group); $result = ( $hdb !== FALSE ? $hdb->out($key) : FALSE ); @ ++$this->stats['delete']; $this->group_ops[$group][] = "delete $id"; if ( false !== $result ) unset($this->cache[$key]); return $result; } function flush() { return true; } function get($id, $group = 'default') { $key = $this->key($id, $group); $hdb =& $this->get_hdb($group); if ( isset($this->cache[$key]) ) $value = $this->maybe_unserialize($this->cache[$key]); else if ( in_array($group, $this->no_hdb_groups) || $hdb === FALSE ) $value = false; else $value = $this->maybe_unserialize($hdb->get($key)); @ ++$this->stats['get']; $this->group_ops[$group][] = "get $id"; if ( NULL === $value ) $value = false; $this->cache[$key] = $this->maybe_serialize($value); if ( 'checkthedatabaseplease' == $value ) $value = false; return $value; } function key($key, $group) { if ( empty($group) ) $group = 'default'; $group = $this->pre.$group; return preg_replace('/\s+/', '', "$group:$key"); } function replace($id, $data, $group = 'default', $expire = 0) { $result = $this->add($id, $data, $group, $expire); return $result; } function set($id, $data, $group = 'default', $expire = 0) { $result = $this->add($id, $data, $group, $expire); return $result; } function &get_hdb($group) { if ( empty($group) ) $group = 'default'; if ( in_array($group, $this->no_hdb_groups) ) return FALSE; $group = $this->pre . $group; if ( !isset($this->db[$group]) ) { $this->db[$group] = new TCHDB(); $this->db[$group]->open(trailingslashit(WP_CONTENT_DIR) . 'cache/' . $group . '.hdb', TCHDB::OWRITER | TCHDB::OCREAT); } return $this->db[$group]; } function maybe_serialize( $data ) { if ( is_array( $data ) || is_object( $data ) ) return serialize( $data ); if ( is_serialized( $data ) ) return serialize( $data ); return $data; } function maybe_unserialize( $original ) { if ( is_serialized( $original ) ) // don't attempt to unserialize data that wasn't serialized going in return @unserialize( $original ); return $original; } } ?>
ただし Tokyo Cabinet は TCHDB::OWRITER オプションで、DBをオープンしている際はテーブルロックをかけてしまうので、このままではパフォーマンスが悪いです。
その辺も、考慮してこのモジュールを修正すれば、結構イケるのでは無いでしょうか?
共用レンタルサーバだから memcached は使えないんだよなぁという人はチャレンジしてみてください。