为了更好更方便的使用redis,做了一个redis封装的类,
redis key的结构为:global Pre(缺省时,默认使用表前缀) + table name(除前缀的数据表明) + id(数据表主键id).以数据表主键id作为redis的区分,不同的子类(model)定义不同的redis前缀,
取redis时,如果拿不到数据,则会到数据库中查询相应的数据(以传入id为查询依据)
子类实例化时,必须要声明对应的数据表
子类也可以关联其他的子表,比如查商品表,可以通过关联sku表,顺带查询出相应的sku数据
使用本实例,需自行实现通过sql查询功能
1、配置文件
array( 'host' => '127.0.0.1', //redis 主机地址 'port' => '6379', //端口号 6379 'pwd' => '', //密码 // ), );
2、redis基类
get 必须传第二个参数 $this->_table = array( 'mainTable' => array( 'table' => 'abroad_goods', 'fields' => '*', 'name' => 'ag_id' ), 'subTable' => array( //keyName 为子表数据在返回值中的key名称,必须 array( 'table' => 'abroad_order', 'fields' => '*', 'keyName' => 'x1', 'name' => 'ao_ag_id', 'order' => 'ao_id DESC', 'where' => array( array('name' => 'ao_goods_name', 'oper' => 'like', 'value' => ''%说法%''), array('name' => 'ao_status', 'oper' => '=', 'value' => '1'), //只查有用的数据 缓存到内存中 ) ), array( 'table' => 'abroad_order', 'fields' => '*', 'keyName' => 'x2', 'name' => 'ao_ag_id', 'order' => 'ao_id DESC', 'where' => array( array('name' => 'ao_goods_name', 'oper' => 'not like', 'value' => ''%说法%''), ) ), ) );*/ class App_Model_Redis_RedisAbroadBaseStorage { private $redis = null; //redis实例 protected $_redis_pre = ''; //当前模型redis数据前缀 默认为 _table_pre protected $_table_pre = ''; //默认拿config/mysql.php 中的tablepre protected $_table; /** * App_Model_Redis_RedisAbroadBaseStorage constructor. * 使用到了系统DB类的两个方法( fetch_first 和 fetch_all),如果要移植到其他项目,请实现此类,或作相应改动 */ public function __construct() { $redis_config = plum_parse_config('redis_config', 'redis'); $this->setTablePre(); $this->redis = new Redis(); if ($this->redis->connect($redis_config['host'], $redis_config['port']) == false) { die($this->redis->getLastError()); } //如果有密码,则调用auth if ($redis_config['pwd'] !== '') { if ($this->redis->auth($redis_config['pwd']) == false) { die($this->redis->getLastError()); } } } /** * @param $tableArr 设置模型数据表信息 */ public function setTableData($tableArr) { $this->_table = $tableArr; } /** * 获取模型数据表信息 */ public function getTableData() { return $this->_table; } /** * 设置表前缀,如果子类有传入,则使用传入值, * 否则使用系统配置 config/mysql.php */ private function setTablePre() { $query = plum_parse_request_uri(); $prefix = array_shift($query); if ($prefix == 'manageadmin') { $db_config = plum_parse_config('manageadmin', 'mysql'); } else { $db_config = plum_parse_config('default', 'mysql'); } $this->_table_pre = $this->_table_pre ? $this->_table_pre : $db_config['tablepre']; } /** * @return string redis key前缀 */ public function getPre() { return $this->_redis_pre ? $this->_redis_pre : $this->_table_pre; } /** * 重新设置前缀 * @param $pre string 前缀 */ public function setPre($pre) { $this->_redis_pre = $pre; } /** * redis完整key为: redis前缀 + 主表名 + id * @param $id * @return string 组装redis key */ public function getKey($id) { $table_name = isset($this->_table['mainTable']['table']) ? $this->_table['mainTable']['table'] : 'no_table'; return $this->getPre() . $table_name . '_' . $id; } /** * 获取redis的值 * 先查redis 如果没有 去查mysql 如果还没有 则返回null * @param $id int id * @param $hasDataBase bool 默认没有查到数据时,去数据库拿数据 * @return string 有数据返回序列化后的数据,没数据返回空字符串 */ public function get($id, $hasDataBase = true) { $key = $this->getKey($id); if ($this->redis->exists($key)) { $res = $this->redis->get($key); } elseif ($hasDataBase && isset($this->_table['mainTable']['table'])) { //没有的话,去数据库查询 $res = $this->queryData($id); $this->set($id, $res); $res = $this->redis->get($key); } else { return ''; } return unserialize($res); } /** * 如果主表数据为空,则不再查询子表,但相反,子表可以为空 * @param $id int 表id * @return mixed array 查询得到的数据 */ private function queryData($id) { $data = DB::fetch_first($this->formatSql($this->_table['mainTable'], $id)); if (empty($data)) { return array(); } // $data = $this->object2array($data); if (!empty($this->_table['subTable'])) { foreach ($this->_table['subTable'] as $info) { $data[$info['keyName']] = DB::fetch_all($this->formatSql($info, $id)); } } return $data; } /** * @param $tableInfo array 表信息 * @param $id int 主表主键id * @return string 返回组织后的sql */ private function formatSql($tableInfo, $id) { $fields = $tableInfo['fields'] ? $tableInfo['fields'] : '*'; $sql = "SELECT " . $fields . " FROM " . $this->_table_pre . $tableInfo['table'] . ' '; //" WHERE " . $tableInfo['name'] . " = '" . $id . "' "; $sql .= $this->formatWhere($tableInfo, $id); $sql .= $this->formatOrder($tableInfo); return $sql; } /** * 拼接sql * @param $tableInfo * @param $id * @return mixed */ private function formatWhere($tableInfo, $id) { $whereSql = ''; if (isset($tableInfo['where'])) { $whereSql = ' WHERE 1=1 '; if (!empty($tableInfo['where'])) { foreach ($tableInfo['where'] as $v) { $value = isset($v['value']) ? $v['value'] : $id; $oper = isset($v['oper']) ? $v['oper'] : '='; switch ($oper) { case 'in': case 'not in': $whereSql .= " AND " . $v["name"] . " " . $oper . " (" . trim($value, ",") . ") "; break; default: $whereSql .= " AND " . $v["name"] . " " . $oper . " " . trim($value, ",") . " "; break; } } } } return $whereSql; } /** * @param $tableInfo 数据表 * @return string 返回值 */ private function formatOrder($tableInfo) { $orderSql = ''; if (isset($tableInfo['order'])) { $orderSql = ' order by ' . $tableInfo['order'] . ' '; } return $orderSql; } /** * @param $id * @param $value * @param int $expire 过期时间 单位:秒 。默认(-1)为不限 * @param bool $isNewExpire 如果是有过期时间的,是否重新生成,即从更新那一刻起,重新倒计时 * @return true */ public function set($id, $value, $expire = -1, $isNewExpire = true) { $key = $this->getKey($id); $value = $value ? serialize($value) : ''; //序列化数据 空字符不序列化 if ($expire > -1) { if ($isNewExpire) { $this->redis->set($key, $value); $this->expire($id, $expire); } else { $leftTime = $this->redis->ttl($key); $this->redis->set($key, $value); $this->expire($id, $leftTime); } } else { $this->redis->set($key, $value); } return true; } /** * @param $id * @return bool|true */ public function update($id) { $res = $this->queryData($id); return $this->set($id, $res); } /** * @param $idArr array 要查询的数据的key数组 * @return array 返回查询到的缓存数据,与传入参数数据结构一致 */ public function mget($idArr) { $return = array(); foreach ($idArr as $k => $v) { $return[$k] = $this->get($v); } return $return; } /** * 批量存储 * @param $arr array 要设置的数组,数组的key作为redis的key,value作为redis的value * @return bool 有数据的话返回数组 */ public function mset($arr) { $msetArr = array(); if (!empty($arr)) { foreach ($arr as $k => $v) { $key = $this->getKey($k); $msetArr[] = array($key => $v); } return $this->redis->mset($msetArr); } return false; } /** * @param $id int 数据id * @param $second int 过期时间,单位:秒 * @return true|false */ public function expire($id, $second) { $key = $this->getKey($id); return $this->redis->expire($key, $second); } /** * @param $ids array 要删除的数据的id,单个可以为字符串,多个需要是id 数组 * @return int 删除的数量 */ public function del($ids) { if (is_array($ids)) { $keyArr = array(); foreach ($ids as $val) { $keyArr[] = $this->getKey($val); } return $this->redis->del($keyArr); } else { $key = $this->getKey($ids); return $this->redis->del($key); } } /** * 默认不对外开发,如果要使用,请在子类重载 * @return bool 删除所有 为了数据安全 此接口不对外开放 */ protected function flashAll() { return $this->redis->flushAll(); } /** * 根据正则表达式 获取当前系统的key * @param $pattern string 正则表达式 * @return array keys(*):当前系统所有的key , */ public function keys($pattern) { return $this->redis->keys($pattern); } /** * 对应 * @param $id int剩余时间 time to left * @return int 单位:秒, */ public function ttl($id) { $key = $this->getKey($id); return $this->redis->ttl($key); } /** * 当前key是否存在 * @param $id * @return bool */ public function exists($id) { $key = $this->getKey($id); return $this->redis->exists($key); } }
3、子类
_table_pre = 'haitao_'; //默认拿config/mysql.php 中的tablepre $this->_redis_pre = 'haitao_for_test_'; //当前模型redis数据前缀 默认为 _table_pre //数据集合,必须保持此数据结构 /*$this->_table = array( 'mainTable' => array( 'table' => 'abroad_goods', 'fields' => '*', 'name' => 'ag_id' ), 'subTable' => array( //keyName 为子表数据在返回值中的key名称,必须 array( 'table' => 'abroad_order', 'fields' => '*', 'keyName' => 'x1', 'name' => 'ao_ag_id', 'order' => 'ao_id DESC', 'where' => array( array('name' => 'ao_goods_name', 'oper' => 'like', 'value' => ''%说法%''), array('name' => 'ao_status', 'oper' => '=', 'value' => '1'), //只查有用的数据 缓存到内存中 ) ), array( 'table' => 'abroad_order', 'fields' => '*', 'keyName' => 'x2', 'name' => 'ao_ag_id', 'order' => 'ao_id DESC', 'where' => array( array('name' => 'ao_goods_name', 'oper' => 'not like', 'value' => ''%说法%''), ) ), ) );*/ parent::__construct(); } public function flashAll() { return parent::flashAll(); // TODO: Change the autogenerated stub } }
4、调用
flashAll(); // $model->set(8, '----------1111---- '); for ($i = 1; $i < 10000; $i++) $model->set('access_token' . $i, mt_rand(10000, 5555555)); print_r($model->get('access_token1')); } public function setAction() { $model = new App_Model_Redis_RedisAbroadBannerStorage(); $m = $model->get('access_token', false); print_r($m); print_r($model->keys('*')); } public function getAction() { $model = new App_Model_Redis_RedisAbroadBannerStorage(); $model->flashAll(); $data = $model->getTableData(); $data['subTable'][0]['keyName'] = 'xxxx2'; //oper 缺省,则使用 =; 如果缺省value,则使用$id 作为条件; // print_r($data); $model->setTableData($data); $data = $model->getTableData(); // print_r($data); print_r($m = $model->get(8)); } }