Chinaunix首页 | 论坛 | 博客
  • 博客访问: 295524
  • 博文数量: 153
  • 博客积分: 3347
  • 博客等级: 中校
  • 技术积分: 1556
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-30 17:50
文章分类

全部博文(153)

文章存档

2013年(7)

2012年(21)

2011年(46)

2010年(16)

2009年(63)

我的朋友

分类: 系统运维

2011-06-17 17:07:00

/**
 * 在STable类上扩展缓存功能
 * @author ice
 *
 */
class SCachedTable extends STable {

 public function __construct($tableName) {
  parent::__construct($tableName);
 }

 //是否临时禁止缓存
 private $temporaryDisableCache = false;

 /**
  * 临时禁止缓存,只禁止一次
  * @return SCachedTable
  */
 public function disCache() {
  $this->temporaryDisableCache = true;
  return $this;
 }

 /**
  * 检查是否允许使用Memcache
  *
  * @return unknown
  */
 private function _enable() {
  //如果已经临时禁止了缓存
  if($this->temporaryDisableCache){
   //下次不再临时禁止
   $this->temporaryDisableCache = false;
   return false;
  }

  //检查缓存是否开启
  return SMemcache::enable();
 }

 /**
  * 具体执行数据库查询,如果缓存里没有,则查询并保存到缓存中
  *
  * @param string $sql
  * @return 查询结果
  */
 protected function _query($sql) {
  //如果缓存未开启,则直接查询数据库
  if(!$this->_enable()){
   return parent::_query($sql);
  }

  //从缓存中取
  $data = SMemcache::get($sql);

  //如果缓存未中
  if(!$data){
   //从数据库查询
   $data = parent::_query($sql);

   //保存到缓存中
   SMemcache::set($this->tableName,$sql,$data);
  }

  return $data;
 }

 /**
  * 具体执行数据库语句,先清除本表的缓存,再执行
  *
  * @param string $sql
  * @return boolean 执行是否成功
  */
 protected function _execute($sql) {
  //检查配置开关
  if($this->_enable()){
   SMemcache::clear($this->tableName);
  }
  return parent::_execute($sql);
 }

 /**
  * 获取查询语句中的涉及表名
  *
  * @param string $sql
  * @return array 表名列表
  */
 private function _getNameFromQuery($sql) {
  $matches = null;

  //获取命令字
  if(!preg_match('/^\s*(\w+)\b/i',$sql,$matches)){
   throw new Exception("SQL query statement invalid:" . $sql);
  }

  //查询语句必须是Select开头,否则不承认
  if(strtolower($matches[0]) != 'select'){
   throw new Exception("Can't use '" . $matches[0] . "' in query statement.");
  }

  //匹配 表名部分 ,包括 From之后和Join之后
  if(!preg_match_all('/from\s+([\w|`]+)(\s*,\s*([\w|`]+))*|join\s+([\w|`]+)/i',$sql,$matches)){
   return array();
  }

  //收集匹配的表名
  $names = array();
  $range = array(
   1,3,4
  );
  foreach($range as $i){
   foreach($matches[$i] as $name){
    if($name){
     array_push($names,trim($name,'`'));
    }
   }
  }
  return $names;
 }

 /**
  * 获取执行语句中的涉及表名
  *
  * @param string $sql SQL语句
  * @return array 表名列表
  */
 private function _getNameFromExecute($sql) {
  $matches = null;

  //获取命令字
  if(!preg_match('/^(\w+)\b/i',$sql,$matches)){
   throw new Exception("SQL query statement invalid:" . $sql);
  }

  //只支持Insert,Update,Delete,Replace语句,其它不支持
  $command = strtolower($matches[0]);
  if(!in_array($command,array(
   'insert','update','delete','replace'
  ))){
   throw new Exception("Don't support '" . $matches[0] . "' in execute statement.");
  }

  //匹配表名部分,包括 insert [into] $tbl , replace [into] $tbl, update $tbl, delete from $tbl
  if(!preg_match_all('/insert\s+(into\s+)?([\w|`]+)|replace\s+(into\s+)?([\w|`]+)|delete\s+from\s+([\w|`]+)|update\s+([\w|`]+)/i',$sql,$matches)){
   return array();
  }

  //收集匹配的表名
  $names = array();
  $range = array(
   2,4,5,6
  );
  foreach($range as $i){
   foreach($matches[$i] as $name){
    if($name){
     array_push($names,trim($name,'`'));
    }
   }
  }
  return $names;
 }

 /**
  * 重载query方法,允许指定是否使用缓存,默认是使用缓存,
  *
  * @param string $sql SQL语句
  * @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此查询与这些表相关.
  * @return mixed 查询数据
  */
 public function query($sql) {
  //检查配置开关
  if(!$this->_enable()){
   return parent::_query($sql);
  }

  //重构表名列表
  $tables = $this->_getNameFromQuery($sql);
  
  if(!SConfig::system('multi_table') and count($tables)>1){
   return $this->_err(__METHOD__,__LINE__,'disable multi table query:'.$sql);
  }

  //如果无法解析表名,不缓存,直接返回查询结果
  if(!$tables or !count($tables)){
   return parent::query($sql);
  }

  //从缓存中取
  $data = SMemcache::get($sql);
  if($data){
   return $data;
  }

  //从数据库中取
  $data = parent::query($sql);

  //设置到缓存中,并与每一个表关联
  foreach($tables as $name){
   SMemcache::set($name,$sql,$data);
  }

  return $data;
 }

 /**
  * 重载execute方法,允许指定是否使用缓存,默认是使用缓存
  *
  * @param string $sql 要执行的SQL语句
  * @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此执行语句与这些表相关
  * @return mixed 执行结果
  */
 public function execute($sql) {
  //检查配置开关
  if(!$this->_enable()){
   return parent::_execute($sql);
  }

  //重构相关表名
  $tables = $this->_getNameFromExecute($sql);
  
  if(!SConfig::system('multi_table') and count($tables)>1){
   return $this->_err(__METHOD__,__LINE__,'disable multi table execute:'.$sql);
  }

  //如果可以解析表名 清除相关缓存
  if($tables and count($tables)){
   //清除相关表中的所有缓存.因为可能影响到
   foreach($tables as $name){
    SMemcache::clear($name);
   }
  }

  return parent::execute($sql);
 }
}

 
 
/**
 * Memcache缓存类
 * 增强域功能
 */
final class SMemcache {
 //具体缓存服务器的句柄
 private static $handles=array();
 //缓存配置
 private static $config;
 //获取缓存配置,由于配置类使用缓存功能,所以缓存类不可以使用配置类
 private static function config() {
  if(!self::$config){
   self::$config = require (DIR_CONFIG . 'memcache.config.php');
  }
  return self::$config;
 }
 /**
  * 判断是否允许缓存
  *
  * @return bool
  */
 public static function enable() {
  $config = self::config();
  return $config['enable'];
 }
 /**
  * 建立与Memcache的长连接
  *
  * @return 连接句柄
  */
 private static function connect($n) {
  if(!isset(self::$handles[$n]) or !self::$handles[$n]){
   self::$handles[$n] = new Memcache();
   $config = self::config();
   if(!self::$handles[$n]->pconnect($config[$n]['host'],$config[$n]['port'])){
    throw new Exception('Memcache connect fail');
   }
  }
  return self::$handles[$n];
 }
 
 /**
  * 对指定的缓存键进行散列,返回服务器编号
  * @param string $key 要缓存的内容的键
  * @return int 服务器编号(1->m)
   */
 private static function hash($key){
  return abs(crc32($key)) % self::$config['servers'] +1;
 }
 /**
  * 根据键获取Memcache的句柄,以便直接使用.
  *
  * @param string $key 缓存内容的键
  * @return Memcache 缓存类实例
  */
 public static function getHandle($key) {
  return self::connect(self::hash($key));
 }
 /**
  * 清除指定表的所有缓存数据
  *
  * @param string $field 域
  */
 public static function clear($field) {
  for($i=1;$i<=self::$config['servers'];$i++){
   $handle = self::connect($i);
 
   $keys = $handle->get('Field_' . $field);
   if($keys){
    foreach($keys as $key){
     $handle->delete($key);
    }
    $handle->delete('Field_' . $field);
   }
  }
 }
 /**
  * 缓存一条数据
  *
  * @param string $table 表名
  * @param string $key  键(实际是查询语句)
  * @param fixed $data 数据
  */
 public static function set($field , $key , $data) {
  $handle = self::connect(self::hash($key));
  //缓存数据
  $key = md5($key);
  $handle->set($key,$data,0,0);
  //取出此表已经缓存的所有键
  $keys = $handle->get('Field_' . $field);
  if(!$keys){
   $keys = array();
  }
  //增加一个键,并保存回去,以便以后统一清除
  $keys[] = $key;
  $handle->set('Field_' . $field,$keys,0,0);
 }
 /**
  * 从缓存中取一条数据
  *
  * @param string $table 表名
  * @param string $key 键名
  * @return fixed 数据
  */
 public static function get($key) {
  $handle = self::connect(self::hash($key));
  return $handle->get(md5($key));
 }
}
阅读(773) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~