编程语言
首页 > 编程语言> > javascript – 使用Firebase排行榜排名

javascript – 使用Firebase排行榜排名

作者:互联网

我有一个项目,我需要显示前20名的排行榜,如果用户不在排行榜,他们将出现在他们当前排名的第21位.

有效的方法吗?

我使用Cloud Firestore作为数据库.我认为选择它而不是MongoDB是错误的,但我在项目的中间,所以我必须使用Cloud Firestore.

该应用程序将由30K用户使用.没有获得所有30k用户,有没有办法做到这一点?

 this.authProvider.afs.collection('profiles', ref => ref.where('status', '==', 1)
        .where('point', '>', 0)
        .orderBy('point', 'desc').limit(20))

这是我为获得前20名所做的代码,但如果他们不在前20名,那么获得当前登录用户排名的最佳做法是什么?

解决方法:

在排行榜中找到任意玩家的排名,以扩展的方式是数据库的常见难题.

有几个因素会推动您需要选择的解决方案,例如:

>总人数
>单个玩家添加分数的比率
>评分新成绩(并发球员*以上)
>分数范围:有界或无界
>分数分布(统一,或者是他们的’热门分数’)

简单的方法

典型的简单方法是对所有具有较高分数的玩家进行统计,例如SELECT count(id)FROM players WHERE得分> {} playerScore.

这种方法工作规模较小,但随着玩家群体的增长,它很快变得既慢又耗资源(在MongoDB和Cloud Firestore中).

Cloud Firestore本身不支持计数,因为它是不可扩展的操作.您只需要计算返回的文档,就需要在客户端实现它.或者,您可以使用Cloud Functions for Firebase在服务器端进行聚合,以避免返回文档的额外带宽.

定期更新

而不是给他们一个实时排名,而是将其更改为每隔一小时更新一次.例如,如果您查看Stack Overflow的排名,它们只会每天更新.

对于这种方法,如果运行时间超过540秒,则可以是schedule a functionschedule App Engine.该函数将在梯形图集合中写出玩家列表,其中新的等级字段填充有玩家等级.当玩家现在观看阶梯时,您可以轻松获得玩家在O(X)时间内排名的前X名.

更好的是,您可以进一步优化并明确地将顶部X写为单个文档,因此要检索梯形图,您只需要阅读2个文档,top-X&玩家,节省金钱并加快速度.

这种方法适用于任何数量的玩家和任何写入率,因为它是在带外完成的.您可能需要根据您的支付意愿调整频率.每小时30K玩家每小时0.072美元(每天1.73美元),除非你做了优化(例如,忽略所有0分球员,因为你知道他们最后并列).

倒指数

在这种方法中,我们将创建一些倒置索引.如果有一个有限的得分范围显着小于想要玩家的数量(例如,0-999得分对比30K玩家),则该方法有效.它也适用于无限分数范围,其中独特分数的数量仍然明显小于玩家数量.

使用一个名为“分数”的单独集合,您可以获得一个文档,用于每个单独的分数(如果没有人有该分数则不存在),并带有一个名为player_count的字段.

当玩家获得新的总分时,您将在分数集合中进行1-2次写入.一次写入是1到player_count的新分数,如果不是他们的第一次-1他们的旧分数.这种方法适用于“您的最新分数是您当前的分数”和“您的最高分是您当前的分数”风格梯子.

找出一个玩家的确切等级就像SELECT sum(player_count)1 FROM FROM WHERE得分>一样容易. {} playerScore.

由于Cloud Firestore不支持sum(),因此您可以执行上述操作,但在客户端进行求和. 1是因为总和是你之上的玩家数量,所以加1会给你那个玩家的等级.

使用这种方法,您需要读取最多999个文档,平均500个以获得玩家排名,但实际上如果删除没有玩家的分数,这将会更少.

新分数的写入率很重要,因为你只能平均每2秒*更新一次单独的分数,对于0-999的完美分布分数,这意味着500分新的分数/秒**.您可以通过使用distributed counters为每个分数增加此值.

*每2秒只有1个新分数,因为每个分数产生2次写入
**假设平均游戏时间为2分钟,则每秒500个新分数可以支持60000个没有分布式计数器的并发玩家.如果您使用的是“最高得分是您目前的得分”,那么实际情况会更高.

Sharded N-ary Tree

这是迄今为止最难的方法,但可以让你为所有玩家提供更快和实时的排名位置.它可以被认为是上面的反向索引方法的读优化版本,而上面的反向索引方法是对此的写优化版本.

您可以按照适用的一般方法关注‘Fast and Reliable Ranking in Datastore’的相关文章.对于这种方法,你需要有一个有界的分数(这可能是无限的,但需要从下面的变化).

我不推荐这种方法,因为您需要为具有半频率更新的任何梯形图的顶级节点执行分布式计数器,这可能会抵消读取时间的好处.

Ternary tree example

最后的想法

根据您为玩家显示排行榜的频率,您可以结合使用方法来优化这一点.

在较短的时间段内将“倒置索引”与“定期更新”相结合,可以为所有玩家提供O(1)排名访问权限.

只要在所有玩家上查看排行榜>在“定期更新”期间的4倍,您将节省资金并拥有更快的排行榜.

基本上每个时期,比如说5-15分钟,您可以按降序从分数中读取所有文档.使用它,保持运行总数players_count.将每个分数重新写入名为scores_ranking的新集合中,并使用新的字段players_above.此新字段包含不包括当前分数player_count的运行总计.

要获得玩家的等级,您现在需要做的就是从score_ranking – >中读取玩家得分的文档.他们的排名是上半场球员.

标签:angularfire2,javascript,firebase,database,google-cloud-firestore
来源: https://codeday.me/bug/20190918/1810724.html