【问题标题】:Game scoreboard on AzureAzure 上的游戏记分牌
【发布时间】:2017-03-27 03:49:02
【问题描述】:

您能否向我推荐一种在 Azure 云上存储游戏分数的替代解决方案?我正在寻找一种托管运行成本低易于上下扩展的存储选项。

输入数据:{ "player": 1, "score": 10 }(分数可能已经计算过了,不需要总结。)

应该可以选择进行 2 种类型的查询:
1. 获取玩家在计分板上的位置。
2. 获取位置 X 和 Y 之间的位置(球员和得分)。

应用程序无法在写入期间告知玩家在记分牌中的位置 - 后端数据库解决方案应提供该信息,或者数据布局应允许快速计算所有玩家之间的位置。 非实时精度是可以接受的。

当前的后端技术选择:Azure Web App (.NET Core)、存储(表、Blob、队列)。
使用 SQL Server 很明显如何做到这一点,但我想避免将 SQL Server 带入技术堆栈。由于有多个 Web 服务器,因此也不能选择保留在内存中。

您知道如何使用任何其他 Azure 服务存储和检索此类数据吗? DocumentDB、Data Lake 还是其他?

【问题讨论】:

  • Azure 表存储似乎适合你
  • 使用单个分区键?
  • (1) 你预计有多少玩家?数百?数千?百万? (2) 您想跟踪所有玩家的位置还是只跟踪前 10 名? (3) XY 是固定段(如 1-10、11-20、..)还是它们的开始/范围?像 (1-next10, next10, ...)。请注意,使用固定分段时,某些分段可能会显示为空。
  • 实际上数以万计,但让我们假装数以百万计,因为我想了解在这种情况下解决方案会有多么不同。需要 1. 跟踪 top100 玩家 2. 提供玩家 (player1) 与他的直接竞争对手的位置:#3010 player5 score; #3009 player1得分; #3008 player7得分。为了简单起见,我们可以忽略 #10000 以下的所有内容并显示“您的位置是 #10000+”

标签: database azure storage azure-storage azure-data-lake


【解决方案1】:

计算在列表中的位置的问题在于,当您的数据可以分布在多个服务器上时,很难进行排名(以及一般的窗口操作)。随着数据的增长,这将成为一项昂贵的操作,并且无法很好地扩展。

Data Lake 确实支持窗口函数,包括 RANK() and DENSE_RANK()。您需要使用一些具有代表性的数据对此进行很好的测试,以了解性能如何。


另一种选择是在一定的时间间隔预先计算排名,并将完整的记分板“缓存”到表存储中;如果对用户有延迟是可以接受的(例如,您可以说排名每小时刷新一次)

那么你可以有一个预定的 WebJob:

  1. 读取分数高于阈值的所有分数记录,按分数排序
  2. 为玩家排名创建一个新表,例如player-scores-170401-124200
  3. 为整体排名创建一个新表,例如scoreboard-170401-124200
  4. 将排名记分板分批写入新表中

这样,您可以在PartitionKey=player 上查询player-scores 以获得个人排名,并使用不等式过滤器查询scoreboard 以获得位置窗口。

查询(这是您可能应该优化的操作)变得非常便宜和容易,因为它是每条信息的单一事务。

运行计算也不会太昂贵,而且交易成本是您需要密切关注的事情。假设 n 条得分记录:

  • 您可以分批读取 100 个。Transactions ~= n / 100
  • 您可以批量写入同一个分区,每批 100 个。事务 ~= 2n / 100
  • 您可以选择保留历史排名,或在单个事务中删除整个表

所以你的 100 万条记录的总交易量是:

1000000*3/100 = 30,000 per run

每天每小时执行一次(72 万笔交易),每天大约需要花费 2-3 美分(当然不包括存储费用)。在我的书中,这算便宜:-)

【讨论】:

    【解决方案2】:

    如果您使用表格,那么您的第一个查询很容易,您可以使用玩家 ID(玩家的唯一标识符,无论您选择什么)作为分区键,并且分数可以是同一实体的属性。当您想读回(您的查询 1)时,您只需使用唯一的玩家 ID 进行点查询并获取分数。

    您的第二个查询有点棘手。您可能希望避免下载整个表并在客户端进行内存排序,这是客户端和表服务世界中最差的。

    因此需要在此处选择不同的分区键来优化第二个查询。您可以做的一件事是创建一个范围桶并获得该范围的唯一哈希。 IE。如果你正在编写一个位置为 19 的玩家并且你选择你的桶大小为 50,那将转到你的第一个位置在 1 - 50 之间的玩家桶。在将位置为 19 的玩家写入桌子之前,你的外观(或适配器)您可以调用它)客户端中的代码层将检测到这将进入第一个存储桶,为该存储桶生成一个散列,并将生成的存储桶 1-50 的散列作为分区键和实际的播放器唯一 ID 将播放器写入表作为其行键和其余属性。

    您为存储桶生成的散列可能只是存储桶的下限和上限的散列等。请确保使用加密散列而不是 Object.GethashCode,因为这可能会为完全相同的对象返回不同的散列值,并且应该不能用于他的目的。

    当您想要获取范围 X 和 Y 之间的所有玩家时,您需要查询那些您知道覆盖该范围而不是整个表的分区。并且您将使用确切的分区键(代表存储桶的哈希值)查询分区,这样会很有效。

    当您插入一个播放器时,您需要为您的第一个查询创建 2 个实体 1 为您的第二个查询创建第二个实体并插入两者。此过程也称为非规范化数据以针对不同的查询需求进行优化。希望这有助于了解如何使用 Azure 表进行优化。

    【讨论】:

    • 对于这两个查询我都不知道位置。
    • 您不需要知道播放器的位置进行查询,您只需要在实际将播放器写入表存储时知道位置。对于您的第一个查询,您使用作为播放器唯一 ID 的分区键查询表。对于您的第二个查询,您使用与该范围 X-Y 对应的哈希值作为分区键来查询表。如果您的范围跨越多个存储桶,您可能需要执行其中一些查询,但您仍然不需要查询的位置
    • 感谢您的回答,抱歉不清楚。我不知道你写的时候的位置。该应用程序只能提供玩家 ID 和他的分数。我正在寻找可以告诉我位置的技术/数据布局。
    【解决方案3】:

    Azure 表存储有利于存储纯数据,而且价格便宜且易于扩展。但这对复杂的查询没有好处。如果满足您的需求,您可以通过选择 Table Storage 来节省一些钱。价格范围为每 GB/月 4.5 至 12 美分。

    Azure DocumentDB 是一个分布式文档数据库。它比 Azure 表存储更强大,但也更昂贵。价格:每月每 GB 25 美分。

    Azure Data Lake Store 是针对大数据分析工作负载的优化存储,我们经常使用它来批处理、交互式、流式分析和机器学习数据,例如日志文件、物联网数据、点击流、大型数据集

    如果可以满足您的需求,最好选择 Azure 表存储。根据您的描述,我建议您使用 Azure 表存储并通过使用不同的分区键来优化查询。关于如何设计一个可扩展和高性能的表,下面的链接供您参考。

    Azure Storage Table Design Guide: Designing Scalable and Performant Tables

    获取玩家在计分板上的位置。

    如果我们想获得位置(排名),存储数据的最佳方式是使用分数作为分区键。如果我们只知道当前玩家的id,​​我们需要在查询位置之前获取当前玩家的分数。要根据玩家 id 查询玩家,最好通过将玩家 id 设置为分区键,在 Azure 表存储中存储另一个游戏数据副本。

    获取位置 X 和 Y 之间的位置(球员和得分)。

    使用分数作为分区键适合这种场景。

    总之,我建议您存储两个不同的游戏数据副本。一种是使用 score 作为分区键,另一种是使用玩家 id 作为分区键。

    对于这两个查询我都不知道位置

    查询的结果数据是按分区键排序的。所以结果计数就是你的玩家的位置(排名)。

    例如,将 score 设置为 partition key 后,可以使用小于 score(ex. 10) 的 partition key 来查询一系列实体,实体计数为得分为 10 的玩家的位置。

    【讨论】:

    • 感谢您的回答。我错过了最重要的部分 - 如何计算职位?应用程序只知道事实 - 特定玩家的得分。
    • 关于 Data Lake,接受来自应用程序的事件并定期计算所有玩家的位置并将结果保存在 Azure Storage blob/table 中是否是一项好技术?
    • 如果您将位置存储在 Azure 存储中,每次更改分数都需要重新计算位置并更新所有数据。如果分数会频繁变化,那么会在分数变化上花费大量时间。
    • 关于你的回答,听起来它不会起作用 - 如果我使用分数作为分区键,为了获得玩家的位置,我将不得不读取所有得分较高的行作为分区钥匙。如果您建议使用 PlayerID 作为分区键和位置作为字段的附加表 - 位置是未知的,直到所有行都被扫描,如前所述。
    • 如何添加一个额外的表格来记录分数以及有多少玩家达到这个分数?我们可以将分数设置为分区键。当我们修改一个玩家的分数时,例如将玩家1的分数从10改为30,我们需要减少10分的计数并增加30分的计数。我们可以通过计算所有玩家的数量来轻松获得位置得分大于玩家得分的人。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-08
    相关资源
    最近更新 更多