zoukankan      html  css  js  c++  java
  • php+redis实现排行榜(demo)

    比如讲数据存储在了 Redis 的有序集合 user_score 中,使用 Redis 来统计玩家排行榜的数据。

    首先我们需要思考的是,一个典型的游戏排行榜都包括哪些功能呢?

    统计全部玩家的排行榜
    按名次查询排名前 N 名的玩家
    查询某个玩家的分数
    查询某个玩家的排名
    对玩家的分数和排名进行更新
    查询指定玩家前后 M 名的玩家
    增加或移除某个玩家,并对排名进行更新
    在 Redis 中实现上面的功能非常简单,只需要使用 Redis 我们提供的方法即可,针对上面的排行榜功能需求,我们分别来看下 Redis 是如何实现的。

    统计全部玩家的排行榜
    在 Redis 里,统计全部玩家的排行榜的命令格式为 ZREVRANGE 排行榜名称 起始位置 结束为止 [WITHSCORES] 。

    我们使用这行命令即可:

    ZREVRANGE user_score 0 -1 WITHSCORES

    我们对玩家排行榜 user_score 进行统计,其中 -1 代表的是全部的玩家数据, WITHSCORES 代表的是输出排名的同时也输出分数。

    按名次查询排名前 N 名的玩家
    同样我们可以使用 ZREVRANGE 完成前 N 名玩家的排名,比如我们想要统计前 10 名玩家,可以使用: ZREVRANGE user_score 0 9 。

     

    查询某个玩家的分数
    命令格式为 ZSCORE 排行榜名称 玩家标识 。

    时间复杂度为 O(1) 。

    如果我们想要查询玩家 10001 的分数可以使用: ZSCORE user_score 10001 。

     

    查询某个玩家的排名
    命令格式为 ZREVRANK 排行榜名称 玩家标识 。

    时间复杂度为 O(log(N)) 。

    如果我们想要查询玩家 10001 的排名可以使用: ZREVRANK user_score 10001 。

    对玩家的分数进行更新,同时排名进行更新
    如果我们想要对玩家的分数进行增减,命令格式为 ZINCRBY 排行榜名称 分数变化 玩家标识 。

    时间复杂度为 O(log(N)) 。

    比如我们想对玩家 10001 的分数减 1,可以使用: ZINCRBY user_score -1 10001 。

    然后我们再来查看下玩家 10001 的排名,使用: ZREVRANK user_score 10001 

    你能看到排名由 17153 降到了 18036 名。

    查询指定玩家前后 M 名的玩家

    比如我们想要查询玩家 10001 前后 5 名玩家都是谁,当前已知玩家 10001 的排名是 18036,那么可以使用: ZREVRANGE user_score 18031 18041 

    这样就可以得到玩家 10001 前后 5 名玩家的信息。

    增加或删除某个玩家,并对排名进行更新

    如果我们想要删除某个玩家,命令格式为 ZREM 排行榜名称 玩家标识 

    时间复杂度为 O(log(N)) 

    比如我们想要删除玩家 10001,可以使用: ZREM user_score 10001 

    这样我们再来查询下排名在 18031 到 18041 的玩家是谁,使用: ZREVRANGE user_score 18031 18041 

    你能看到玩家 10001 的信息被删除,同时后面的玩家排名都向前移了一位。

    如果我们想要增加某个玩家的数据,命令格式为 ZADD 排行榜名称 分数 玩家标识 

    时间复杂度为 O(log(N)) 

    这里,我们把玩家 10001 的信息再增加回来,使用: ZADD user_score 93.1504697596 10001 

    然后我们再来看下排名在 18031 到 18041 的玩家是谁,使用: ZREVRANGE user_score 18031 18041 

    你能看到插入了玩家 10001 的数据之后,排名又回来了。

    下面封装的一个demo,摘自https://blog.csdn.net/u011822516/article/details/82734992

    <?php
    namespace Leaderboard;
    
    /**
     * 使用rediszset的的商品排行榜
     * @author yiwang
     *        
     */
    class RedisLeaderboard
    {
    
        /**
         *
         * @var object redis client
         */
        private $redis;
        /**
         *
         * @var string 放置排行榜的key
         */
        private $leaderboard;
    
        /**
         * 构造函数
         * @param object $redis 已连接redis的phpredis的对象
         * @param string $leaderboard 字符串,排行榜的key名
         */
        public function __construct($redis = [], $leaderboard = '')
        {
    
            if ($redis) {
              $this->redis = $redis;
            } else {
              $this->redis = new Redis();
              $this->redis->connect('127.0.0.1');
            }
    
            if ($leaderboard) {
                //这里不会检查当前的key值是否存在,是为了方便重新访问对应的排行榜
                $this->leaderboard = $leaderboard;
            } else {
                $this->leaderboard = 'leaderboard:' . mt(1, 100000);
                while (!empty($this->redis->exists($this->leaderboard))) {
                    $this->leaderboard = 'leaderboard:' . mt(1, 100000);
                }
            }
    
        }
        /**
         * 获取当前的排行榜的key名
         * @return string
         */
        public function getLeaderboard()
        {
            return $this->leaderboard;
        }
        /**
         * 将对应的值填入到排行榜中
         * @param  $node 对应的需要填入的值(比如商品的id)
         * @param number $count 对应的分数,默认值为1
         * @return Long 1 if the element is added. 0 otherwise.
         */
        public function addLeaderboard($node, $count = 1)
        {
            return $this->redis->zAdd($this->leaderboard, $count, $node);
        }
        /**
         * 给出对应的排行榜
         * @param int $number 需要给出排行榜数目
         * @param bool $asc 排序顺序 true为按照高分为第0
         * @param bool $withscores 是否需要分数
         * @param callback $callback 用于处理排行榜的回调函数
         * @return [] 对应排行榜
         */
        public function getLeadboard($number, $asc = true, $withscores = false,$callback = null)
        {
            if ($asc) {
                $nowLeadboard =  $this->redis->zRevRange($this->leaderboard, 0, $number -1, $withscores);//按照高分数顺序排行;
            } else {
                $nowLeadboard =  $this->redis->zRange($this->leaderboard, 0, $number -1, $withscores);//按照低分数顺序排行;
            }
    
    
            if ($callback) {
                //使用回调处理
                return $callback($nowLeadboard);
            } else {
                return $nowLeadboard;
            }
        }
        /**
         * 获取给定节点的排名
         * @param string $node 对应的节点的key名
         * @param string $asc 是否按照分数大小正序排名, true的情况下分数越大,排名越高
         * @return 节点排名,根据$asc排序,true的话,第一高分为0,false的话第一低分为0
         */
        public function getNodeRank($node, $asc = true)
        {
            if ($asc) {
                //zRevRank 分数最高的排行为0,所以需要加1位
                return $this->redis->zRevRank($this->leaderboard, $node);
            } else {
                return $this->redis->zRank($this->leaderboard, $node);
            }
        }
    
    }

     

  • 相关阅读:
    RAM调优之日志分析
    HDU Always Cook Mushroom (极角排序+树状数组)
    并非全部的程序猿都适合做技术管理
    HTTP Header具体解释
    Linux 通配符
    寻找正在连接中的网络连接
    hdu 1052 田忌赛马
    linux上电自启动应用程序具体解释
    C++ 中的 const 类型变量
    FileUtil
  • 原文地址:https://www.cnblogs.com/wt645631686/p/9767719.html
Copyright © 2011-2022 走看看