社交网络的DynamoDB表设计

时间:2019-06-12 20:52:01

标签: amazon-web-services amazon-dynamodb dynamodb-queries amazon-neptune

我在DynamoDB中遇到了一个思考问题。 我的结构如下:

  • 主键=“ id”

  • 排序键=“排序” 我有帖子,用户和“跟随用户B的用户A”的关系。

用户:

  • id = 1234
  • sort =“ USER_USER_1234”
  • name =“ max”(例如)

-

  • id = 3245
  • sort =“ USER_USER_3245”
  • name =“ tom”

帖子:

  • id = 9874

  • sort =“ POST_POST_1234(因为它是由用户ID 1234创建的)

  • createdAt = 1560371687

以下内容:

  • id = 1234

  • sort =“ USER_FOLLOW_3245”

-> tom遵循最大(但最大不是tom)

我如何设计查询以获取tom(id = 3245)所关注人员的所有帖子?那么以我为例,帖子ID 9874? 我的方法是放置一个GSI,其中sort是主键,id是排序键(我可以查询所有用户A跟随的人),而不是从用户那里获得所有帖子(在同一个GSI的帮助下),在第二个索引之后对结果进行排序,其中createdAt是排序键。问题在于,这需要大量查询(想象用户A将关注10000个人,并且他们全都发布帖子)。您是否可以针对这种情况推荐一种技术或设计思维方法?我的第二种方法是索引整个应用程序表以进行弹性搜索并进行嵌套查询。这更有意义吗?还是建议使用其他类型的数据库,例如AWS neptune?

2 个答案:

答案 0 :(得分:1)

在Amazon Neptune中,这很简单:

g.V(3245).E('post')

上面的查询将从ID为“ 3245”的顶点开始,向由Edge标签“ post”连接的所有顶点返回一个迭代器。您可以通过从这些顶点投影特定属性(.property('name'))或实体化整个顶点(.valueMap())来进一步加强它。这只是Gremlin语法,您也可以轻松地使用SPARQL进行相同的操作,Amazon Neptune支持这两种语法。

对您来说,更大的问题是评估您希望对数据执行的所有查询类型,并查看在图形数据库中对其进行建模是否有意义。如果可以,那么最好使用Neptune,而不是使用其他产品的混合定制。查询/遍历高度连接的数据,浏览关系等是使用图形数据模型的一些经典用例。

答案 1 :(得分:0)

有一个关于类似问题的AWS实践实验室-“ 包含社交网络的移动应用程序”:https://aws.amazon.com/getting-started/hands-on/design-a-database-for-a-mobile-app-with-dynamodb/4/

简要说明:

  1. 用户将通过您的应用程序上传照片
  2. 用户将要查找并关注朋友
  3. 跟随朋友,用户将收到该朋友的新照片的通知
  4. 用户将能够向朋友发送消息
  5. 朋友可以查看他们的照片
  6. 用户可以使用以下四个表情符号之一对照片做出反应:心脏,笑脸,竖起大拇指或戴墨镜。
  7. 在查看照片时,用户应该能够看到照片收到的每种反应的数量

该模型具有以下实体:UserPhotoReactionFriendship

User可以有许多Photos,而Photo可以有许多Reactions。最后,Friendship实体代表用户之间的多对多关系,因为一个用户可以跟随多个用户,然后跟随多个其他用户。

访问模式

根据业务需求,以下是确定的访问模式:

用户

  1. 创建用户个人资料(写)
  2. 更新用户个人资料(写)
  3. 获取用户个人资料(阅读)

照片

  1. 为用户上传照片(写)
  2. 查看用户最近的照片(已读)
  3. 对照片进行反应(写)
  4. 查看照片和反应(阅读)

友谊

用户可以关注朋友,查看其朋友的活动更新,并接收有关他们可能想关注的其他朋友的推荐。

友谊是一种单向关系,例如Twitter。一个用户可以选择关注另一位用户,而该用户可以选择关注该用户。对于我们的应用程序,我们将称其为关注者的用户称为“关注者”,并将称其为关注者的用户称为“关注者”。

基于此信息,我们具有以下访问模式:

  1. 关注用户(写)
  2. 查看用户的关注者(已读)
  3. 用户关注的视图(已读)

在“友谊”实体上,我们有一种访问模式,该模式需要找到跟随特定用户的所有用户,还需要一种访问模式,以找到给定用户遵循的所有用户。

表格设计

因此,我们将使用同时具有PK和SK值的复合主键。复合主键将为我们提供PK上的查询功能,以满足我们需要的一种查询模式:

Entity               PK                  SK

User          USER#<USERNAME>          #METADATA#<USERNAME>

Photo         USER#<USERNAME>.         PHOTO#<USERNAME>#<TIMESTAMP>

Reaction  REACTION#<USERNAME>#<TYPE>   PHOTO#<USERNAME>#<TIMESTAMP>

Friendship    USER#<USERNAME>          #FRIEND#<FRIEND_USERNAME>

Friendship实体使用与User实体相同的PK。这样一来,您就可以在单个查询中获取用户的元数据以及该用户的所有关注者:

    KeyConditionExpression="PK = :pk AND SK BETWEEN :metadata AND :photos",
    ExpressionAttributeValues={
        ":pk": { "S": "USER#{}".format(username) },
        ":metadata": { "S": "#METADATA#{}".format(username) },
        ":photos": { "S": "PHOTO$" },
    },

二级(倒排)索引对于查询多对多关系的“另一侧”很有用。您的“友谊”实体就是这种情况。通过主键结构,您可以通过查询表的主键来查询特定用户的所有关注者。添加反向索引后,您可以通过查询反向索引来找到用户关注的用户(以下称“关注”):

    KeyConditionExpression="SK = :sk",
    ExpressionAttributeValues={
        ":sk": { "S": "#FRIEND#{}".format(username) }
    },

扩展名

有趣的是,对设计进行调整以支持超级用户(拥有数百万的关注者)。

此处未提及的另一个有趣的访问模式用户供稿-查看其朋友最近发布的所有照片。这可以用另一个表来完成,以包含此数据流,每当一个朋友发布内容时(查找其关注者,更新其供稿...),该数据流都会更新。