【问题标题】:Node Redis Efficient Way to Get High ScoresNode Redis 获取高分的高效方法
【发布时间】:2015-01-10 01:38:35
【问题描述】:

在 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);
    });
},

【问题讨论】:

    标签: javascript node.js redis


    【解决方案1】:

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

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

    更有效的解决方案需要您更多地考虑您的数据。你的数据有范围吗?例如,如果您所有用户的分数都落在 0 到 10000 之间?如果是这样,您可以在此处使用桶排序并将结果缓存在 redis 中。插入和查找都是 O(1)(因为桶的数量是固定的)。只有前 100 名,您维护一个排序的数据结构。这样,你就有了“显示前 100 名记分牌”和“搜索给定用户的排名”的功能,两者都是常数时间复杂度。

    【讨论】:

    • 我在寻找解决方案时阅读了一些关于 ZADD 用于自动完成的信息。我想我会试试这个,因为我没有范围。我的分数结果是 null 到无穷大。
    • 是的,您可以先尝试 ZADD,直到遇到一些性能问题。对于桶排序,您可以为您的 NULL 结果使用一个特殊的桶。对吗?
    【解决方案2】:

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

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

    【讨论】:

    • 我不太明白。不过要清楚,排序是在服务器上完成的,请参阅瀑布中的最后一个函数。我的代码可能很糟糕,但没那么糟糕。
    • 无意冒犯 - 我的意图是你会使用按分数预排序的排序集。调用 sort 方法可能会调用 Redis 的 SORT,这会占用 CPU。
    猜你喜欢
    • 2015-11-03
    • 2017-12-24
    • 2012-07-17
    • 2022-01-04
    • 1970-01-01
    • 2020-07-17
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多