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 は使えないんだよなぁという人はチャレンジしてみてください。
つぶやく
ブログにエントリ: WordPress Object Cache のバックエンドに Tokyo Cabinet を利用する http://dogmap.jp/Be [twicco.jp @wokamoto ]
.
『Tokyo Cabinet』ってしらなかった。 http://bit.ly/cfj0rm WordPress Object Cache のバックエンドに Tokyo Cabinet を利用する : dogmap.jp
WordPressのキャッシュに使用しているんですね。なるほど、そういう使い方が!! RT @wokamoto: @hftorisan ちなみに僕が Tokyo Cabinet をインストールしてみたときの記事です。 http://dogmap.jp/Be