Redis实战运用之LIST


声明:本文转载自https://my.oschina.net/u/3736695/blog/1581898,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

今天下午发了一篇Redis实战运用之HASH居然被被官方推荐,有了100多个阅读,居然还有4个收藏,意料之外更是给了我一个小惊喜,特别是哪4个收藏的,特别感谢。

今天开始之后争取以后每天分享一篇,虽然我也是个菜鸟,写出来东西可能也有各种问题,望各位批评指正。但至少还是能给一些人一点帮助,也是极好的。 

好了废话基本说完了,下面具体说果图网http://www.guotuw.com)功能点的具体实现:                  redis安装配置,自己去谷歌。开发语言:PHP  框架:YII2 运行环境:LNMP 。

2、redis数据结构:LIST的运用

简约图解一把:

话说我的果图网里面有大量的美女写真图片,大概150G的图片。假设我现在来抓取我自己这个网站的图片我就会用到LIST。PHP有点基础的人就能知道file_get_contents,fgets,fsockopen,curl_init等相关方法就可以简单的获取到目标网页的源码,然后通过正则、XPATH、phpQuery等等手段匹配到网页中的图片地址,然后呢,放到数据库? 这当然可以,但这不是重点。

此时,我甚至可以把所有图片地址放入一个LIST(分段同时放入多个LIST,也可以算是解决阻塞的一个方法),然后多线程不断的从LIST的一端取出(LPOP,RPOP这两个方法都可以)单个图片链接抓取,抓取失败的图片地址,放到LIST的另一端,程序一直跑就行了,直到全部图片下载完成。

其实吧,以上方法就实现了一个‘队列’,看到这里聪明的你就可以举一反三了。例如抢购、处理订单、消息排行等需要“排队处理“的一些数据就可以用LIST来实现了。

更实在一点的去看我新闻列表其实就是一个链表实现的。完整代码如下:

     /*      * 新闻列表页面      */     public function actionIndex()     {         $page = (int)Yii::$app->request->get('page') ?: 1;         //列表新闻         $result = News::pageNews($page, 10,['id','image','title','content','desc','create_time','view']);          $params['news'] = $result['data'];               $params['pageString'] = $this->createPageString($pagination);//分页不是重点 ^_^          return $this->render('index', $params);     }        //分页获取新闻数据同时缓存数据,这个方法写道新闻的MODEL里面。     public function pageNews($currentPage = 1, $fields = [],$pageSize = 10){         $redisKey = 'newsList';          $exists = Yii::$app->get('redis')->exists($redisKey);         if(intval($exists) == 0){             //我的数据不多一波查询没毛病             $newsIds = News::find()                 ->select('id')                 ->where(['status' => NEWS::STATUS_ACTIVVE])                 ->orderBy(['id' => SORT_DESC])                 ->asArray()                 ->all();             if(!empty($newsIds)){                 foreach($newsIds as $newsId){                     Yii::$app->get('redis')->lPush($redisKey,$newsId['id']);                 }                 //缓存个12个小时,过期时间或者不过期按照自己的需求来设定即可,redis也可以指定过期时间点 expireAt                 Yii::$app->get('redis')->expire($redisKey,12 * 60 * 60);             }         }         //RedisService::pageData()和News::formatNews()下面有具体方法体         $pageNewsData = RedisService::pageData($redisKey,$currentPage,$pageSize);         !empty($pageNewsData['data']) && $pageNewsData['data'] = News::formatNews($pageNewsData['data'],$fields);            return $pageNewsData;     }       /**       * @desc 分页获取LIST、ZSET数据,可做为公共方法       * 上面代码中RedisService::pageData的方法体,这里运用了List的常用方LRANGE,LLEN。      * @param string $redisKey      * @param int $currentPage      * @param int $perPage      * @param string $type      * @param string $sort      * @return mixed      */     public static function pageData($redisKey = '',$currentPage = 1, $perPage = 10,$type = 'list' ,$sort = 'desc'){         $pagination['currentPage'] = $currentPage;         $pagination['perPage'] = $perPage;         $pagination['totalPage'] = $pagination['totalCount'] = 0;          $rs['data'] = [];         $redis = Yii::$app->redis;         if(strtolower($type) == 'list'){             $pagination['totalCount'] = (int)$redis->Llen($redisKey);             $pagination['totalPage'] = ceil($pagination['totalCount'] / $perPage);              $start = ($currentPage - 1) * $perPage;             $end = $currentPage * $perPage - 1;             $rs['data'] = $redis->LRange($redisKey,$start,$end);         }else if(strtolower($type) == 'zset'){ //SORT SET下一篇讲解             $pagination['totalCount'] = $redis->ZCard($redisKey);             $pagination['totalPage'] = ceil($pagination['totalCount'] / $perPage);              $start = ($currentPage - 1) * $perPage;             $end = $currentPage * $perPage - 1;             if(strtolower($sort) == 'desc'){                 $rs['data'] = $redis->ZRevrange($redisKey,$start,$end);             }else{                 $rs['data'] = $redis->zRange($redisKey, $start, $end);             }         }         $rs['pagination'] = $pagination;          return $rs;     }      /**      * @desc 批量格式化新闻数据      * @param array $newIDs      * @param array $fields      * @return array      */     public static function formatNews($newIDs = [],$fields = []){         $rs = [];          if($newIDs){             empty($fields) && $fields = static::$fields;             foreach ($newIDs as $id){                 $formatNewInfo = static::getNewsInfoById($id,$fields);                 if($formatNewInfo) $rs[] = $formatNewInfo;             }         }          return $rs;     }         //根据ID获取新闻详情,结合上一篇讲解的HASH使用     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);                 $newInfo = ArrayHelper::filter($_newInfo->attributes,$fields);             }         }          $newInfo && $newInfo = static::formatNewInfo($newInfo);//格式化新闻数据         return $newInfo;     }

以上代码有点长,不过还是可以看一起。当然这只是最常规基础的用法,先基础,后面自己可以逐步深入。

即使如此,配合之前的Redis实战运用之HASH其实已经基本上实现果图网新闻模块的全部功能啦,新闻列表新闻详情详情的请求数据基本上都只是从缓存中读取了,缓存也会自动更新了。

    /*      * 新闻详情页面 详情分页问题      */     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);     }

至于LIST操作的其他命令,这些操作都常用而且又都非常简单。具体如下:

序号 命令及描述
1 BLPOP key1 [key2 ] timeout 
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
2 BRPOP key1 [key2 ] timeout 
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
3 BRPOPLPUSH source destination timeout 
从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
4 LINDEX key index 
通过索引获取列表中的元素
5 LINSERT key BEFORE|AFTER pivot value 
在列表的元素前或者后插入元素
6 LLEN key 
获取列表长度
7 LPOP key 
移出并获取列表的第一个元素
8 LPUSH key value1 [value2] 
将一个或多个值插入到列表头部
9 LPUSHX key value 
将一个值插入到已存在的列表头部
10 LRANGE key start stop 
获取列表指定范围内的元素
11 LREM key count value 
移除列表元素
12 LSET key index value 
通过索引设置列表元素的值
13 LTRIM key start stop 
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
14 RPOP key 
移除并获取列表最后一个元素
15 RPOPLPUSH source destination 
移除列表的最后一个元素,并将该元素添加到另一个列表并返回
16 RPUSH key value1 [value2] 
在列表中添加一个或多个值
17 RPUSHX key value 
为已存在的列表添加值

基本上看方法名字就能知道其含义,有空自己去测试下,很快就能熟练运用了。

篇幅又有点长了,里面涉及到了一个SORT SET。下一篇讲解SORT SET。

注:上面提到采集150G图片的采集,其实单机也就跑了2天而已, 并且保存对应关系,看写真列表这个页面就很明显了,如果有感兴趣的我就把全部代码都贴上来。^_^

本文发表于2017年11月30日 12:33
(c)注:本文转载自https://my.oschina.net/u/3736695/blog/1581898,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2414 讨论 1 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1