discuz缓存机制

一、 discuz缓存机制分析:

使用缓存机制的目的很明显,降低服务器性能的消耗,对于常用且变动比较小的数据的数据,可以尽可能的使用缓存来解决,代替最原生的不断的进行数据库查询匹配的过程。而Discuz中提供的数据缓存方式包括内存、数据库、文件三种方式,具体如下。

(1)、项目根目录/config/config_global.php配置数据库/文件缓存方式,当然还有第三种,就是内存缓存。

$_config['cache']['type'] = 'sql';//file

(2)、缓存数据获取:

根目录/source/function/function_core.php:

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 = C::t('common_syscache')->fetch_all($caches);
    foreach($cachedata as $cname => $data) {
      if($cname == 'setting') {
        $_G['setting'] = $data;
      } elseif($cname == 'usergroup_'.$_G['groupid']) {
        $_G['cache'][$cname] = $_G['group'] = $data;
      } elseif($cname == 'style_default') {
        $_G['cache'][$cname] = $_G['style'] = $data;
      } elseif($cname == 'grouplevels') {
        $_G['grouplevels'] = $data;
      } else {
        $_G['cache'][$cname] = $data;
      }
    }
  }
  return true;
}

从函数中可以看出,数据缓存其实相当于把一些常用的数据,通过特定的需求,根据缓存的方式存储于文件 或者 表(前缀)_common_syscache 或者 内存中,当需要使用到某个类型的数据的时候,只需要在代码中加入类似loadcache('setting');,就可以获取到缓存数据并赋值于自定义全局变量$_G中,即$_G['setting']; ,其中fetch_all中会判断当前使用的是哪一种缓存方式,如下该函数:

脚本:table_common_syscache.php

public function fetch_all($cachenames) {
    $data = array();
    $cachenames = is_array($cachenames) ? $cachenames : array($cachenames);
    if($this->_allowmem) {
      $data = memory('get', $cachenames);
      $newarray = $data !== false ? array_diff($cachenames, array_keys($data)) : $cachenames;
      if(empty($newarray)) {
        return $data;
      } else {
        $cachenames = $newarray;
      }
    }
 
    if($this->_isfilecache) {
      $lostcaches = array();
      foreach($cachenames as $cachename) {
        if(!@include_once(DISCUZ_ROOT.'./data/cache/cache_'.$cachename.'.php')) {
          $lostcaches[] = $cachename;
        } elseif($this->_allowmem) {
          memory('set', $cachename, $data[$cachename]);
        }
      }
      if(!$lostcaches) {
        return $data;
      }
      $cachenames = $lostcaches;
      unset($lostcaches);
    }
 
    $query = DB::query('SELECT * FROM '.DB::table($this->_table).' WHERE '.DB::field('cname', $cachenames));
    while($syscache = DB::fetch($query)) {
      $data[$syscache['cname']] = $syscache['ctype'] ? unserialize($syscache['data']) : $syscache['data'];
      $this->_allowmem && (memory('set', $syscache['cname'], $data[$syscache['cname']]));
      if($this->_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.getglobal('config/security/authkey'))."\n\n$cachedata?>");
          fclose($fp);
        }
      }
    }
 
    foreach($cachenames as $name) {
      if($data[$name] === null) {
        $data[$name] = null;
        $this->_allowmem && (memory('set', $name, array()));
      }
    }
 
    return $data;
  }

当服务器支持使用内存缓存时,使用该方式,DISCUZ代码中默认支持的方式包括redis、memcache、apc、xcache、eaccelerator、wincache这几种,至于使用哪一种,还需要取决于web 服务器支持方式,获取到数据且不空则返回其结果;

当服务器不支持内存缓存时,使用第二种文件缓存,文件缓存会将数据存放于目录 ./data/cache/  下,并取名为 cache_' .$cachename . '.php   ,如果获取到数据且不空,则返回其结果;

而数据库缓存查找操作是在前面两种不支持,或者执行完且数据为空的情况下,根据库缓存从数据库表(前缀)_common_syscache中获取存储数据,其实这里就不能描述为缓存,只是把数据按照一定的格式存放在数据库中,由于其使用其数据的客户端不需要经常实时变动,也就是说可以存在一定的延迟,才使用这样的方式,简化数据库操作,存储方式包括字段缓存变量名(如setting)、缓存类型(0,1是否为序列化数据)、缓存时间、缓存数据。

当执行了数据库查询操作之后,如果检测到存在内存缓存or文件缓存,由于从前面逻辑可以判断,如果支持该两种缓存方式,能够执行数据库查询操作是因为该两种缓存方式缓存的数据为空,这个时候就需要将数据库查询到的数据缓存到内存缓存或者文件缓存中。(这里就是文件缓存数据生成的位置,而数据缓存生成的数据则存放与./data/cache/目录下)

(3)、缓存数据生成:(文件缓存数据根据数据是否需要缓存,且支持文件缓存,会在数据获取的过程中存储于缓存文件中,缓存文件中如果存在数据的话,则不会从数据库中获取数据,具体详情如“缓存数据获取”中有所描述)

数据库缓存生成:

方式一:

根目录/source/function/function_core.php,直接数据库缓存数据的存储:

function savecache($cachename, $data) {
    C::t('common_syscache')->insert($cachename, $data);
}
 
function save_syscache($cachename, $data) {
    savecache($cachename, $data);
}

很简单,把需要缓存的数据直接存储于需要的位置中(内存、文件、数据库),如下insert操作:

public function insert($cachename, $data) {
 
    parent::insert(array(
      'cname' => $cachename,
      'ctype' => is_array($data) ? 1 : 0,
      'dateline' => TIMESTAMP,
      'data' => is_array($data) ? serialize($data) : $data,
    ), false, true);
 
    if($this->_allowmem && memory('get', $cachename) !== false) {
      memory('set', $cachename, $data);
    }
    $this->_isfilecache && @unlink(DISCUZ_ROOT.'./data/cache/cache_'.$cachename.'.php');
  }

存储数据将存放于缓存数据库中,内存中,以及删除原始的缓存文件(目的请参照缓存数据获取时文件缓存的生成过程,当文件缓存数据为空时,会重新生成文件缓存数据)

方式二:

即使用function_cache.php脚本中的updatecache($cachename)函数,该函数的实现机制协同项目目录./source/function/cache/cache_*.php脚本。

而./source/function/cache/cache_*.php脚本最终的依然是调用save_syscache函数,所以,为了方便数据格式化,在需要存储特定数据的时候,可以在./source/function/cache/目录下创建相应的数据生产脚本文件,该脚本会通过原有的逻辑,通过调用updatecache('特定标示符') 函数,然后调用cache_特定标示符.php脚本生成缓存数据,缓存数据的存放根据系统设定好的缓存方式进行存储。

************************************************************

二、 Discuz官方提供“Discuz缓存机制”——原文: http://bbs.zb7.com/discuz/dx25/plug/construct/construct_cache.htm

Discuz! X2.5 的 config_global.php 中有这样一行代码

$_config [ 'cache' ][ 'type' ] =  'sql' ;

这就是 Discuz! 内置的缓存方式,如果填写 'sql' 则为使用数据库缓存,填写 'file' 则为使用文件缓存。

内存级缓存

缓存层的引入是为了解决MYSQL自身对高并发处理的性能瓶颈,目前产品缓存层采用主流的Key-Value对形式,内存级的缓存产品很多,支持的内存优化接口有 Memcache、eAccelerator、Alternative PHP Cache(APC)、Xcache、Redis 五种,优化系统将会依据当前服务器环境依次选用接口,单服务器环境中推荐使用APC,多服务器环境中推荐使用Redis或Memcache。

数据层是以表为单位的类文件,所有表类都继承discuz_table基类,基类实现缓存操作的相关函数;理论上所有的数据表均可以缓存,目前产品在六个压力大的数据表内置开启了缓存机制:用户相关表、回帖、主题、主题和专辑关系、淘贴专辑、用户关注关系。

  • 用户相关表

缓存表:'common_member', 'count', 'status','profile', 'field_home', 'field_forum'。

UID为缓存KEY。表数据更新时缓存数据会同步更新。

  • 回帖

以TID为单位,缓存第一页的post数据。表数据更新时缓存数据会同步更新。

  • 主题

TID为缓存KEY。表数据更新时缓存数据会同步更新。

版块列表默认参数第一页时以 forumdisplay_FID 为缓存KEY,缓存时间内数据不更新。

  • 主题和专辑关系

以TID为单位,此TID的专辑ID集合,表数据更新时缓存数据会同步更新。

  • 淘贴专辑

以TID为单位,此TID的专辑集合,缓存时间内数据不更新。

  • 用户关注关系

以UID为单位,此UID关注用户的关系数据,缓存时间内数据不更新。

内存级缓存层实现细节

  • discuz_table基类中缓存机制的实现
/** 
* @var string 缓存主键名前缀,为空时表示此表不支持缓存 
*/ 
protected $_pre_cache_key; 
 
/** 
* @var string 缓存时间,以秒为单位,0表示永久或相关配置文件中的默认值 
*/ 
protected $_cache_ttl;
  • discuz_table基类中缓存机制的方法
//缓存一个变量到缓存中,如果 KEY已经在则会被覆盖为新值
store_cache($id, $data, $cache_ttl = null, $pre_cache_key = null)
//获取指定KEY的缓存数据
fetch_cache($ids, $pre_cache_key = null)
//清除指定KEY的缓存
clear_cache($ids, $pre_cache_key = null)
//更新一个已经存在的KEY,只更新修改的字段
update_cache($id, $data, $cache_ttl = null, $pre_cache_key = null)
//批量更新缓存,只更新已经存在KEY的指定修改的字段
update_batch_cache($ids, $data, $cache_ttl = null, $pre_cache_key = null)
//重置已经存在的KEY的值
reset_cache($ids, $pre_cache_key = null)
//累加缓存数据中某字段的值
increase_cache($ids, $data, $cache_ttl = null, $pre_cache_key = null)

posted on 2018-01-27 21:56  alleyonine  阅读(1481)  评论(0编辑  收藏  举报

导航