+ -
当前位置:首页 → 问答吧 → 基于memcache接口的统一存储工具类设计

基于memcache接口的统一存储工具类设计

时间:2009-08-06

来源:互联网

本帖最后由 evangui 于 2009-8-6 10:12 编辑

文章来源:http://www.guigui8.com/index.php/archives/448.html

上个星期做在线用户的统计,最初是想用memcached进行在线用户数据的存储的,后来由于种种原因,还是改用了mysql处理。
刚开始处理的时候,临时想做一个统一接口的key-value数据的存储方式。然后我模拟memcached接口,写了个简单的k-v数据存储的工具类。
临时想到,暂且记下。

1. 接口类



/**
* 存储工具抽象接口类
*
*/
abstract class StoreTool
{
abstract public function set($key, $val);
abstract public function get($key);
abstract public function replace($key, $val);
abstract public function delete($key);
abstract public function flush();
abstract public function increment($key, $val=1);
abstract public function add($key, $val);
}



2. memcached和mysql处理 (mysql用的heap engine)

[php]

/**
* mysql heap engin 存储工具类
*
*/
class DbStoreTool extends StoreTool
{
static private $_instance;
private $_dbh;
private $_tablename = 'mmouc_memory_kv';

static public function getInstance($config) {
  if (self::$_instance == null) {
   self::$_instance = new self($config);
  }
  return self::$_instance;
}

private function __construct($config) {
  $conn = $config['persistency'] ? mysql_pconnect($config['host'].':'.$config['port'], $config['user'], $config['password'])
     : mysql_connect($config['host'].':'.$config['port'], $config['user'], $config['password']);

  if($conn) {
   if($config['charset']) mysql_query("SET NAMES '" . $config['charset'] . "'", $conn);

   if(!empty($config['database'])) {
    $dbselect = mysql_select_db($config['database'], $conn);
    if(!$dbselect) {
     mysql_close($conn);
     $conn = $dbselect;
    }
   }

   $this->_dbh = $conn;
   $this->_tablename = $config['tablename'];
  }
}

/**
  * Store data at the server
  * stores an item var with key on the memcached server
  *
  * @param unknown_type $key
  * @param unknown_type $val
  * @return unknown
  */
public function set($key, $val) {
  $res = $this->update($key, $val);
  if (0 === $res) {
   return $this->add($key, $val);
  }
  return true;
}

/**
  *  Replace value of the existing item
  *  should be used to replace value of existing item with key.
  *  In case if item with such key doesn't exists, This function returns FALSE
  *
  * @param unknown_type $key
  * @param unknown_type $val
  * @return unknown
  */
public function replace($key, $val) {
  $res = $this->update($key, $val);
  if (0 === $res) {
   return false;
  }
  return true;
}

public function get($key) {
  if (is_array($key)) {
   $in_keys = "'" . implode("','", $key) . "'";
   $sql = "
    SELECT `k`, `v` FROM `" . $this->_tablename . "`
    WHERE `k` IN ({$in_keys})
   ";
   $res = mysql_query($sql, $this->_dbh);

   if (empty($res)) {
    return MMO_STORE_OP_ERROR;
   }

   $_arr_res = array();
   while ($row = mysql_fetch_assoc($res)) {
    $row['v'] = unserialize($row['v']);
    $_arr_res[$row['k']] = $row;     
   }

   $out = array();
   foreach ($key as $_k) {
    $out[] = $_arr_res[$_k]['v'];
   }
   return $out;

  } else if (is_string($key)) {

   $sql = "
    SELECT `v` FROM `" . $this->_tablename . "`
    WHERE `k`='{$key}'
   ";
   $res = mysql_query($sql, $this->_dbh);

   if (empty($res)) {
    return -1;
   }
   $row = mysql_fetch_assoc($res);
   if (empty($row)) {
    return MMO_STORE_ITEM_NOT_EXIST;
   }
   return unserialize($row['v']);
  } else {

   return false;
  }

}

public function delete($key) {
  $sql = "
   DELETE FROM `" . $this->_tablename . "`
   WHERE `k`='$key'   
   ";
  $res = mysql_query($sql, $this->_dbh);
  if (!$res) {
   return MMO_STORE_OP_ERROR;
  }
  return mysql_affected_rows($this->_dbh);
}

public function flush() {
  $sql = " TRUNCATE TABLE `" . $this->_tablename . "` ";
  $res = mysql_query($sql, $this->_dbh);
  return $res ? true : false;
}

/**
  *
  * TODO:
  * 修改这里的并发访问问题
  *
  * @param unknown_type $key
  * @param unknown_type $val
  * @return unknown
  */
public function increment($key, $val=1) {
  $_db_val = $this->get($key);
  if (MMO_STORE_ITEM_NOT_EXIST == $_db_val) {
   //不存在
   return false;   
  }

  $val = intval($_db_val) + intval($val);
  $this->update($key, $val);
  return $val;
}

/**
  * Add an item to the server
  *
  * stores variable var with key only if such key doesn't exist at the server yet
  *
  * @param unknown_type $key
  * @param unknown_type $val
  * @return unknown
  */
public function add($key, $val) {
  if (!$this->_isExist($key)) {
   $val = serialize($val);
   $time = time();
   $sql = "
    INSERT INTO `" . $this->_tablename . "`
    SET `k`='{$key}',
        `v`='{$val}',
        `t`='{$time}'
   ";
   $res = mysql_query($sql, $this->_dbh);
   return $res ? true : MMO_STORE_OP_ERROR;

  } else {
   return false;
  }
}


private function _isExist($key, $val='') {
  $sql = "
   SELECT COUNT(`k`) as 'num'
   FROM `" . $this->_tablename . "`
   WHERE `k`='{$key}'
  ";
  !empty($val) && $sql .= ", `v`='" . serialize($val) . "'";
  $res = mysql_query($sql, $this->_dbh);
  if (empty($res)) {
   return -1;
  }
  $row = mysql_fetch_assoc($res);
  return $row['num'] ? true : false;
}

private function update($key, $val) {
  $val = serialize($val);
  $time = time();
  $sql = "
   UPDATE `" . $this->_tablename . "`
   SET `v`='{$val}',
       `t`='{$time}'
   WHERE `k`='$key'   
   ";
  $res = mysql_query($sql, $this->_dbh);
  if (!$res) {
   return MMO_STORE_OP_ERROR;
  }
  return mysql_affected_rows($this->_dbh);
}
}

class FileStoreTool
{

}

class MemcacheStoreTool extends StoreTool
{
static private $_instance;
private $_memcacheHandler;

static public function getInstance($config) {
  if (self::$_instance == null) {
   self::$_instance = new self($config);
  }
  return self::$_instance;
}

private function __construct($config) {
  $this->_memServers = $config;
  $this->_initMemcacheObj();
}

public function set($key, $val) {
  return $this->_memcacheHandler->set($key, $val);
}
public function get($key) {
  return $this->_memcacheHandler->get($key);
}

public function replace($key, $val) {
  return $this->_memcacheHandler->replace($key, $val);
}

public function delete($key) {
  return $this->_memcacheHandler->delete($key);
}
public function flush() {
  return $this->_memcacheHandler->flush();
}
public function increment($key, $val=1) {
  return $this->_memcacheHandler->increment($key, $val);
}

public function add($key, $val) {
  return $this->_memcacheHandler->add($key, $val);
}

/**
    * 检查保存Session数据的路径是否存在
    *
    * @return bool 成功返回true
    */
   private function _initMemcacheObj(){
       if (!class_exists('Memcache') || !function_exists('memcache_connect')){
           die('Failed: Memcache extension not install, please from http://pecl.php.net download and install');
       }        
       if ($this->_memcacheHandler && is_object($this->_memcacheHandler)){
           return true;
       }
       $this->_memcacheHandler = new Memcache;
       if (!empty($this->_memServers)) {
          foreach ($this->_memServers as $_host => $_port) {
            $this->_memcacheHandler->addServer($_host, $_port);
        }
       }

       return true;
   }
}

[/php]

3. mysql和file相关

mysql 记录方式,对应表 建表语句如下:
CREATE TABLE IF NOT EXISTS `mmouc_memory_kv` (
`k` varchar(40) NOT NULL COMMENT '键名',
`v` varchar(2048) NOT NULL COMMENT '键值的serialize值',
`t` int(10) NOT NULL DEFAULT '0' COMMENT '更新时间',
PRIMARY KEY (`k`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8 COMMENT='代替memcache数据记录表';
对于file,暂未处理,以后补上。


4. todo:

a. 对于memcached数据的遍历方式策略,需要做一个完备而统一接口的策略算法处理类,可以在多种分级存储模式,和存储策略间任意切换;
b. file存储工具类补上

代码下载: storetoolclass.zip (2.85 KB)
下载次数: 6
2009-8-6 10:07

作者: evangui   发布时间: 2009-08-06

佩服

作者: houhuiyang   发布时间: 2009-08-06