zoukankan      html  css  js  c++  java
  • PHP+Redis 实例【一】点赞 + 热度 上篇

    这次的开篇,算是总结下这段时间来的积累吧,废话不多说,直接干!

    前言

    点赞其实是一个很有意思的功能。基本的设计思路有大致两种, 一种自然是用mysql(写了几百行的代码都还没写完,有毒)啦

    数据库直接落地存储, 另外一种就是利用点赞的业务特征来扔到redis(或memcache)中, 然后离线刷回mysql等。

    我这里所讲的功能都是基于我之前的项目去说的,所以有些地方可以不用管的,我主要是记录这个功能的实现思路,当你理解了,基本想用什么鬼语言写都一样的。

    直接写入Mysql

    直接写入Mysql是最简单的做法。

    做三个表即可,

    • comment_info

      记录文章的主要内容,主要有like_count,hate_count,score这三个字段是我们本次功能的主要字段。

    • comment_like

      记录文章被赞的次数,已有多少人赞过这种数据就可以直接从表中查到;

    • user_like_comment

      记录用户赞过了哪些文章, 当打开文章列表时,显示的有没有赞过的数据就在这里面;

    缺点

    • 数据库读写压力大

      热门文章会有很多用户点赞,甚至是短时间内被大量点赞, 直接操作数据库从长久来看不是很理想的做法

    redis存储随后批量刷回数据库

    redis主要的特点就是快, 毕竟主要数据都在内存嘛;

    另外为啥我选择redis而不是memcache的主要原因在于redis支持更多的数据类型, 例如hash, set, zset等。

    下面具体的会用到这几个类型。

    优点

    • 性能高

    • 缓解数据库读写压力

      其实我更多的在于缓解写压力, 真的读压力, 通过mysql主从甚至通过加入redis对热点数据做缓存都可以解决,

      写压力对于前面的方案确实是不大好使。

    缺点

    • 开发复杂

      这个比直接写mysql的方案要复杂很多, 需要考虑的地方也很多;

    • 不能保证数据安全性

      redis挂掉的时候会丢失数据, 同时不及时同步redis中的数据, 可能会在redis内存置换的时候被淘汰掉;

      不过对于我们点赞而已, 稍微丢失一点数据问题不大;

    其实上面第二点缺点是可以避免的,这就涉及到redis 的一些设计模式,不懂没关系,我尽量详细的写,后面我会给出如何解决这个缺点。

    设计功能前知识准备

      1.将要用到的redis数据类型(具体的类型说明,请看底部链接,有详细说明):

    • zset  这个类型主要用来做排序或者数字的增减,这里被用作like 和hate的数字记录,以及热度的记录。
    • set  这个是无序集合,主要用来记录今天需不需要更新,将今天被点赞(包括点讨厌)过的文章id记录下来,方便晚上或者有时间对这部分数据更新。
    • hash  这个是散列,主要用来存储数据以及索引。这里被用来记录用户对哪个文章点了什么,方便下次判断(我看过一些网上的介绍使用set来记录,那个也可以,但是本人觉得这样做更省空间,以及方便管理,再有就是hash的速度快)。
    • list  这个是队列大佬,我们的数据能不能 安全 回到mysql就靠它了。

      2.关于热度如何去判断:

      大家都知道,文章获得点赞数越高,文章的热度就越高,那么怎么判断呢?不就直接记录点赞数就行啦,但是对于最新的文章怎么办?例如有一篇文章一年前发布的,获得50个赞,有篇最新文章获得49个赞,但是按照上面所说的一年前的文章热度还比最新的高,这就不合理了,文章都是时效性,谁都想看最新最热的。

      so!我们要换个方法去处理这个时效性,绝大部分语言都有 时间戳 生成的方法,时间戳随着时间越新,数字越大,直接将时间戳初始化赋值给文章的score,这样最新的文章相比以前的文章就会靠前了。接着是点赞对score的影响,我们假设一天得到20个赞算是一天最热,一天60*60*24=86400秒,然后得到一个赞就是得到86400 / 20 = 4320分。具体数字看自己的业务需求定,我只是举例子而已。点hate当然也会减去相应的数字。

    激动时刻!直接上代码了!里面有详细注释!

      1 <?php
      2 
      3 class Good
      4 {
      5     public $redis = null;
      6 
      7     //60*60*24/20=4320,每个点赞得到的分数,反之即之。
      8     public $score = 4320;
      9 
     10     //点赞增加数,或者点hate增加数
     11     public $num = 1;
     12 
     13     //init redis
     14     public $redis_host = "127.0.0.1";
     15     public $redis_port = "6379";
     16     public $redis_pass = "";
     17 
     18     public function __construct()
     19     {
     20         $this->redis = new Redis();
     21         $this->redis->connect($this->redis_host,$this->redis_port);
     22         $this->redis->auth($this->redis_pass);
     23     }
     24 
     25     /**
     26     * @param int $user_id 用户id
     27     * @param int $type 点击的类型 1.点like,2.点hate
     28     * @param int $comment_id 文章id
     29     * @return string json;
     30     */
     31     public function click($user_id,$type,$comment_id)
     32     {
     33         //判断redis是否已经缓存了该文章数据
     34         //使用:分隔符对redis管理是友好的
     35         //这里使用redis zset-> zscore()方法
     36         if($this->redis->zscore("comment:like",$comment_id))
     37         {
     38             //已经存在
     39             //判断点的是什么
     40             if($type==1)
     41             {
     42                 //判断以前是否点过,点的是什么?
     43                 //redis hash-> hget()
     44                 $rel = $this->redis->hget("comment:record",$user_id.":".$comment_id);
     45                 if(!$rel)
     46                 {
     47                     //什么都没点过
     48                     //点赞加1
     49                     $this->redis->zincrby("comment:like",$this->num,$comment_id);
     50                     //增加分数
     51                     $this->redis->zincrby("comment:score",$this->score,$comment_id);
     52                     //记录上次操作
     53                     $this->redis->hset("comment:record",$user_id.":".$comment_id,$type);
     54 
     55                     $data = array(
     56                         "state" => 1,
     57                         "status" => 200,
     58                         "msg" => "like+1",
     59                     );
     60                 }
     61                 else if($rel==$type)
     62                 {
     63                     //点过赞了
     64                     //点赞减1
     65                     $this->redis->zincrby("comment:like",-($this->num),$comment_id);
     66                     //增加分数
     67                     $this->redis->zincrby("comment:score",-($this->score),$comment_id);
     68                     $data = array(
     69                         "state" => 2,
     70                         "status" => 200,
     71                         "msg" => "like-1",
     72                     );
     73                 }
     74                 else if($rel==2)
     75                 {
     76                     //点过hate
     77                     //hate减1
     78                     $this->redis->zincrby("comment:hate",-($this->num),$comment_id);
     79                     //增加分数
     80                     $this->redis->zincrby("comment:score",$this->score+$this->score,$comment_id);
     81                     //点赞加1
     82                     $this->redis->zincrby("comment:like",$this->num,$comment_id);
     83                     //记录上次操作
     84                     $this->redis->hset("comment:record",$user_id.":".$comment_id,$type);
     85 
     86                     $data = array(
     87                         "state" => 3,
     88                         "status" => 200,
     89                         "msg" => "like+1",
     90                     );
     91                 }
     92             }
     93             else if($type==2)
     94             {
     95                 //点hate和点赞的逻辑是一样的。参看上面的点赞
     96                 $rel = $this->redis->hget("comment:record",$user_id.":".$comment_id);
     97                 if(!$rel)
     98                 {
     99                     //什么都没点过
    100                     //点hate加1
    101                     $this->redis->zincrby("comment:hate",$this->num,$comment_id);
    102                     //减分数
    103                     $this->redis->zincrby("comment:score",-($this->score),$comment_id);
    104                     //记录上次操作
    105                     $this->redis->hset("comment:record",$user_id.":".$comment_id,$type);
    106 
    107                     $data = array(
    108                         "state" => 4,
    109                         "status" => 200,
    110                         "msg" => "hate+1",
    111                     );
    112                 }
    113                 else if($rel==$type)
    114                 {
    115                     //点过hate了
    116                     //点hate减1
    117                     $this->redis->zincrby("comment:hate",-($this->num),$comment_id);
    118                     //增加分数
    119                     $this->redis->zincrby("comment:score",$this->score,$comment_id);
    120 
    121                     $data = array(
    122                         "state" => 5,
    123                         "status" => 200,
    124                         "msg" => "hate-1",
    125                     );
    126                     return $data;
    127                 }
    128                 else if($rel==2)
    129                 {
    130                     //点过like
    131                     //like减1
    132                     $this->redis->zincrby("comment:like",-($this->num),$comment_id);
    133                     //增加分数
    134                     $this->redis->zincrby("comment:score",-($this->score+$this->score),$comment_id);
    135                     //点hate加1
    136                     $this->redis->zincrby("comment:hate",$this->num,$comment_id);
    137 
    138                     $data = array(
    139                         "state" => 6,
    140                         "status" => 200,
    141                         "msg" => "hate+1",
    142                     );
    143                     return $data;
    144                 }
    145             }
    146         }
    147         else
    148         {
    149             //未存在
    150             if($type==1)
    151             {
    152                 //点赞加一
    153                 $this->redis->zincrby("comment:like",$this->num,$comment_id);
    154                 //分数增加
    155                 $this->redis->zincrby("comment:score",$this->score,$comment_id);
    156                 $data = array(
    157                     "state" => 7,
    158                     "status" => 200,
    159                     "msg" => "like+1",
    160                 );
    161             }
    162             else if($type==2)
    163             {
    164                 //点hate加一
    165                 $this->redis->zincrby("comment:hate",$this->num,$comment_id);
    166                 //分数减少
    167                 $this->redis->zincrby("comment:score",-($this->score),$comment_id);
    168 
    169                 $data = array(
    170                     "state" => 8,
    171                     "status" => 200,
    172                     "msg" => "hate+1",
    173                 );
    174             }
    175             //记录
    176             $this->redis->hset("comment:record",$user_id.":".$comment_id,$type);
    177         }
    178 
    179         //判断是否需要更新数据
    180         $this->ifUploadList($comment_id);
    181 
    182         return $data;
    183     }
    184 
    185     public function ifUploadList($comment_id)
    186     {
    187         date_default_timezone_set("Asia/Shanghai"); 
    188         $time = strtotime(date('Y-m-d H:i:s'));
    189 
    190         if(!$this->redis->sismember("comment:uploadset",$comment_id))
    191         {
    192             //文章不存在集合里,需要更新
    193             $this->redis->sadd("comment:uploadset",$comment_id);
    194             //更新到队列
    195             $data = array(
    196                 "id" => $comment_id,
    197                 "time" => $time,
    198             );
    199             $json = json_encode($data);
    200             $this->redis->lpush("comment:uploadlist",$json);
    201         }
    202     }
    203 }
    204 
    205 //调用
    206 $user_id = 100;
    207 $type = 1;
    208 $comment_id= 99;
    209 $good = new Good();
    210 $rel = $good->click($user_id,$type,$comment_id);
    211 var_dump($rel);

    温馨提示:

      1.上面代码只是一个实现的方法之一,里面的代码没精分过,适合大部分小伙伴阅读。用心看总有收获。

      2.对于第三方接口,应该在外面包装多一层的,但是边幅有限,我就不做这么详细,提示,大家可以作为参考。

      3.剩下的将数据返回数据的方法,等下篇再继续了。欢迎大家来交流心得。

    redis手册中文版传送门:http://www.cnblogs.com/zcy_soft/archive/2012/09/21/2697006.html#string_INCR;

     

  • 相关阅读:
    基于Freescale的主流芯片HCS08
    BizTalk Server 2010 映射器(Mapper) [ 下篇 ]
    BizTalk Server 2010 使用 WCF Service [ 中篇 ]
    Ext JS 4 Beta 1发布了
    Step by Step WebMatrix网站开发之一:Webmatrix安装
    REST WebService与SOAP WebService的比较
    BizTalk Server 2010 使用 WCF Service [ 上篇 ]
    BizTalk Server 2010 映射器(Mapper) [ 中篇 ]
    BizTalk Server 2010 映射器(Mapper) [ 上篇 ]
    ExtJS 4 Beta 2预览:Ext.Brew包
  • 原文地址:https://www.cnblogs.com/sunshine-H/p/7922285.html
Copyright © 2011-2022 走看看