For some time we used RedisCache Yii extension to cache db scheme. But doing one performance optimization we come to idea that keep all db scheme in a single php-file will be much faster. We wouldn’t need to process ~20 (each for one table) read operations to redis and save certain amount on critical ms. All you need is just include a single file.
How to implement custom cache adaptor
Yii provides simple interface for cache implementation. All you need is extend your component from CCache and implement couple methods:
{
public function init()
{
}
public function set($id,$value,$expire=0,$dependency=null)
{
// save data to cache
return true;
}
public function get($id)
{
// get data from cache
return $value; // or false if not found
}
public function delete($id)
{
// delete data from cache
return true;
}
}
Then simply add your component into main.php:
“cache” is default key for caching component.
DB scheme cache
To turn on db scheme you will need to change db config:
'username' => 'user',
'password' => 'pass',
...
'schemaCachingDuration' => 3600,
'schemaCacheID' => 'cache',
schemaCachingDuration – cache expiration interval in seconds. Put 0 if about to disable cache. schemaCacheID – cache component key. By default Yii use “cache”.
DB scheme file cache extension
class DBSchemeCache extends CCache
{
private $_sets = array();
private $_cacheValues = array();
public $file = 'protected/runtime/db_scheme.php';
public function init()
{
parent::init();
Yii::app()->attachEventHandler('onEndRequest', array($this, 'endRequest'));
$this->_loadCacheFile();
}
public function set($id,$value,$expire=0,$dependency=null)
{
$this->_sets[$id] = $value;
return true;
}
public function get($id)
{
if (!empty($this->_cacheValues[$id])) {
return $this->_cacheValues[$id];
}
return false;
}
public function delete($id)
{
return true;
}
public function endRequest()
{
$this->_saveSets();
}
private function _saveSets()
{
if (!empty($this->_sets)) {
foreach ($this->_cacheValues as $key=>$value) {
if (!isset($this->_sets[$key])) {
$this->_sets[$key] = $value;
}
}
$cacheFileContent = '<?php $cache = array();' . PHP_EOL . PHP_EOL;
$cacheFileContent .= 'function o($class, $props) { $object = new $class(); foreach($props as $k=>$v){if (property_exists($object, $k)) $object->$k = $v;} return $object;};' . PHP_EOL . PHP_EOL;
foreach ($this->_sets as $key => $value) {
$cacheFileContent .= '$cache["' . addslashes($key) . '"] = ' . var_export($value, true) . ';' . PHP_EOL;
}
$cacheFileContent .= 'return $cache;';
$cacheFileContent = preg_replace('/(\w+?)::__set_state\(/is', 'o("\\1",', $cacheFileContent);
$fp = fopen($this->file, 'w');
fwrite($fp, $cacheFileContent);
fclose($fp);
}
}
private function _loadCacheFile()
{
if (is_file($this->file))
$this->_cacheValues = include($this->file);
}
}
As you can see caching db scheme for php file is quite simple. Just note that extension doesn’t support cache expire interval and for cache renew you just need to delete cache file (protected/runtime/db_scheme.php).
To separate db scheme cache from system cache, we’ll use special cache key – “dbschemecache”:
plug in component into main.php
database connection config
'username' => 'user',
'password' => 'pass',
...
'schemaCachingDuration' => 3600,
'schemaCacheID' => 'dbschemecache',
Thanks for reading. Hope you’ll find this article useful.
Comments (8)
And how much faster this solution is? Why not to store this ‘file’ in Redis?
> And how much faster this solution is? Just numbers on our environment with ~30 tables: - getting table schemes from redis: ~ 20ms - file cache: ~2-3ms
> Why not to store this ‘file’ in Redis? Honestly, we didn’t think about this option, but I’m not sure that serialization/deserialization of 30 objects and getting big string from redis will be faster either just ‘$dbScheme = include “db_scheme.php”‘. But we could check
Hey
Im glad that you were using the extension I made “RedisCache”
Nice tutorial
I also really enjoyed the JS tutorial “jQuery plugin carcass”
Cheers
Gustavo
Hi Gustavo, Glad to hearing from you and it is a good chance to say that we enjoyed using RedisCache. It is awesome.
Thanks for reading us. We will keep working hard to post something interesting.
Hi,
Nice article but why didn’t you used the CFileCache that is included in the framework?
Hi, CFileCache is fast too, but it use other way: file_get_content + unserialize instead of include “db_scheme_file.php”. Our solution give us opportunity to use opcode in additional.
And why not to implement delete method something like this?
Submitted a patch to this at: http://www.yiiframework.com/forum/index.php/topic/38223-database-schema-cache-in-single-php-file-onendrequest-error/page__view__findpost__p__185691 Have a look. thanks for the class though, quite helpful.