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

DynamoDB 中的社交网络架构设计

社交网络业务使用场景

此使用场景探讨如何将 DynamoDB 用于社交网络。社交网络是一种在线服务,让不同的用户可以彼此互动。我们设计的社交网络将提供一个时间线供用户查看,其中包括他们的帖子、他们的粉丝、他们关注的人以及他们关注的人的发帖。此架构设计的访问模式为:

  • 获取给定 userID 的用户信息

  • 获取给定 userID 的粉丝名单

  • 获取给定 userID 的关注名单

  • 获取给定 userID 的帖子列表

  • 获取给定 postID 中喜欢该帖子的用户名单

  • 获取给定 postID 的点赞次数

  • 获取给定 userID 的时间线

社交网络实体关系图

这是我们在社交网络架构设计中使用的实体关系图 (ERD, Entity Relationship Diagram)。

社交网络访问模式

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

  • getUserInfoByUserID

  • getFollowerListByUserID

  • getFollowingListByUserID

  • getPostListByUserID

  • getUserLikesByPostID

  • getLikeCountByPostID

  • getTimelineByUserID

社交网络架构设计演变

DynamoDB 是一个 NoSQL 数据库,因此不允许您执行联接操作,也就是合并来自多个数据库的数据的操作。不熟悉 DynamoDB 的客户可能会不必要地将关系数据库管理系统 (RDBMS, Relational DataBase Management System) 的设计理念(例如为每个实体创建表)应用于 DynamoDB。DynamoDB 单表设计的目的是根据应用程序的访问模式,以预先联接的形式写入数据,然后无需额外计算即可立即使用数据。有关更多信息,请参阅 DynamoDB 中的单表与多表设计。

现在,我们来逐步了解一下如何改进架构设计以解决所有访问模式。

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

要获取给定用户的信息,我们需要使用键条件 PK=<userID> 对基表执行 Query 操作。查询操作允许您对结果进行分页,这在用户有很多关注者时很有用。有关 Query 的更多信息,请参阅DynamoDB 中的查询操作

在示例中,我们跟踪用户的两种类型的数据:他们的“count”和“info”。用户的“count”反映了他们有多少粉丝,他们关注了多少用户,以及他们创建了多少帖子。用户的“info”反映了他们的个人信息,例如他们的姓名。

我们看到这两种数据类型由以下两项表示。排序键 (SK) 中带有“count”的项目比带有“info”的项目更有可能发生改变。DynamoDB 会考虑更新前后的项目大小,所消耗的预置吞吐量将反映这些项目大小中较大的一个。因此,即使您只更新项目属性的子集,UpdateItem 仍会消耗全部的预置吞吐量(之前和之后项目大小中的较大者)。您可以通过单个 Query 操作获取项目,并使用 UpdateItem 对现有的数值属性进行加减。

步骤 2:解决访问模式 2 (getFollowerListByUserID)

要获取关注给定用户的用户名单,我们需要使用键条件 PK=<userID>#follower 对基表执行 Query 操作。

步骤 3:解决访问模式 3 (getFollowingListByUserID)

要获取给定用户所关注的用户名单,我们需要使用键条件 PK=<userID>#following 对基表执行 Query 操作。接下来,您可以使用 TransactWriteItems 操作将多个请求分组在一起,并执行以下操作:

  • 将用户 A 添加到用户 B 的粉丝名单,然后将用户 B 的粉丝人数增加 1

  • 将用户 B 添加到用户 A 的粉丝名单,然后将用户 A 的粉丝人数增加 1

步骤 4:解决访问模式 4 (getPostListByUserID)

要获取给定用户发布的帖子列表,我们需要使用键条件 PK=<userID>#post 对基表执行 Query 操作。这里有非常重要的一点需要注意,用户的 postID 必须是递增的:第二个 postID 值必须大于第一个 postID 值(因为用户希望以排序的方式查看他们的帖子)。为此,您可以根据时间值生成 postID 来实现此目的,例如通用唯一词典排序标识符 (ULID, Lexicographically Sortable Identifier)。

步骤 5:解决访问模式 5 (getUserLikesByPostID)

要获取对给定用户的帖子点赞的用户名单,我们需要使用键条件 PK=<postID>#likelist 对基表执行 Query 操作。这种方法与我们在访问模式 2 (getFollowerListByUserID) 和访问模式 3 (getFollowingListByUserID) 中用来检索粉丝和关注名单时使用的模式相同。

步骤 6:解决访问模式 6 (getLikeCountByPostID)

要获取给定帖子的点赞数,我们需要使用键条件 PK=<postID>#likecount 对基表执行 GetItem 操作。当拥有许多粉丝的某个用户(例如名人)创建帖子时,这种访问模式可能会导致节流问题,因为当单个分区的吞吐量超过每秒 1000 个 WCU 时,就会出现节流。这个问题不是 DynamoDB 造成的,只是正好出现在 DynamoDB 中,因为它位于软件堆栈的最后部分。

您应该评估一下是否有必要让所有用户同时查看点赞数,还是可以在一段时间内逐步显示。通常,帖子的点赞数不必立即获得 100% 准确的值。您可以通过在应用程序和 DynamoDB 之间放置队列,用于定期进行更新,以此来实施此策略。

步骤 7:解决访问模式 7 (getTimelineByUserID)

要获取给定用户的时间线,我们需要使用键条件 PK=<userID>#timeline 对基表执行 Query 操作。我们来考虑一个场景,即用户的粉丝需要同步查看用户的帖子。每次用户发了一个帖子时,都会读取用户的粉丝名单,并将用户的 userID 和 postID 逐步输入其所有粉丝的时间线中。然后,当应用程序启动时,您可以通过 Query 操作读取时间线键,并对任何新项目使用 BatchGetItem 操作,将 userID 和 postID 的组合结果来填充时间线屏幕。您无法通过 API 调用来读取时间线,但是如果帖子经常会进行编辑,则这是一种更具成本效益的解决方案。

时间线是显示最近帖子的位置,所以我们需要一种方法来清理旧帖子。您可以使用 DynamoDB 的 TTL 功能来免费删除旧帖子,而无需使用 WCU。

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

访问模式 基表/GSI/LSI 操作 分区键值 排序键值 其他条件/筛选条件
getUserInfoByUserID 基表 Query PK=<userID>
getFollowerListByUserID 基表 Query PK=<userID>#follower
getFollowingListByUserID 基表 Query PK=<userID>#following
getPostListByUserID 基表 Query PK=<userID>#post
getUserLikesByPostID 基表 Query PK=<postID>#likelist
getLikeCountByPostID 基表 GetItem PK=<postID>#likecount
getTimelineByUserID 基表 Query PK=<userID>#timeline

社交网络最终架构

这是最终的架构设计。要以 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 的数据可视化工具功能。