Redis实战运用之HASH
去年五月份的时候换了一个工作,到新公司要做一个娱乐新闻方面的项目,项目还挺大,主要涉及到的功能,包括各种数据排名、订阅、关注、点赞、评论、投piao、定向发布消息等。在和公司的另外一个技术讨论怎么做缓存的时候,了解redis这玩意,感觉还不错应用场景也非常适合,虽然我们两都没有用过redis,当即决定使用文件缓存和redis动静结合来做具体的缓存方案。
到现在项目上线一年多了,基于上面的缓存方案,至少现在网站每天稳定100W左右的流量,运行非常稳定。具体项目就不展示出来了。不过基于技术在于折腾的原则,最近用redis新折腾出来一个网站果图网(http://www.guotuw.com),各位可以访问了看下各个页面、各种功能测试等,速度还是过得去的。基本上前台所有的页面和功能基本都穿透不到数据库,全部数据都是实时的。
好了废话基本说完了,下面具体说果图网(http://www.guotuw.com)各个功能点的具体实现:
redis安装配置,自己去谷歌。开发语言:PHP 框架:YII2 运行环境:LNMP 。
1、redis数据结构:HASH的运用
代码先上,用的是https://github.com/yiisoft/yii2-redis这个扩展封装了下面的方法,如果用PHP的reids扩展,各种方法能直接用不用封装。
class RedisService { /*** * @desc 设置redis hash数据 * @param string $redisKey * @param array $table * @param int $expire 过期时间单位秒 * @return bool */ public static function setHash($redisKey = '',$table = [],$expire = 0 ){ if(empty($redisKey) || empty($table)) return false; $params = [$redisKey]; foreach ($table as $key => $value){ if(!empty($value)){ if(is_bool($value)){ $value = (int)$value; } $params[] = $key; $params[] = $value; } } if(count($params) > 1) Yii::$app->redis->executeCommand('HMSET',$params); } /** * @desc 读取redis Hash数据 * @param string $redisKey * @param array $fields * @return array */ public static function getHash($redisKey = '',$fields = []){ $data = $rs = []; if(empty($fields)){ $data = Yii::$app->redis->HGetall(trim($redisKey)); $count = count($data); for($i = 0 ; $i < $count ; $i++){ if($i % 2 != 0){ $rs[$data[$i - 1]] = $data[$i]; } } }else{ $fieldCount = count($fields); for($i = 0 ; $i < $fieldCount ; $i++){ $rs[$fields[$i]] = Yii::$app->redis->HGet($redisKey,$fields[$i]); } } return $rs; } } RedisService::setHash('news:'.1000,['id' => 1000,'view' => 100000,'title' => '果图网上下了','content' =>'这是有很多美女的网站']; RedisService::getHash('news:'.1000,['id','title']);

hash设置后的效果如图 ,范例存储的是具体的新闻数据 ,键:"new:新闻ID",其实哈希就是具体的单条记录,存储过后读取就会非常方便了。只需要ID(怎么来管理ID请往后看LIST相关)就能读取具体的数据了,由于redis是存储在基于内存的缓存,基于新闻这种数据的时效性,设置最多24个小时甚至更短的过期时间就能很快的释放内存,过期的新闻有命中的时候在设置一小段过期时间就可以了,长文本可以用PHP自带的函数压缩一下。
例如:http://www.guotuw.com网站的新闻、写真、明星数据都是放入HASH,访问对应的详情页面的时候根据ID确定HASH键值(news:ID)通过HGETALL或者HMGET命令从内存中读取数据肯定非常迅速啊,然后通过HINCRBY方法添加一个浏览量,是不是就完全避开数据库了。^_^
/* * 新闻详情页面 详情分页问题 */ public function actionInfo() { $id = (int)Yii::$app->request->get('id'); $params['newInfo'] = News::getNewsInfoById($id); if (empty($params['newInfo'])) $this->redirect('site/error'); News::incrementView($id); //添加浏览量 return $this->render('info', $params); } //News 里面的具体方法实现 public static function getNewsInfoById($newId = 0,$fields = []){ $newId = intval($newId); if($newId < 1) return []; empty($fields) && $fields = static::$fields; $newInfo = RedisService::getHash("news:".$newId,$fields); if(empty($newInfo)){ $_newInfo = News::findOne($newId); //数据库读取数据 if($_newInfo){ RedisService::setHash("news:".$newId,$_newInfo->attributes,,24 * 60 * 60);//缓存24小时 $newInfo = ArrayHelper::filter($_newInfo->attributes,$fields); //获取指定字段 } } $newInfo && $newInfo = static::formatNewInfo($newInfo);//格式化新闻数据 return $newInfo; } //新闻添加访问量 public static function incrementView($newId,$increment = 1){ $newId = intval($newId); if($newId > 0 && Yii::$app->get('redis')->exists('news:'.$newId)){ Yii::$app->get('redis')->hIncrBy('news:'.$newId,'view',$increment); } }
至于hash操作的其他命令,如下:
Hash(哈希表) | 以下所谓的域其实相当于字段名名称:例如新闻的 title content view |
HDEL | HDEL key field [field ...] 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 |
HEXISTS | HEXISTS key field 查看哈希表 key 中,给定域 field 是否存在。 |
HGET | HGET key field 返回哈希表 key 中给定域 field 的值。 |
HGETALL | HGETALL key 返回哈希表 key 中,所有的域和值。 |
HINCRBY | HINCRBY key field increment 为哈希表 key 中的域 field 的值加上增量 increment 。增量也可以为负数,相当于对给定域进行减法操作。 例如:增加新闻的浏览量是不是很到位 HINCRBY news:100 view 1 |
HINCRBYFLOAT | HINCRBYFLOAT key field increment 为哈希表 key 中的域 field 加上浮点数增量 increment |
HKEYS | HKEYS key 返回哈希表 key 中的所有域。 |
HLEN | HLEN key 返回哈希表 key 中域的数量。 |
HMGET | HMGET key field [field ...] 返回哈希表 key 中,一个或多个给定域的值。 |
HMSET | HMSET key field value [field value ...] 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
HSET | 设置单个域 |
HSETNX | HSETNX key field value 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。 若域 field 已经存在,该操作无效。 |
HVALS | HVALS key 返回哈希表 key 中所有域的值。 |
基本上看方法名字就能知道其含义,更可况右边还给了具体的说明,有空自己去测试下,很快就能熟练运用了。
篇幅太长不好看的,分开讲先熟悉基本,后面写各种综合使用技巧。下一篇讲解LIST。