赵荣涛's Archivers

From admin on 2011-07-08 02:08:05

Discuz!X1.5之缓存机制浅析

Discuz X中,缓存核心函数在function_core.php和function_cache.php中。其中以updatecache和loadcache两个函数最为核心。

这里简单叙述一下X里面,缓存的机制。
X的缓存,主要是存储不经常变动的数据,存储方式可以选择数据库,或者文件。下面举例说明。我们可以在后台的admincp_click.php,看到 updatecache('click'),证明click是使用了缓存机制的。Click就是表态图标,头像什么的信息,路过,雷人,鸡蛋等等,这些信息是​基本不修改的,并且会在前台很多地方用到。而如果不使用缓存的话,则每次都需要遍历一次click表,而如果有100个表态图像的话则会读100次库,每篇日志都会有cl​ick信息,如果有100篇日志则会读click表10000次。

X的做法就是,把类似click表的内容,序列化,存放在common_syscache表中,这样每次只需要读一次库,或者存放在文件中,每次都读这个文件。下面我们详​细看看代码是怎么运行的。我们这里模拟一个需要缓存的表message,只有两个字段,subject和content。当然 message这样的经常更新的数据是不适合缓存的,这里只是举例方便。
function updatecache($cachename = '') {

global $_G;

static $cachelist = array(''message); //把需要缓存的数据名,可自己添加。

$updatelist = empty($cachename) ? $cachelist : (is_array($cachename) ? $cachename : array($cachename));
foreach($updatelist as $value) {
getcachearray($value); //填充缓存数据
}
这里看看getcachearray()代码

function getcachearray($cachename, $script = '') {
global $_G;

$cols = '*';
$conditions = '';
$timestamp = TIMESTAMP;
switch($cachename) { //根据缓存名称按要求写相关代码,什么需要缓存,什么不需要缓存,我这里的conditions用默认的,就是缓存全部数据。
case 'message':
$table = 'message';
break;
…………
}
$data = array();
$query = DB::query("SELECT $cols FROM ".DB::table($table)." $conditions");
//读库,存为数组。
switch($cachename) {
case 'message':
while($row = DB::fetch($query)) {
$data['subject'][$row['id']] = $row['subject'];
$data['content'][$row['id']] = $row['content'];
$data['dateline'][$row['id']] = $row['dateline'];
}

save_syscache($cachename, $data); //序列化存到syscache表中。
return true;
}

看看save_syscache()是干嘛的。

function save_syscache($cachename, $data) {
static $isfilecache, $allowmem;
if($isfilecache === null) {
$isfilecache = getglobal('config/cache/type'); //取缓存类型,是file,还是sql。
$allowmem = memory('check'); //判断是否使用Memcached
}

if(is_array($data)) {
$ctype = 1;
$data = addslashes(serialize($data)); //序列化
} else {
$ctype = 0;
}
//入库
DB::query("REPLACE INTO ".DB::table('common_syscache')." (cname, ctype, dateline, data) VALUES ('$cachename', '$ctype', '".TIMESTAMP."', '$data')");

//memcached启用的话,清空缓存
$allowmem && memory('rm', $cachename);
//缓存file存在的话,删除文件
$isfilecache && @unlink(DISCUZ_ROOT.'./data/cache/cache_'.$cachename.'.php');
}

总结一下updatecache()的作用,读原始库,这里是message表,序列化存到syscache表的一条记录,并且清空memcached缓存和缓存的fil​e。
Updatecache => getcachearray => save_syscache

下面我们研究缓存模块的下半部分,loadcache();

function loadcache($cachenames, $force = false) {
global $_G;
static $loadedcache = array();
$cachenames = is_array($cachenames) ? $cachenames : array($cachenames);
$caches = array();
foreach ($cachenames as $k) {
if(!isset($loadedcache[$k]) || $force) {
$caches[] = $k;
$loadedcache[$k] = true;
}
}

if(!empty($caches)) {
$cachedata = cachedata($caches); //读数据库缓存或者file缓存
foreach($cachedata as $cname => $data) {
$_G['cache'][$cname] = $data;
$cachearr = $_G['cache'][$cname];
}
}
return $cachearr;

function cachedata($cachenames) {
static $isfilecache, $allowmem;

if($isfilecache === null) {
$isfilecache = getglobal('config/cache/type') == 'file';
$allowmem = memory('check');
}

$data = array();
$cachenames = is_array($cachenames) ? $cachenames : array($cachenames);
if($allowmem) {
$newarray = array();
foreach ($cachenames as $name) {
$data[$name] = memory('get', $name);//通过memcached取值
if($data[$name] === null) {
$data[$name] = null;
$newarray[] = $name;
}
}
if(empty($newarray)) {
return $data;
} else {
$cachenames = $newarray;
}
}

if($isfilecache) {
$lostcaches = array();
foreach($cachenames as $cachename) {
//这里include缓存file
if(!@include_once(DISCUZ_ROOT.'./data/cache/cache_'.$cachename.'.php')) {
$lostcaches[] = $cachename;
}
}
//如果已经有file,则不走$lostcaches[] = $cachename,$lostcaches没值,直接return file中的$data。
if(!$lostcaches) {
return $data;
}
$cachenames = $lostcaches;
unset($lostcaches);
}
$query = DB::query("SELECT /*!40001 SQL_CACHE */ * FROM ".DB::table('common_syscache')." WHERE cname IN ('".implode("','", $cachenames)."')");
while($syscache = DB::fetch($query)) {
$data[$syscache['cname']] = $syscache['ctype'] ? unserialize($syscache['data']) : $syscache['data'];
//通过memcached存到内存里
$allowmem && (memory('set', $syscache['cname'], $data[$syscache['cname']]));
//创建file,数据存到file中
if($isfilecache) {
$cachedata = '$data[\''.$syscache['cname'].'\'] = '.var_export($data[$syscache['cname']], true).";\n\n";
if($fp = @fopen(DISCUZ_ROOT.'./data/cache/cache_'.$syscache['cname'].'.php', 'wb')) {
fwrite($fp, "<?php\n//Discuz! cache file, DO NOT modify me!\n//Identify: ".md5($syscache['cname'].$cachedata)."\n\n$cachedata?>");
fclose($fp);
}
}
}

foreach ($cachenames as $name) {
if($data[$name] === null) {
$data[$name] = null;
$allowmem && (memory('set', $name, array()));
}
}

return $data;
}

查看完整版本: Discuz!X1.5之缓存机制浅析

Tags: discuz