数据建模即是要设计应用程序如何在给定数据库中存储数据。对于 DynamoDB 等 NoSQL 数据库,数据建模不同于使用关系数据库进行建模。关系数据库旨在实现灵活性,非常适用于分析应用程序。在关系数据建模中,首先要从实体开始。当您具有标准化关系模型后,可以满足应用程序中需要的任何查询模式。

NoSQL(非关系型)数据库旨在提高,扩大规模,而不是为了实现灵活性。尽管关系数据库的性能可能会随着扩展而下降,但是横向扩展 DynamoDB 之类的数据库在任意规模下都能提供一致的性能。某些 DynamoDB 用户的表大于 100TB,这些表的读写性能与表小于 1GB 时并无差别。

要使用 DynamoDB 等 NoSQL 数据库获得最佳效果,需要转变使用典型关系数据库的思维方式。使用 DynamoDB 对数据进行建模时,请使用以下最佳实践。

1.重点关注访问模式
执行任何类型的数据建模时,您都将从一个实体关系图开始,该图描述了应用程序中的不同对象(或实体)及其之间的连接方式(或实体之间的关系)。

在关系数据库中,您要将实体直接放入表中,并使用外键指定关系。定义数据表后,关系数据库提供了一种灵活的查询语言,可以您需要的形状返回数据。

在 DynamoDB 中,您要在对表进行建模之前考虑访问模式。NoSQL 数据库侧重于速度,而不是灵活性。您首先要明白如何访问数据,然后按照数据将被访问的形状对其据进行建模。

在设计 DynamoDB 表之前,记录您在应用程序中读写数据的每项需求。透彻考虑应用程序中的所有流,因为您要针对访问模式优化表。

2.优化对 DynamoDB 的请求数量
记录应用程序的访问模式需求后,就可以开始设计表了。您应为每个访问模式设计一个表,以最大限度地减少对 DynamoDB 的请求数量。理想情况下,每个访问模式应该只需向 DynamoDB 发出一个请求,因为网络请求比较慢,这会限制在应用程序中发出的网络请求数量。

要优化对 DynamoDB 的请求数量,您需要了解一些核心概念:

主键
二级索引
事务

3.不要伪造关系模型
刚接触 DynamoDB 的用户经常会尝试在非关系数据库 DynamoDB 之上实现关系模型。如果您尝试这样做,会失去 DynamoDB 的大部分优势。

用户使用 DynamoDB 尝试的最常见反模式(对重复出现问题的无效响应)有:

  •  标准化:在关系数据库中,您会将数据标准化以减少数据冗余和存储空间,然后使用联接组合多个不同的表。然而,大规模联接不仅速度缓慢,而且成本高昂。DynamoDB 不支持联接,因为联接会随着表的增长而变慢。
  • 每个表一个数据类型:DynamoDB 表通常在一个表中包含不同类型的数据。在我们的示例中,单个表中有 User、Game 和 UserGameMapping 实体。在关系数据库中,这将建模为三个不同的表。
  • 过多的二级索引:用户经常尝试为自己需要的每个额外访问模式创建一个二级索引。DynamoDB 采用无架构设计,索引同样如此。利用属性中的灵活性在表的多个数据类型中重用单个二级索引。这称为索引重载

在下面的步骤中,我们将构建实体关系图并预先设计出访问模式。这些应始终是您使用 DynamoDB 的首要步骤。然后,在接下来的模块中,我们将在表设计中实现这些访问模式。

完成模块所需时间:20 分钟


  • 步骤 1:构建实体关系图

    任何数据建模练习的第一步都是通过构建一个图来显示应用程序中的实体以及它们之间的关系。

    我们的应用程序包含以下实体:
    • User
    • Game
    • UserGameMapping

    User 实体表示应用程序中的用户。一个用户可以创建多个 Game 实体,游戏的创建者将决定游戏地图和游戏开始时间。一个 User 可以创建多个 Game 实体,因此 User Game 之间存在一对多的关系。

    最后,一个 Game 包含多个 User,一个 User 可以在一段时间内玩多个不同的 Game。因此,User Game 之间存在多对多的关系。我们可以用 UserGameMapping 实体表示这种关系。

    考虑到这些实体和关系,我们的实体关系图如下所示。

    Module2-step1

    (单击可放大)

    Module2-step1
  • 第 2 步:考虑用户资料访问模式

    我们的游戏的用户需要创建用户资料。这些资料包括用户名、头像、游戏统计数据及其他关于每个用户的信息等数据。当用户登录时,游戏将显示这些用户资料。其他用户可以通过查看用户的资料,来查看他们的游戏统计数据及其他细节。

    当用户玩游戏时,游戏统计数据会更新,以反映用户玩过的游戏数量、获胜游戏数量和杀敌数量。

    基于这些信息,我们有三种访问模式:

    •  创建用户资料(写入)
    •  更新用户资料(写入)
    • 获取用户资料(读取)
  • 步骤 3:考虑游戏前访问模式

    我们能的游戏是一款大逃杀游戏。玩家可以在特定的地图上创建游戏,其他玩家可以加入游戏。当有 50 名玩家加入游戏时,游戏开始,其他玩家不能再加入。

    当搜索要加入的游戏时,某些玩家可能想要玩一个特定的地图。其他玩家并不关心地图,他们希望浏览所有地图的开放游戏。

    基于这些信息,我们有以下七种访问模式:

    • 创建游戏写入
    • 查找开放游戏(读取)
    • 通过地图查找开放游戏(读取)
    • 查看游戏(读取)
    • 查看游戏中的用户(读取)
    • 将用户加入游戏(写入)
    • 开始游戏(写入)
  • 步骤 4:游戏中和游戏后访问模式

    最后,我们看一下游戏中和游戏后的访问模式。

    在游戏过程中,玩家努力打败其他玩家,目标是成为最后一个活着的玩家。我们的应用程序会跟踪每个玩家在游戏中的杀敌数量,以及玩家在游戏中存活的时间。如果玩家是游戏中最后三名幸存者之一,他将获得游戏的金牌、银牌或铜牌。

    之后,玩家可能想要回顾他们玩过的游戏或其他玩家玩过的游戏。

    基于这些信息,我们有三种访问模式:

    • 为用户更新游戏(写入)
    • 更新游戏(写入)
    • 为用户查找所有玩过的游戏(读取)

    我们现在已经设计了游戏的所有访问模式。在下面的模块中,我们将使用 DynamoDB 实现这些访问模式。

    请注意,计划阶段可能需要执行几次迭代。首先大致了解应用程序需要的访问模式。映射表中的主键、二级索引和属性。回到开始,并确保满足所有访问模式。当您确信计划阶段已经完成时,继续执行。