用Redis 有序集合设计粉丝关注表

在用关系型数据库(Mysql)设计粉丝关注表时,感觉要写很多判断,比如:
新增关注时,要判断之前的关联是否存在,如果不存在才插入。
又或者获取粉丝数量或关注的人数量,一般做法是在数据库里查出来再存到Redis,新增关注后再用redis的incr命令+1,在查询的过程中又要考虑索引问题。。。总之比较的麻烦

后来有考虑过用redis的的集合(Set)来存粉丝和关注的人,但问题是集合是无序的,想要获取关注时间就无能为力了。

最后用Redis的有序集合解决了上述的所有问题,有顺集合中有一个score分数,会根据这个分数有序排列,我们直接把关注时间当成这条数据的分数,这样就有搞定了,还能根据分数取范围、分页、排序等操作。于是将粉丝、关注的人全部存入redis中,但又担心redis挂了,数据还没来得及写入到磁盘导致数据丢失的问题,我又在写入redis的时候同步写了一份到mysql。

相关命令:
zRank:判断指定key的分数,我主要用来判断某个人是否在集合内
multi和exec命令,这两个是用来写事务操作的,比如我关注了张三,会创建两个有序集合,一个是我关注的人,第一个是张三的粉丝

zCount:根据分数统计集合内的数量,我用来统计新增的数丝数
zRem:删除成员,用于取关
zCard:获取集合内所有成员,用于统计粉丝数和关注人数
zRevRange:用于获取关注者列表、数丝列表,按分数(关注时间)倒序排列,还可以分页

以下是相关代码,仅做参考

<?php

namespace app\common\model;

class Follow extends \think\Model
{
    protected $table = 'xxxxxxx';

    /**
     * 是否关注了某人
     *
     * @param int $userId
     * @param int $followerId
     *
     * @return bool
     */
    public function isFollow(int $userId, int $followerId): bool
    {
        $key = Cache::UserFollow . $userId;
        $rank = Cluster::init()->zRank($key, $followerId);
        return $rank !== false;
    }

    /**
     * 添加关注
     *
     * @param int $userId
     * @param int $followerId
     *
     * @return bool
     */
    public function addFollow(int $userId, int $followerId): bool
    {
        $key = Cache::UserFollow . $userId;
        $key2 = Cache::UserFans . $followerId;
        $subscribeTime = time();

        $redis = Cluster::init()->multi();
        $result = $redis->zAdd($key, $subscribeTime, $followerId)
            ->zAdd($key2, $subscribeTime, $userId)
            ->exec();

        // 写入DB作为备份
        if ($result[0] === 1 && $result[1] === 1) {
            $insertDb = [
                'user_id' => $userId,
                'follower_id' => $followerId,
                'status' => 1,
                'create_time' => $subscribeTime
            ];
            $this->table($this->table)->insert($insertDb);
        }

        // 设置第一个用户关注时间,用于统计新增粉丝数
        Cluster::init()->setnx($this->getFansFirstTimeKey($followerId), time());

        return true;
    }

    /**
     * 重置未读新增粉丝的起始时间
     * 如果粉丝,才新增该key(避免每个人都生成一个无意义的key)
     *
     * @param int $userId
     */
    public function resetFansFirstTime(int $userId)
    {
        Cluster::init()->set($this->getFansFirstTimeKey($userId), time(), ['xx']);
    }

    /**
     * 获取未读新增粉丝的起始时间
     *
     * @param int $userId
     *
     * @return int
     */
    public function getFansFirstTime(int $userId): int
    {
        return intval(Cluster::Get($this->getFansFirstTimeKey($userId)));
    }

    /**
     * 获取新增粉丝数量
     *
     * @param int $userId
     *
     * @return int
     */
    public function getNewFansNum(int $userId): int
    {
        $firstTime = $this->getFansFirstTime($userId);
        $key = Cache::UserFans . $userId;
        return Cluster::init()->zCount($key, $firstTime, time());
    }

    /**
     * 取消关注
     *
     * @param int $userId
     * @param int $followerId
     *
     * @return bool
     */
    public function unFollow(int $userId, int $followerId): bool
    {
        $key = Cache::UserFollow . $userId;
        $key2 = Cache::UserFans . $followerId;

        $redis = Cluster::init()->multi();
        $result = $redis->zRem($key, $followerId)
            ->zRem($key2, $userId)
            ->exec();

        // 写入DB作为备份
        if ($result[0] === 1 && $result[1] === 1) {
            $this->table($this->table)
                ->where("user_id", $userId)
                ->where("follower_id", $followerId)
                ->setField("status", 2);
        }

        return true;
    }

    /**
     * 获取我的粉丝列表
     *
     * @param int $userId
     * @param int $offset
     * @param int $limit
     *
     * @return array
     */
    public function FansList(int $userId, int $page, int $pageSize)
    {
        $key = Cache::UserFans . $userId;
        return $this->getList($key, $page, $pageSize);
    }

    /**
     * 获取我的关注列表
     *
     * @param int $userId
     * @param int $offset
     * @param int $limit
     *
     * @return array
     */
    public function FollowList(int $userId, int $page, int $pageSize): array
    {
        $key = Cache::UserFollow . $userId;
        return $this->getList($key, $page, $pageSize);
    }

    /**
     * 获取粉丝数量
     *
     * @param int $userId
     *
     * @return int
     */
    public function getFansNum(int $userId): int
    {
        $key = Cache::UserFans . $userId;
        return Cluster::init()->zCard($key);
    }

    /**
     * 获取我关注的人数量
     *
     * @param int $userId
     *
     * @return int
     */
    public function getFollowNum(int $userId): int
    {
        $key = Cache::UserFollow . $userId;
        return Cluster::init()->zCard($key);
    }

    private function getList(string $key, int $page, int $pageSize)
    {
        $offset = ($page - 1) * $pageSize;
        return Cluster::init()->zRevRange($key, $offset, $offset + $pageSize - 1, true);
    }

    private function getFansFirstTimeKey(int $userId)
    {
        return Cache::UserFansFirstTime . $userId;
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值