Node Redis 获取高分的高效方式

Node Redis Efficient Way to Get High Scores

在 redis 中,我有一个包含 100 000 个用户并且还在不断增长的大型数据集。为了制作排行榜,我扫描了整个数据库,获取每个用户的所有哈希值。然后我一个一个过一遍,得到分数。稍后,我在 javascript 中进行排序和修整。我的问题是,做同样的事情更快的方法是什么?当前查询需要几秒钟。我的直觉是将数据存储在 JS 中,并且只 运行 查询一次。

getLeaderboards: function(player){
    var self = this;

    async.waterfall([
        function(callback){
            client.smembers("usr", function(err, replies){
                var pvpleaders = [];
                var expleaders = [];
                var lastUser = false;
                for(var i = 0; i < replies.length; i++){
                    var name = replies[i].toString();
                    if(i == replies.length - 1){
                        lastUser = true;
                    }
                    if(name.charAt(0) != "_"){
                        callback(err, name, pvpleaders, expleaders, lastUser);
                    }
                }
            });
        },
        function(name, pvpleaders, expleaders, lastUser, callback){
            client.hgetall("u:" + name, function(err, reply){
                if(reply){
                    kills = parseInt(reply.kills);
                    exp = parseInt(reply.exp);
                    remortexp = parseInt(reply.remortexp) || 0;
                    if(kills > 0){
                        //log.info(name+' '+kills);
                        pvpleaders.push([kills, name]);
                    }
                    if(exp + remortexp > 0){
                        //log.info(name+' '+exp);
                        expleaders.push([exp + remortexp, exp, remortexp, name]);
                    }
                }
                if(lastUser){
                    log.info('user user');
                    callback(err, pvpleaders, expleaders);
                }
            });
        }
    ], function (err, pvpleaders, expleaders) {
        if(err){log.info(err);}
        pvpleaders.sort(function(a, b) { if (a[0] === b[0]) { return 0; } else { return (a[0] > b[0]) ? -1 : 1; } });
        pvpleaders.splice(30, pvpleaders.length);
        expleaders.sort(function(a, b) { if (a[0] === b[0]) { return 0; } else { return (a[0] > b[0]) ? -1 : 1; } });
        expleaders.splice(30, expleaders.length);
        var leaderboards = JSON.stringify({pvp: pvpleaders, exp: expleaders});
        player.sendLeaderboards(leaderboards);
    });
},

基本上你的问题是关于如何设计一个高效的记分牌,如果我的理解是正确的话。

一个简单的解决方案是在 Redis 中使用 ZADD。它将创建一个排序集。 ZADD复杂度的时间是O(M * log(N)),用ZRANGE得到结果,时间复杂度是O(log(N)+M)。

一个更有效的解决方案需要您更多地考虑您的数据。你的数据有范围吗?例如,如果您所有用户的分数都在 0 到 10000 之间?如果是这样,您可以在此处使用桶排序并将结果缓存在 redis 中。插入和查找都是O(1)(因为桶的数量是固定的)。只有前 100 名,你维护一个排序的数据结构。这样,你就有了函数 "Show top 100 scoreboard" 和 "Search the ranking of given user" 并且两者都是恒定的时间复杂度。

使用 Sorted Set 数据结构 - 排行榜是它的典型用例。

将成员的值设置为玩家ID,将分数设置为玩家的分数。然后您可以在服务器端执行有效范围(z[rev]rangebyscore 命令)并且结果已经排序。在大多数情况下,这将比任何客户端 post 处理快得多,因为可以节省网络流量。