DynamoDB 中的游戏个人资料架构设计 - Amazon DynamoDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

DynamoDB 中的游戏个人资料架构设计

游戏个人资料业务使用场景

此使用场景探讨使用 DynamoDB 来存储游戏系统的玩家个人资料。许多现代游戏,尤其是在线游戏,需要用户(在本例中为玩家)先创建个人资料,然后才能玩游戏。游戏个人资料通常包括以下内容:

  • 基本信息,例如玩家的用户名

  • 游戏数据,例如物品和装备

  • 游戏记录,例如任务和活动

  • 社交信息,例如好友名单

为了满足此应用程序的细粒度数据查询访问要求,主键(分区键和排序键)将使用通用名称(PK 和 SK),因此它们可能会使用各种类型的值重载,如下所示。

此架构设计的访问模式为:

  • 获取用户的好友名单

  • 获取玩家的所有信息

  • 获取用户的物品清单

  • 从用户的物品清单中获取特定物品

  • 更新用户的角色

  • 更新用户的物品数量

在不同的游戏中,游戏个人资料的大小会有所不同。压缩大型属性值可以让属性值符合 DynamoDB 的项目限制,从而降低成本。吞吐量管理策略取决于多种因素,例如:玩家数量、每秒玩的游戏数量以及工作负载的季节性。通常,对于新推出的游戏,玩家数量和受欢迎程度是未知的,因此我们一开始使用按需吞吐量模式

游戏个人资料实体关系图

这是我们在游戏个人资料架构设计中使用的实体关系图(ERD)。

游戏个人资料访问模式

我们将为社交网络架构设计考虑这些访问模式。

  • getPlayerFriends

  • getPlayerAllProfile

  • getPlayerAllItems

  • getPlayerSpecificItem

  • updateCharacterAttributes

  • updateItemCount

游戏个人资料架构设计演变

从上面的 ERD 可以看出,这是一对多关系类型的数据建模。在 DynamoDB 中,一对多数据模型可以整理为项目集合,这不同于通过创建多个表并使用外键来链接的传统关系数据库。项目集合是一组共享相同分区键值但具有不同排序键值的项目。在项目集合中,每个项目都有一个唯一的排序键值,该值将其与其他项目区分开来。考虑到这一点,我们可以为每种实体类型的 HASHRANGE 值使用以下模式。

首先,我们使用 PKSK 等通用名称,将不同类型的实体存储在同一个表中,以便模型可供未来使用。为了提高可读性,我们可以添加前缀来表示数据的类型,也可以提供名为 Entity_typeType 的任意属性。在当前示例中,我们使用以 player 开头的字符串,将 player_ID 存储为 PK;将 entity name# 用作 SK 的前缀,然后添加一个 Type 属性来指示这段数据所属的实体类型。这样,我们以后便能够支持存储更多的实体类型,并使用 GSI 重载和稀疏 GSI 等高级技术来满足更多的访问模式。

让我们开始实施访问模式。添加玩家和添加装备等访问模式可以通过 PutItem 操作实现,因此我们可以忽略这些访问模式。在本文中,我们将重点介绍上面列出的典型访问模式。

步骤 1:解决访问模式 1 (getPlayerFriends)

我们通过此步骤解决访问模式 1 (getPlayerFriends)。在我们目前的设计中,好友关系很简单,游戏中的好友数量很少。为简单起见,我们使用列表数据类型来存储好友列表(1:1 建模)。在此设计中,我们使用 GetItem 来满足这种访问模式。在 GetItem 操作中,我们明确提供分区键和排序键值以获取特定项目。

但是,如果某个游戏中有大量好友,并且他们之间的关系很复杂(例如好友关系是双向的,包括邀请和接受组件),则必须使用多对多关系来单独存储每个好友,这样才能扩展到大小不受限的好友名单。而且,如果好友关系变更涉及同时对多个项目进行操作,则可以使用 DynamoDB 事务将多个操作分组在一起,并将它们作为“全有或全无”的单个 TransactWriteItemsTransactGetItems 操作提交。

步骤 2:解决访问模式 2 (getPlayerAllProfile)、3 (getPlayerAllItems) 和 4 (getPlayerSpecificItem)

我们使用此步骤解决访问模式 2 (getPlayerAllProfile)、3 (getPlayerAllItems) 和 4 (getPlayerSpecificItem)。这三种访问模式的共同点是使用 Query 操作进行范围查询。根据查询的范围,使用键条件筛选表达式,这些方法在实际开发中经常使用。

在查询操作中,我们为分区键提供一个值,然后获取具有该分区键值的所有项目。访问模式 2 (getPlayerAllProfile) 以这种方式实施。或者,我们可以添加排序键条件表达式,这是一个字符串,用于确定要从表中读取的项目。访问模式 3 (getPlayerAllItems) 通过添加键条件排序键 begins_with ITEMS# 来实施。此外,为了简化应用程序端的开发,我们可以使用筛选条件表达式来实施访问模式 4 (getPlayerSpecificItem)。

以下是使用筛选条件表达式筛选 Weapon 类别项目的伪代码示例:

filterExpression: "ItemType = :itemType" expressionAttributeValues: {":itemType": "Weapon"}
注意

筛选表达式在查询已完成但结果尚未返回到客户端时应用。因此,无论是否存在筛选表达式,查询都将占用同等数量的读取容量。

如果访问模式是查询大型数据集,需要筛选掉大量数据,仅保留一小部分数据,则合适的方法是设计更有效的 DynamoDB 分区键和排序键。例如,在上面的获取特定 ItemType 的示例中,如果每个玩家都有很多物品并且典型的访问模式是查询特定的 ItemType,那么将 ItemType 引入 SK 中作为复合键会更有效率。数据模型类似于以下所示:ITEMS#ItemType#ItemId

步骤 3:解决访问模式 5 (updateCharacterAttributes) 和 6 (updateItemCount)

我们使用此步骤解决访问模式 5 (updateCharacterAttributes) 和 6 (updateItemCount)。当玩家需要修改角色时,例如减少货币或修改其物品中特定武器的数量,可以使用 UpdateItem 来实施这些访问模式。在更新玩家货币时,为了确保金额永远不会低于最低数量,我们可以添加一个 条件表达式,确保只有其大于或等于最低金额时才减少余额。以下是伪代码示例:

UpdateExpression: "SET currency = currency - :amount" ConditionExpression: "currency >= :minAmount"

在使用 DynamoDB 进行开发并使用原子计数器减少库存时,我们可以通过使用乐观锁来确保幂等性。以下是原子计数器的伪代码示例:

UpdateExpression: "SET ItemCount = ItemCount - :incr" expression-attribute-values: '{":incr":{"N":"1"}}'

此外,在玩家用货币购买物品的情况下,整个流程中需要扣除货币并同时添加物品。我们可以使用 DynamoDB 事务将多个操作分组在一起,并将它们作为“全有或全无”的单个 TransactWriteItemsTransactGetItems 操作提交。TransactWriteItems 是同步的幂等性写入操作,可将最多 100 个写入操作分组到一个“全有或全无”操作中。这些操作以原子方式完成,以便所有操作都成功或都失败。事务有助于消除重复或货币消失的风险。有关事务的更多信息,请参阅DynamoDB 事务示例

下表总结了所有访问模式以及架构设计如何解决访问模式:

访问模式 基表/GSI/LSI 操作 分区键值 排序键值 其他条件/筛选条件
getPlayerFriends 基表 GetItem PK=PlayerID SK=“FRIENDS#playerID”
getPlayerAllProfile 基表 Query PK=PlayerID
getPlayerAllItems 基表 Query PK=PlayerID SK begins_with “ITEMS#”
getPlayerSpecificItem 基表 Query PK=PlayerID SK begins_with “ITEMS#” filterExpression: "ItemType = :itemType" expressionAttributeValues: { ":itemType": "Weapon" }
updateCharacterAttributes 基表 UpdateItem PK=PlayerID SK=“#METADATA#playerID” UpdateExpression: "SET currency = currency - :amount" ConditionExpression: "currency >= :minAmount"
updateItemCount 基表 UpdateItem PK=PlayerID SK =“ITEMS#ItemID” update-expression: "SET ItemCount = ItemCount - :incr" expression-attribute-values: '{":incr":{"N":"1"}}'

游戏个人资料最终架构

这是最终的架构设计。要以 JSON 文件格式下载此架构设计,请参阅 GitHub 上的 DynamoDB 示例

基表:

在此架构设计中使用 NoSQL Workbench

若要进一步探索和编辑新项目,您可以将此最终架构导入到 NoSQL Workbench,这是一款为 DynamoDB 提供数据建模、数据可视化和查询开发功能的可视化工具。请按照以下步骤开始使用:

  1. 下载 NoSQL Workbench。有关更多信息,请参阅 下载 NoSQL Workbench for DynamoDB

  2. 下载上面列出的 JSON 架构文件,该文件已经采用 NoSQL Workbench 模型格式。

  3. 将 JSON 架构文件导入到 NoSQL Workbench。有关更多信息,请参阅 导入现有数据模型

  4. 导入到 NOSQL Workbench 后,您便可编辑数据模型。有关更多信息,请参阅 编辑现有数据模型

  5. 要将数据模型可视化、添加样本数据或从 CSV 文件导入样本数据,请使用 NoSQL Workbench 的数据可视化工具功能。