[置顶] 休闲游戏中使用redis作为排行榜数据存储

1.排行榜的需求


[置顶] 休闲游戏中使用redis作为排行榜数据存储_第1张图片            [置顶] 休闲游戏中使用redis作为排行榜数据存储_第2张图片

    我有做了一个网站 http://www.cgame360.com/,专门放一些简单的html5游戏。现在有2个需求,我想获得一个游戏的top 10的用户的分数,还有一个是某个用户的排名,加上前后4名的人员。上图中少了名字,那个cocos2d js输入框有点问题就没加了。
     现在的问题是用什么数据库来存储比较好?我想要尽可能快的查找出数据。
     我搞过mysql这种关系型的数据库,第一感觉用在这里会很慢。数据量一大就会很慢,因为它排序的时候会对所有数据进行处理。还有第二个需求用sql语句会很蛋疼,我还没想到高效的实现方式。第一个前10名的,可以搞一个表来专门存前10名的。主要就是卡在第二个需求。
    我google了下,发现nosql比较适合现在的需求。nosql比较有名的是Hadoop,MongoDB。但我发现一个更加适合这个需求的数据库redis.

2.redis中的有序集合


2.1redis有序集合插入一条数据


   这里没时间再从安装redis讲起了。我记得好像Mac系统比较容易用,windows好像不太好用redis。
   redis的有序集合插入一条数据非常容易:
   ZADD key score memeber
具体例子:
   ZADD game1 89 tom
这样game1中就有一条数据了Tom的分数是89

2.2 redis得到排名在某个范围的元素列表


ZRANGE key start stop [withscores]
ZREVRANGE key start stop [withscores]

具体例子:
ZRANGE game1 0 -1 withscores
这样就可以得到game1中所有的记录了,从小到大排序,-1表示最后一个元素的意思。
那么我们想要得到前10的记录用什么命令呢?
非常简单:ZRANGE game1 -10 -1 withscores  -10表示往数10个的意思。下面是我mac机执行的输出:

127.0.0.1:6379> zrange g_1 -10 -1 withscores
 1) "tom5"
 2) "506"
 3) "tom6"
 4) "507"
 5) "hug"
 6) "600"
 7) "tom"
 8) "5000"
 9) "tom8"
10) "5088"
11) "tom9"
12) "5089"
13) "tom10"
14) "5090"
15) "tom11"
16) "5091"
17) "tom12"
18) "5092"
19) "tom13"
20) "5093"

zrevrange 是表示逆向的意思,会从大到小输出。

2.3 redis得到一个元素的排名


  ZRANK key member
  ZREVRANK key memeber
具体例子:
 zrank game1 tom

2.4 redis 得到一个元素的分数


  ZSCORE key member
具体例子:
zscore game1 tom

3.PHP 中使用redis


    上面讲的redis命令已经够我们使用了,接下来就是用redis与java或者php等服务器语言来组成一个服务了。我这里选择了PHP,我感觉用户提交一个分数用http的post,得到排行榜的分数用php的get够用了。
php中使用redis有两种方式,分别是predis 和phpredis,前者是完全使用php的,后者是c语言编写的php扩展。后者性能会好些,但需要安装php扩展比较麻烦。predis非常简单,就是一些php文件,扔到网站目录下就能工作了。这里选择使用了predis.

3.1 插入一条数据

<?php
require './predis/autoload.php';

$redis = new Predis\Client(
    array(
        'scheme'=>'tcp',
        'host'=>'127.0.0.1',
        'port'=>6379,
 ));


//header("content-type:text/html;charset=utf-8");

if(trim($_POST['game_id']) == '' || trim($_POST['user_name']) == ''  || trim($_POST['score']) == ''){
    echo 'need more information!';
    exit;
}else{
    // echo 'OK';
    // echo $_POST['game_id'];
    // echo $_POST['user_name'];
    // echo $_POST['score'];

    $game_id = "g_" . $_POST['game_id'];
    $name = $_POST['user_name'];
    $score = $_POST['score'];

    if($score > 0){
       $beforeScore = $redis->zscore($game_id
, $name);

       if($score > $beforeScore){
         $itemScore = array($name => $score);
         $redis->zadd($game_id, $itemScore);
       }

    }

    
}

主要这里我就简单地判断了下他现在的分数是不是比以前的高,比以前的高就插入一条数据。非常简单。

3.2 得到前10的玩家分数


<?php
require './predis/autoload.php';
$redis = new Predis\Client(
    array(
        'scheme'=>'tcp',
        'host'=>'127.0.0.1',
        'port'=>6379,
 ));

if(trim($_GET['game_id']) == ''){
    echo 'need game_id';
}else{
	$game_id = "g_" . $_GET['game_id'];
    $board_score = $redis->zrange($game_id, -10, -1, 'withscores');
    //print_r($board_score);
    echo json_encode($board_score);
    //echo $board_score;

}

这里看起来非常简单,最后我用了json_encode把php中的数组转换成json了。这样方便处理。

3.3得到我的排名


<?php
require './predis/autoload.php';
$redis = new Predis\Client(
    array(
        'scheme'=>'tcp',
        'host'=>'127.0.0.1',
        'port'=>6379,
 ));

if(trim($_GET['game_id']) == '' || trim($_GET['user_name']) == ''){
    echo 'need game_id and name';
}else{
	$game_id = "g_" . $_GET['game_id'];
    $user_name = $_GET['user_name'];

    $userCurrentRange = $redis->zrevrank($game_id, $user_name); 

     //echo 'current range:' . $userCurrentRange;

    $result = array();
    if(true){
        $halfCount = 4;

         $leftRank = $userCurrentRange - $halfCount;
        $rightRank = $userCurrentRange + $halfCount;
        if($leftRank < 0){
          $leftRank  = 0;
        }

        $board_score = $redis->zrevrange($game_id, $leftRank, $rightRank, 'withscores');
        $index = 0;
        foreach($board_score as $name => $score){
           if($name == $user_name){
               break;
           }else{
               $index = $index + 1;
           }
       }

        $firstRank = $userCurrentRange - $index + 1;

        foreach($board_score as $name => $score){
           $eachItem = array();
           $eachItem['name'] = $name;
           $eachItem['score'] = $score;
           $eachItem['rank'] = $firstRank;

           $result[] = $eachItem;

           $firstRank = $firstRank + 1;
        }

        echo json_encode($result);
 

    }else{
        echo json_encode($result);
    }


     

}

这里稍微复杂了一点点,我先得到这个玩家的排名,这样就可以得到他前后4名玩家的排名区间了,用下zrevrange就ok了。就是要注意left是负数的话是其他的意思,所以要避免是负数。最后我对php数组中加入了排名信息,再次以json返回。

4.0 讲下cocos2d js中的http


php已经可以返回json的数据了,讲下cocos2d js中如何使用http 显示top 10的玩家信息。这里直接给出代码了,也比较简单。主要要注意的是要对返回的数据进行JSON.parse处理。插入数据用post,得到排行榜信息用get就OK了。我也没加反作弊功能。
getJsonFromUrl:function(){
        var xhr = cc.loader.getXMLHttpRequest();
        var args = "?game_id=" + gameId;
        xhr.open("GET", rankBoardURL + args, true);
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && (xhr.status >= 200 && xhr.status <= 207)) {
                var response = xhr.responseText;
                cc.log("res:" + response);
                cc.log("send success");
                var obj = JSON.parse(response);
                this.initContent(obj);
            }
        }.bind(this);

        xhr.send();
    },
    initContent:function(jsonValue){
        var size = cc.director.getWinSize();

        var fontDefBlueStroke = new cc.FontDefinition();
        fontDefBlueStroke.fontName = "Arial";
        fontDefBlueStroke.fontSize = 50;
        fontDefBlueStroke.textAlign = cc.TEXT_ALIGNMENT_LEFT;

        var jsonLength = 0;
        for(var name in jsonValue){
            jsonLength++;
        }
        cc.log("jsonlength:" + jsonLength);
        var nameMargin = 50;
        var nameStartPositionY = 800 - nameMargin * jsonLength;

        var lastRank = jsonLength;
       for(var name in jsonValue){
          cc.log(name);
          cc.log(jsonValue[name]);

          this.rankLabel = new cc.LabelTTF(lastRank + ':', fontDefBlueStroke);
          this.addChild(this.rankLabel);
          this.rankLabel.x = 100;
          this.rankLabel.y = nameStartPositionY;
          this.rankLabel.setAnchorPoint(cc.p(0, 0.5));


          this.nameLabel = new cc.LabelTTF(name, fontDefBlueStroke);
          this.addChild(this.nameLabel);
          this.nameLabel.x = size.width * 0.5 - 100;
          this.nameLabel.y = nameStartPositionY;
          this.nameLabel.setAnchorPoint(cc.p(0, 0.5));

          this.scoreLabel = new cc.LabelTTF(parseFloat(jsonValue[name]).toFixed(2), fontDefBlueStroke);
          this.addChild(this.scoreLabel);
          this.scoreLabel.x = size.width * 0.5;
          this.scoreLabel.y = nameStartPositionY;
          this.scoreLabel.setAnchorPoint(cc.p(0, 0.5));

          nameStartPositionY = nameStartPositionY + nameMargin;
          lastRank = lastRank - 1;
       }
    }


最后大家玩下这个使用了上面讲到的技术的html5游戏,用cocos2d js开发的。http://www.cgame360.com/halloweengame/

整个排行榜的PHP服务我已经打包成一个文件了,点击下面的链接下载。包含了predis,如果你有mac系统或者linux系统,安装好php,redis等等,把这个解压到网站根目录应该就可以用了的.我也是第一次使用redis,哪里用错请评论中指出,谢谢。
http://www.waitingfy.com/?attachment_id=1421



http://www.waitingfy.com/archives/1420


你可能感兴趣的:([置顶] 休闲游戏中使用redis作为排行榜数据存储)