Redis实战运用之LIST

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

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

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

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

简约图解一把:

005009_wJb6_3736695.png

话说我的果图网里面有大量的美女写真图片,大概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,$pageSize = 10, $fields = []){
        $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操作的其他命令,这些操作都常用而且又都非常简单。具体如下:

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

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

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

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

转载于:https://my.oschina.net/u/3736695/blog/1581898

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值