加载中…
个人资料
jqlblue
jqlblue
  • 博客等级:
  • 博客积分:0
  • 博客访问:1,916
  • 关注人气:7
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

用redis实现排行榜(并列排行)

(2012-07-05 17:03:51)
标签:

redis

rank

same

score

it

分类: web技术相关
redis中的sorted set数据结构,几乎就是为这个场景而生的。
假设一个用户积分排行的场景。
有a,b,c三个用户,他们的积分分别是1,2,3,那么通过如下操作,就可以创建一个排行:
redis.zadd my_rank 1 a
redis.zadd my_rank 2 b
redis.zadd my_rank 3 c
通过 redis.zrank my_rank a 就可以取到 a 在排行中排第几。

但是,如果b用户的积分也是1呢,即a,b,c三个用户,的积分分别是1,1,3。
这个时候,用上面的方法,会发现a还是排第一,b依旧排第二。
因为它原生不支持并列排行,zadd时,当field的score相同时,会以添加时间加权。最先添进去的,就排在最前面,因为先添加了a,后添加了b,所以a在b前。
那如何实现并列排行呢,直接上代码:
    public function setScoreRank($appId, $userId, $exp)
    {
        $redisHandler = $this->getGlobalRedis();
        // 存储用户id,用户积分映射 hset
        $memberKey    = parent::RANK_MEMBER_KEY . ':' . $appId;
        // 存储用户积分,用户数映射 hset
        $scoreStatKey = parent::SCORE_STAT_KEY . ':' . $appId;
        // 存储用户积分排行 zset
        $scoreRankKey = parent::RANK_SCORE_KEY . ':' . $appId;
        $oldExp       = $redisHandler->hGet($memberKey, $userId);
        if ($oldExp === false)
        {
            $redisHandler->multi(Redis::PIPELINE)
                ->hSet($memberKey, $userId, $exp)
                ->hIncrBy($scoreStatKey, $exp, 1)
                ->exec();
        }
        else
        {
            // 旧积分的对应的统计减1
            // 新积分对应的统计+1
            $redisHandler->multi(Redis::PIPELINE)
                ->hSet($memberKey, $userId, $exp)
                ->hIncrBy($scoreStatKey, $exp, 1)
                ->hIncrBy($scoreStatKey, $oldExp, -1)
                ->exec();
        }
        $oldExpStat = $redisHandler->hGet($scoreStatKey, $oldExp);
        // 旧的积分在排行榜中存在,则更新新旧积分排行。不存在的话,需要删除旧的
        if ($oldExpStat)
        {
            $redisHandler->multi(Redis::PIPELINE)
                ->zAdd($scoreRankKey, $exp, $exp)
                ->zAdd($scoreRankKey, $oldExp, $oldExp)
                ->exec();
        }
        else
        {
            $redisHandler->multi(Redis::PIPELINE)
                ->zAdd($scoreRankKey, $exp, $exp)
                ->zRem($scoreRankKey, $oldExp)
                ->exec();
        }
    }
    public function getRank($appId, $userId)
    {
        $redisHandler  = $this->getGlobalRedis();
        $rankMemberKey = parent::RANK_MEMBER_KEY . ':' . $appId;
        $rankKey       = parent::RANK_SCORE_KEY . ':' . $appId;
        $userExp       = $redisHandler->hGet($rankMemberKey, $value);
        return $redisHandler->zRevrank($rankKey, $userExp);
    }

严格来说,这个应该叫积分排行。
用到了3个key:RANK_MEMBER_KEY(用户-积分的映射,Hashes),SCORE_STAT_KEY(积分 - 拥有该积分的人数映射Hashes),RANK_SCORE_KEY(积分排行 Sorted Sets 
先通过RANK_MEMBER_KEY取到用户的积分,然后在RANK_SCORE_KEY去取积分对应的排行。
SCORE_STAT_KEY这个key用作维护RANK_SCORE_KEY。


0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有