麦都-平台开发组

谈谈社区中评论模块的设计

写作时间:2018-05-09

社区,相信很多人开发人员都接触过,1级,2级...,N级。那就聊聊1级评论的设计方法,仅供参考。

这里我是采用redis进行存储和排序查询的,怎样查询第一级评论内容

1、第一缓存key

//存储回复id
private $redis_post_id = 'thread_{post_info_hash_%d}'; //帖子id

//回复hash方式存储 private $redis_post_info_hash = '{post_info_hash_%d}_%d'; //存储回复信息 帖子id,回复id

为什么缓存key需要{....},这是由于在集群redis中使用hash tag

Hash Tags

分片,就是一个hash的过程:对key做md5,sha1等hash算法,根据hash值分配到不同的机器上。

为了实现将key分到相同机器,就需要相同的hash值,即相同的key(改变hash算法也行,但不简单)。

但key相同是不现实的,因为key都有不同的用途。例如user:user1:ids保存用户的tweets ID,user:user1:tweets保存tweet的具体内容,两个key不可能同名。

仔细观察user:user1:ids和user:user1:tweets,两个key其实有相同的地方,即user1。能不能拿这一部分去计算hash呢?

这就是hash tag 。允许用key的部分字符串来计算hash。

当一个key包含 {} 的时候,就不对整个key做hash,而仅对 {} 包括的字符串做hash。

假设hash算法为sha1。对user:{user1}:ids和user:{user1}:tweets,其hash值都等同于sha1(user1)。


2、数据存储
private function addToRedisHash($threadId = 0, $postInfo = array(), $update = 0)
    {
        if(empty($threadId) || empty($postInfo))return false;

        $redis     =  Cache::getInstance('Redis');
        $id_key    =  sprintf($this->redis_post_id, $threadId);
        $info_key  =  sprintf($this->redis_post_info_hash, $threadId, $postInfo['id']);

        $res = $redis->sAdd($id_key, $postInfo['id']);
        if($res || $update == 1){
            $data = [
                'id'   =>  $postInfo['id'],
                'pid'  =>  $postInfo['pid'],
                'thread_id'     =>  $postInfo['thread_id'],
                'user_id'       =>  $postInfo['user_id'],
                'content'       =>  $postInfo['content'],
                'created_time'  =>  $postInfo['created_time'],
                'reply_num'     =>  $postInfo['reply_num'],
                'zan_num'       =>  $postInfo['zan_num'],
                'toid'          =>  $postInfo['toid'],
                'latest_post_time'  =>  $postInfo['latest_post_time']
            ];
            $redis->hMset($info_key, $data);
            //处理缓存时间
            $redis->expire($id_key, $this->redis_post_list_time_out);
            $redis->expire($info_key, $this->redis_post_list_time_out);
        }
        return $res;
    }

帖子回复所有id存储在list中($redis_post_id),所有内容存储在hash中($redis_post_info_hash)

3、按指定条件查询

private function sortPostListByHash($threadId = 0, $by = 'zan', $limit = 10)
    {
        if(empty($threadId) || empty($by))return [];

        $id_key = sprintf($this->redis_post_id, $threadId);
        $redis  = Cache::getInstance('Redis');
        /**
         * 按score从大到小排序,取得id
         */
        //缓存key
        $key =  C('DATA_CACHE_PREFIX').'{post_info_hash_'.$threadId.'}_';

        $sort = array('SORT'  => 'DESC');
        if($by == 'id'){
            //默认按id从大小
        }elseif($by == 'zan'){
            $sort['BY'] = $key.'*->zan_num'; //点赞从多到少排序
        }elseif($by == 'time'){
            $sort['BY'] = $key.'*->latest_post_time'; //更新时间从大到小排序
        }
        $count  = $redis->sCard($id_key);
        if($count == 0)return ['list'=>[], 'totalPage'=>0, 'count'=>0, 'cache'=>0];

        $page   = new \Think\Page($count, $limit);
        $totalPage  = ceil($count/$limit);
        $sort['limit'] = [$page->firstRow,$page->listRows];

        $lists  = $redis->sort($id_key, $sort);
        return ['list'=>$lists, 'totalPage'=>$totalPage, 'count'=>$count, 'cache'=>1];
    }

此处使用到的是list中sort函数,sort中的BY可以指定hash中某个字段。从而实现按指定条件(id,时间,赞等)排序,并获取回复id,再根据id取hash中的数据显示。

仅供参考,二级内容可以更加一级ID查询缓存。帖子回复的更新、删除、增加就不进行说明了。