Python 谷歌应用引擎全球排行榜

Python 谷歌应用引擎全球排行榜,python,google-app-engine,cron,leaderboard,Python,Google App Engine,Cron,Leaderboard,我想为一款手机游戏构建一个后端,其中包括一个所有玩家的“实时”全球排行榜,使用Google App Engine(Python)进行持续一定天数的活动 典型用法如下所示: -用户开始和结束战斗,获得点数(战斗2-5分钟) -在比赛期间,积分在球员账户中累积。 -玩家可以随时查看排行榜。 -排行榜将返回前10名球员,以及略高于或低于该球员得分的5名球员 现在,在实时方面没有真正的限制,电路板可以每30秒更新一次,每小时更新一次。我希望它是尽可能的“快”,而不会花费太多 由于我对GAE不太熟悉,因此

我想为一款手机游戏构建一个后端,其中包括一个所有玩家的“实时”全球排行榜,使用Google App Engine(Python)进行持续一定天数的活动

典型用法如下所示: -用户开始和结束战斗,获得点数(战斗2-5分钟) -在比赛期间,积分在球员账户中累积。 -玩家可以随时查看排行榜。 -排行榜将返回前10名球员,以及略高于或低于该球员得分的5名球员

现在,在实时方面没有真正的限制,电路板可以每30秒更新一次,每小时更新一次。我希望它是尽可能的“快”,而不会花费太多

由于我对GAE不太熟悉,因此我想到了以下解决方案:

  • 每个玩家实体都有一个事件点属性
  • 使用Cron作业,每隔一段时间,对所有得分不为零的玩家的数据存储进行查询。问题是 分类
  • cron作业然后遍历查询结果,写回每个Player实体中的排名
当我想到这个解决方案时,感觉非常“暴力”

此解决方案的问题在于所有实体的读写成本。 如果我们最终拥有50K个活动用户,这意味着需要定期进行50K+1次读取和50K+1次写入的排序查询,这可能非常昂贵(取决于时间间隔)

我知道memcache可以防止某些读取和写入,但如果某些实体不在memcache中,查询它有意义吗? 此外,我还了解到,memcache随时都可以刷新,因此,除非有一种廉价的“备份”方法,否则这似乎是一种危险的使用,因为数据相对重要


有没有更简单的方法来解决这个问题?

这是否更简单还有争议

我假设排名不仅仅是排序积分累积的问题,在这种情况下,这只是一个简单的查询。I排名涉及其他因素,而不仅仅是当前分数

我会考虑为每个用户更新点(有效的队列)写一个事件记录。任务收集所有当前事件记录运行,此外,您还维护一组代表排行榜顶部的记录。根据传入事件记录调整此记录集。一旦处理,就丢弃事件记录。这会将您的读写限制为仅在一个小时间窗口内的活动事件。leader board可能是单个实体,通过键获取并缓存

我假设您可能有不同的排名方案,比如当前活动排名(当前7天)和所有时间排名。(即暂时不上场的球员目前排名不佳)


当玩家查看他们的排名时,您可以通过两个简单的查询
players.query(players.score>somescore).获取(5)
players.query(players.score
这应该不会花费太多,您可以缓存它们

您不需要50000次读取或50000次写入。解决方案是在points属性上设置排序顺序。每次更新时,数据存储将自动更新其顺序,这意味着您不需要除了points属性之外的rank属性。因此,您不需要cron作业

然后,当需要检索引线板时,运行两个查询:一个查询针对用户点数大于或等于的6个实体;第二个-用于点数较少或相等的6个实体。合并结果,这就是您要向用户显示的内容

对于前10个查询,您可能希望将其结果放入Memcache中,过期时间为(比如)5分钟。需要时,首先检查Memcache。如果未找到,请运行查询并更新Memcache

编辑:

澄清查询部分。您需要设置排序顺序和不等式过滤器的正确组合,以获得所需的结果。根据App Engine文档,查询按以下顺序执行:

  • 标识与查询的种类、筛选器相对应的索引 属性、筛选器运算符和排序顺序
  • 从 符合所有条件的第一个实体的索引开头 查询的筛选条件
  • 继续扫描索引,返回 每个实体依次,直到它遇到一个不符合 筛选条件,或到达索引末尾,或 已收集查询请求的最大结果数
  • 因此,对于一个查询,您需要将升序与大于等于或等于过滤器相结合,对于另一个查询,需要将降序与小于等于或等于过滤器相结合。在这两种情况下,您都将要检索的结果限制设置为6


    还有一个注意事项:您将限制设置为6个实体,因为两个查询都将返回用户本身。您可以添加另一个过滤器(userId不等于您的用户id),但我不建议您这样做-节省的成本不值得。显然,您不能对点使用大于/小于过滤器,因为许多用户可能拥有相同数量的点。

    下面是一位谷歌开发人员,他解释了类似的问题以及使用谷歌代码JAM的解决方案。关于这个库的进一步帮助和扩展,可以在Google小组中讨论

    库基本上创建一个N元树,每个节点包含特定范围内的分数计数。分数范围进一步向下划分,直至叶节点,在叶节点处为单个分数。树遍历(O log(n))可用于查找得分高于特定得分的玩家数量。这是运动员的级别。它还建议将分数提交请求聚合到pull taskqueue中,然后在一个队列中成批处理它们