【发布时间】:2015-02-26 16:25:30
【问题描述】:
我们正在打造一款大型多人教育游戏,排行榜中有数百万个条目(基于获得的 XP 总和)。游戏结束后,我们需要显示排行榜以及该玩家/学生的排名情况。
但是这个排行榜有几个过滤器(全球/按国家、按月/年/今天、按年龄等)可以混合在一起,例如'给我排行榜for my Countryfor the last month'。组合数约为 20。
我的问题是如何存储这样一个定期更新的结构;每场比赛后必须重新计算排名。目前,一个典型的完整排行榜有大约 500 万个来自超过 150 个国家/地区的玩家参赛。
我曾经有一个带有 3 个节点的 MySQL 集群表(userid、xps、countryid),但按 XP 排序(在 DBMS 或需要来自 DB 的所有数据的应用程序中)被证明太慢了更大(> 20K 的用户)。这是一个有趣的post,但对于每个查询来说,半秒的时间太长了。
然后我们使用了 REDIS(参见post),但过滤是这里的问题。我们对 TOP 5 和其他人使用了单独的列表。 TOP 5 立即更新,其余部分延迟 20-30 分钟。事实上,我们根据排行榜的缓存实例对这个用户进行排名(虽然使用的是真实的 XP,而不是缓存的),所以这是可以接受的。非 Top5 上的实时不是先决条件。 这对于一个全球排名来说很好,但是如何根据月份和/或国家和/或年龄过滤结果。我们是否需要为每个过滤组合保留一个列表?
我们还在 Java 中测试了自定义结构(将其用作 Java 缓存服务器,在功能上与 REDIS 类似),仍在试验中。哪个是实现我们目标的最佳结构组合?我们最终为每个过滤组合使用了一个列表,例如
Map<FilteringCombination, SortedList<User>>然后对特定键的列表进行二进制搜索。这样,一个完成的游戏需要几次插入,比如 X,但它需要 X*NumOfPlayers 空间,这是保留单个列表的 X 倍(不确定这是否适合内存,但我们总是可以在这里创建一个集群将组合拆分到不同的服务器)。这里有一个问题是如何在失败的情况下重建缓存,但这是我们可以处理的另一个问题。扩展上述方法,如果我们在每个列表中定义评分桶(例如,一个用于 0-100xp 的桶,另一个用于 101 - 1000xp,另一个用于 1001 - 10000xp 等),我们可能会稍微提高性能。分桶策略将基于我们游戏中玩家的 xp 分布。确实,这种分布在现实世界中是动态的,但我们已经看到几个月后变化很小,记住 XP 总是在增加,但新用户也在不断涌现。
我们还在利用集群键和白行功能测试 Cassandra 的自然排序,尽管我们知道拥有数百万行可能并不容易处理。
总而言之,这就是我们需要实现的目标。如果某个用户(我们将其命名为 UserX)不在 Top5 列表中,我们需要显示该用户的排名以及周围的一些玩家(例如上面 2 和下面 2),如下例所示:
Global TOP 5 My Global Ranking (425) My Country Ranking Other Rankings
1. karen (12000xp) 423. george 1. david
2. greg (11280xp) 424. nancy 2. donald
3. philips (10293xp) **425. UserX** 3. susan
4. jason (9800xp) 426. rebecca **4. UserX**
5. barbara (8000xp) 427. james 5. teresa
我研究了许多 SO 或其他帖子,但仍然找不到有效更新和过滤大型排行榜表格的解决方案。您会选择哪一种候选解决方案以及可能的性能改进(空间 + 内存 +(插入/搜索 CPU 成本))?
【问题讨论】:
-
您能分享一下您使用的解决方案吗?
标签: java database caching redis cassandra